Up to Node v8.5.0, publishing a module written in ES6 to NPMJS was a straightforward process: transpile the ES6 code using a tool like Babel, and publish to NPMJS the resulting lib
directory, while your GitHub repo contains the src
files.
With v8.5.0, Node has released experimental support for native modules (export
/import
) via the --experimental-modules
flag. It is now possible to publish purely-ES6 modules to NPMJS, and use them without any transpilation, as long as the files involved have an .mjs extension.
How can I publish an ES6 module (.mjs) so that it can also be used with older Node versions, which don't support ES native modules?
Update:
This is possible with 13.7.0+ using conditional exports (which as of 13.10.0+ are no longer experimental). It's not well documented or obvious how to do this in a completely backwards-compatible way, but here's the trick which I previously researched back when it was experiemental:
node_modules/mod/package.json
node_modules/mod/lib.js
node_modules/mod/lib.mjs
Now it's possible to use both CommonJS:
main.js
And ES Modules:
main.mjs
Old answer:
The trick is not to specify an exact path in the package.json
main
entry. Instead use an extension-lessmain
entry, or supply both anindex.js
andindex.mjs
at the root of the package.Option 1 - Extension-less main:
If you don't include the extension, Node will dynamically use the
.mjs
extension if available and using the ES6 loader, or fallback on.js
.This will resolve to
lib/entry.mjs
in ES6 module mode, orlib/entry.js
in CommonJS mode, with the ES6 loader falling back on the CommonJS version if the MJS file is not available.Option 2 - Use
index.mjs
andindex.js
instead:If your package supplies a root
index.mjs
andindex.js
Node will prefer theindex.mjs
whenimport
-ed, and still use theindex.js
whenrequire
-ed (if noindex.mjs
is supplied, the ES6 loader will use theindex.js
). This means you can supply both an ES6 module version fromindex.mjs
and a CommonJS transpiled version fromindex.js
.Possible Issue:
There is one potential issue that I can think of though, if users of your package mix using both the ES6 and CommonJS modules and expect them to reference the same set of objects. In certain edge-cases, this could be an issue, but multiple packages using the exact same module instance was never a given anyway because different packages can require different versions of the package.
Example:
Example project:
index.mjs
index.js
node_modules/testmod/package.json
In this file, you can optionally use an extension-less
main
entry like this:node_modules/testmod/index.mjs
node_modules/testmod/index.js
Example output (ExperimentalWarning omitted):