ES Modules in NodeJS
ES Modules were introduced in ECMAScript 2015, and can now be used in most modern browsers. NodeJS uses the CommonJS module system, but work is currently underway to finalise ES Modules support in NodesJS, and you can use it today by using experimental flags, or a pre-processor such as Babel.js.
The NodeJS module system uses the CommonJS system (require
and module.exports
), but since version 8.9.0 NodeJS has included ES Modules as an experimental feature, which can be used with the flag --experimental-modules
, as this is experimental it should not be used in a live environment. When running with this experimental flag any files named *.mjs
(see below) will be processed as ES Modules. Note that import
will work for both ES Modules and CommonJS modules.
Filenames
Any ES Modules need to be named *.mjs, otherwise, they will be processed as CommonJS modules. If you try using import from a .js file you will get an error:
> node --experimental-modules module.js
(node:2640) ExperimentalWarning: The ESM module loader is experimental.
D:\Users\Neil\Workspace\github\nodejs-esmodules\module.js:1
(function (exports, require, module, __filename, __dirname) { import message from './myUtil';
^^^^^^
SyntaxError: Unexpected token import
...
at file:///D:/Users/Neil/Workspace/github/nodejs-esmodules/module.js:8:36
Similarly, if a .js file uses export:
> node --experimental-modules module.mjs
(node:13532) ExperimentalWarning: The ESM module loader is experimental.
D:\Users\Neil\Workspace\github\nodejs-esmodules\myUtil\index.js:1
(function (exports, require, module, __filename, __dirname) { export default 'This has been imported (ES modules export)';
^^^^^^
SyntaxError: Unexpected token export
...
at file:///D:/Users/Neil/Workspace/github/nodejs-esmodules/myUtil/index.js:8:36
and if you import a CommonJS module named .mjs:
> node --experimental-modules module.mjs
(node:13644) ExperimentalWarning: The ESM module loader is experimental.
SyntaxError: The requested module does not provide an export named 'default'
at checkComplete (internal/loader/ModuleJob.js:75:27)
at moduleJob.linked.then (internal/loader/ModuleJob.js:58:11)
at <anonymous>
As the import is expecting to find an ES Module default export.
Note- browsers do not determine file types using file extensions, so in browsers, it makes no difference if a file using ES Modules is named *.js or *.mjs, however, to be consistent with NodeJS it is recommended to use *.mjs for ES Modules being used in the browser.
Webpack
An alternative to using the experimental flag is to use Webpack to bundle your files - Webpack supports ES Modules out of the box as of version 2.0, so can bundle both CommonJS and ES Modules without any extra confirguration. For Webpack 4 you can install the webpack
and webpack-cli
dependencies
npm i -D webpack webpack-cli
and then run :
webpack module.mjs -o webpacktest.js --mode=development
to create a new webpacktest.js bundled file that will include any ES Module imports.
Babel
An alternative is to use Babel to transpile any ES Module code back to CommonJS code using Babel. For this we first need to install babel-cli
and babel-preset-env
:
npm i -D babel-cli babel-preset-env
and create a .babelrc
config file containing
{
"presets": ["env"]
}
Now we can run babel to produce files that can be run in standard node
babel module.mjs myUtil/index.mjs myUtil2/index.js -d dist/babeltest
Example
See my nodejs-esmodules repo for a basic example of using the experimental flag, a webpack build and a Babel build.