Some packages we install from npm support both commonjs and es modules,
These packages can be imported as follows:
import express from 'express'
// or
const express = require('express')
I have created a package which I already published to npm using es modules.
and I since my another project which I'm working on is built with commonjs, I realized that I can not require it using the following syntax:
const stackPlayer = require('stack-player')
How can I support the two module systems in my package stack-player so that everyone around the world can use it?
Is there another method other than converting all of my project to es modules (which would be too complex since the project is 1 year old and is big enough to refuse the idea). ?
require() Usage
require() can, by default, only be used in CommonJS Modules. The built in method to import ECMAScript modules into CommonJS is using import(pathToFile).then(module => { }).
Support for require()
If you want to support require() for your package, you must provide a CommonJS module.
Here's a functioning example that demonstrates when and how to utilize require() or import(). There are some small differences how import() of a CommonJS module works compared to a ECMAScript Module. Especially that only the default property on the module object is available, when import() is used on a CommonJS file that exported something with module.exports.
index.js which imports different module types (from the demo above):(In case the stackblitz demo will be deleted:)
// executed as CommonJS module
console.time('');
import('./lib/example.cjs').then(({ default: example }) => {
console.timeLog('', 'import cjs', example() == 'Foo'); // true
});
import('./lib/index.mjs').then(({ example }) => {
console.timeLog('', 'import mjs', example() == 'Foo'); // true
});
try {
const example = require('./lib/example.cjs');
console.timeLog('', 'require cjs', example() == 'Foo'); // true
} catch (e) {
console.timeLog('', 'require cjs', '\n' + e.message);
}
try {
const example = require('./lib/index.mjs');
console.timeLog('', 'require mjs', example() == 'Foo');
} catch (e) {
console.timeLog('', 'require mjs', '\n' + e.message); // Error [ERR_REQUIRE_ESM]: require() of ES Module /path/to/lib/index.mjs not supported.
}
lib/example.cjs
module.exports = function example() {
return 'Foo';
};
lib/index.mjs
import example from './example.cjs';
export { example };
export default example;
Conditional Export for Packages
A conditional export can be supplied for packages to support require(), for example in a case where the CommonJS require() is no longer supported by your package. Refer to this link for more information.
The "exports" field allows defining the entry points of a package when imported by name loaded either via a node_modules lookup or a self-reference to its own name. It is supported in Node.js 12+ as an alternative to the "main" that can support defining subpath exports and conditional exports while encapsulating internal unexported modules.
package.json (example from the nodejs docs)
{
"exports": {
"import": "./index-import.js",
"require": "./index-require.cjs"
},
"type": "module"
}
If so, you have to provide two scripts: one for the CommonJS ("require": "filename") and one for the ECMAScript module ("import": "filename").
While index-require.js must provide the script via exports = ... or module.exports = ..., index-import.js must provide the script with export default.
Keyword Usage
You can only use specific keywords depending on the files module type.
CommonJS Modules
module.exports is used to define the values that a module exports and makes available for other modules to require. It can be set to any value, including an object, function, or a simple data type like a string or number.
exports, module
If you use them inside an ECMAScript module you'll get an undefined Error.
require()
require() inside ECMAScript modules is possible, but you have to use a workaround as mentioned in this answer or take a look at the docs for module.createRequire(fileName):
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
// sibling-module.js is a CommonJS module.
const siblingModule = require('./sibling-module');
If you call require() from within a CommonJS on an ECMAScript module, it throws a not supported Error:
Error [ERR_REQUIRE_ESM]: require() of ES Module /path/to/script.mjs not supported.
With a more detailed error message depending on the situation:
Instead change the require of script.mjs in /path/to/app.js to a dynamic
import() which is available in all CommonJS modules.
Or:
/path/to/script.js is treated as an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which declares all .js files in that package scope as ES modules.
Instead rename /path/to/script.js to end in .cjs, change the requiring code to use dynamic import() which is available in all CommonJS modules, or change "type": "module" to "type": "commonjs" in /path/to/package.json to treat all .js files as CommonJS (using .mjs for all ES modules instead).
ECMAScript Moduls (ESM)
export default is used to export a single value as the default export of a module. This allows for a more concise way to import values, as the import statement can omit the curly braces when importing the default export.
Named exports, on the other hand, allow multiple values to be exported from a module. Named exports use the export keyword followed by an identifier and a value. (export const foo = "bar")
import ... from ...
It can handle CommonJS files and interprets them as if you would've used require().
Example based on express:
import express, { Route, Router } from 'express'; // EJS
// is similar to:
var express = require("express"), { Route, Router } = express; // CJS
Both CommonJS and ECMAScript modules support the import() function, but the returned object can have more properties on ESM files.
Summary:
CJS modules don't need to be converted to ESM, as they can be imported into ESM using the import ... from ... syntax without any modifications to the CJS module. However, it's advisable to write new modules using ECMAScript Module syntax, as it is the standard for both web and server-side applications and enables seamless use of the same code on both sides the browser/client-side and node/server-side.
Specifications
Additionally, I find this article on CommonJS vs. ES modules in Node.js from logrocket.com to be very informative. It delves into the pros and cons of ECMAScript compared to CommonJS in more depth.
Links:
MDN: import()
NodeJS.org: Difference between ECMAScript modules and CommonJS modules
There are two main scenarios:
1. Your package is written using CommonJS (CJS) module loading
This means your package uses require() to load dependencies. For this kind of package no special work is needed to support loading the package in both ES and CJS modules. ES modules are able to load CJS modules via the import statement, with the minor caveat that only default-import syntax is supported. And CJS modules are able to load other CJS modules via the require() function. So both ES modules and CJS modules are able to load CJS modules.
2. Your package is written using ES module loading
This means your package uses import to load dependencies. But don't be fooled - sometimes, especially when using TypeScript, you may be writing import in your code, but it's getting compiled to require() behind the scenes.
Unfortunately, CommonJS modules do not support loading ES modules except (in Node.js) by using the import() function (which is a bit painful and not a great solution).
In order to support CommonJS in this case, your best bet is to transpile your package into a CommonJS module, and ship both CommonJS and ESM versions of your package.
I do this in a number of my own packages mostly by using Rollup, which makes it relatively easy.
The basic concept is this:
Write your package as an ES module.
Install rollup: npm i -D rollup
Run npx rollup index.js --file index.cjs --format cjs to convert your code into a CJS module.
Export both from your package.json:
{
"name": "my-package",
"version": "1.0.0",
"main": "index.js",
"type": "module",
"exports": {
"import": "./index.js",
"require": "./index.cjs"
}
}
This way, the CJS module loader knows to load your index.cjs file, while the ESM loader knows to load your index.js file, and both are happy.
I have a file, test.js with these lines of code inside:
import {blabla} from "./bla";
async function dataGenerator() {
..........
}
(async() => {
console.log('1')
await dataGenerator()
console.log('2')
})()
Note: Ignore the import structure. It is just fictive for the question. In my file the imports are auto.
When I'm trying to run from terminal with node test.js it returns error:
Cannot find module 'D:\bla' imported from D:\test.js
I have added into package.json the line: "type": "module". Without this it returns:
Cannot use import statement outside a module
I'm using node v14. How can I run the test.js without adding to all the imports ".js". There are functions in functions in functions and is complicated to add .js extension. Is there any npm to run it?
Node.js by default does not attempt to guess the file extension when using import for ES modules. This is different from CommonJS modules with require.
In the documentation for the ES module loader you can read how files are found on disk.
The heading 'Customizing ESM specifier resolution algorithm' states:
The --experimental-specifier-resolution=[mode] flag can be used to customize the extension resolution algorithm. The default mode is explicit, which requires the full path to a module be provided to the loader. To enable the automatic extension resolution and importing from directories that include an index file use the node mode.
I have done a react project on my ubuntu system and uploaded on github, now I am cloning that same repository on my windows system and doing npm install and npm start. It is showing this errors.
Check these images
[1]: https://i.stack.imgur.com/3cKFp.png
[2]: https://i.stack.imgur.com/v5OHz.png
I am bit new to react so please help me to fix this issue. My npm version on windows is 8.7.0 and node version is v12.16.3
Compiled with problems:X
ERROR
Must use import to load ES Module: C:\Users\user\Desktop\port\frontend\node_modules\#eslint\eslintrc\universal.js
require() of ES modules is not supported.
require() of C:\Users\user\Desktop\port\frontend\node_modules\#eslint\eslintrc\universal.js from C:\Users\user\Desktop\port\frontend\node_modules\eslint\lib\linter\linter.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename universal.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from C:\Users\user\Desktop\port\frontend\node_modules\#eslint\eslintrc\package.json.
ERROR
Error: Child compilation failed:
Must use import to load ES Module: C:\Users\user\Desktop\port\frontend\node_modules\#eslint\eslintrc\universal.js
require() of ES modules is not supported.
require() of C:\Users\user\Desktop\port\frontend\node_modules\#eslint\eslintrc\universal.js from C:\Users\user\Desktop\port\frontend\node _modules\eslint\lib\linter\linter.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename universal.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from C:\Users\user\Desk top\port\frontend\node_modules\#eslint\eslintrc\package.json.
- child-compiler.js:169
[frontend]/[html-webpack-plugin]/lib/child-compiler.js:169:18
- Compiler.js:564
[frontend]/[webpack]/lib/Compiler.js:564:11
- Compiler.js:1183
[frontend]/[webpack]/lib/Compiler.js:1183:17
- Hook.js:18 Hook.CALL_ASYNC_DELEGATE [as _callAsync]
[frontend]/[tapable]/lib/Hook.js:18:14
- Compiler.js:1179
[frontend]/[webpack]/lib/Compiler.js:1179:33
- Compilation.js:2784 finalCallback
[frontend]/[webpack]/lib/Compilation.js:2784:11
- Compilation.js:3089
[frontend]/[webpack]/lib/Compilation.js:3089:11
- Hook.js:18 Hook.CALL_ASYNC_DELEGATE [as _callAsync]
[frontend]/[tapable]/lib/Hook.js:18:14
It seems that you have a problem with your node version. Most of the react component to run on node version 16. Upgrade your node version and I think it will solve your issue.
The answer is in the error itself. You are using require in your universal js which is not supported, please use import instead
I am confused about require and import in node.js.
Some modules use require and some use import can we use only one or the other for all modules?
Yes, you can exclusively use import statements to load modules.
You must configure Node to use the ESM loader. The most common method of doing this is setting the "type" field in package.json to "module":
// package.json
{
"type": "module"
}
See Determining the module system in the docs.
Given the following quote from the ECMAScript documentation and minimal reproducible code example,
why does using the .js file extension for an Javascript ES module import cause an ERR_MDOULE_NOT_FOUND error when package.json has "type": "module"?
From Node.js v16.3.0 documentation - Determining module system (emphasis mine)
Determining module system
Node.js will treat the following as ES modules when passed to node as the initial input, or when referenced by import statements within ES module code:
Files ending in .mjs.
Files ending in .js when the nearest parent package.json file contains a top-level "type" field with a value of "module".
The documentation says that a .js file extension is treated as an ES module as long as we declare our package's type as module.
Now consider the following minimal reproducible example of how a .js file does not get treated as an ES Module unless renamed to .mjs.
package.json
{
"type": "module"
}
foo.js
export default 'foo module';
index.js
import foo from './foo';
console.log("Hello", foo);
With the above file names and code, the following error occurs.
$ node index.js
node:internal/process/esm_loader:74
internalBinding('errors').triggerUncaughtException(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/Users/georgep/nodemodulestest/foo' imported from /Users/georgep/nodemodulestest/index.js
Did you mean to import ../foo.js?
at new NodeError (node:internal/errors:363:5)
at finalizeResolution (node:internal/modules/esm/resolve:307:11)
at moduleResolve (node:internal/modules/esm/resolve:742:10)
at Loader.defaultResolve [as _resolve] (node:internal/modules/esm/resolve:853:11)
at Loader.resolve (node:internal/modules/esm/loader:89:40)
at Loader.getModuleJob (node:internal/modules/esm/loader:242:28)
at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:73:40)
at link (node:internal/modules/esm/module_job:72:36) {
code: 'ERR_MODULE_NOT_FOUND'
}
But, if I change the following
change foo.js to foo.mjs, and
Update the import in index.js to reflect foo.mjs => import foo from './foo.mjs';
Then the program executes without error.
Why is the .mjs file ending necessary in this situation, when the documentation clearly states that setting "type": "module" in package.json should mean that node treats regular .js files like ES modules?
Environment:
$ node -v
v16.3.0
As pointed out by #ASDFGerte in their comment, and also fully explained in this answer to "Omit the file extension, ES6 module NodeJS":
In ES Modules, file extension is mandatory, so you cannot omit the .js file extension like you can in CommonJS.
This was the source of my confusion. Once I include the file extension, ES Modules work. For example, this works.
index.js
import foo from './foo.js';
The solution feels pretty obvious, but without knowing the exact reason, this difference between the CommonJS convention and ES Modules would have felt like a bug anyways, so I'm happy to have learned how each module system treats its file extensions.