I was about to publish a module to NPM, when I thought about rewriting it in ES6, to both future-proof it, and learn ES6. I've used Babel to transpile to ES5, and run tests. But I'm not sure how to proceed:
- Do I transpile, and publish the resulting /out folder to NPM?
- Do I include the result folder in my Github repo?
- Or do I maintain 2 repos, one with the ES6 code + gulp script for Github, and one with the transpiled results + tests for NPM?
In short: what steps do I need to take to publish a module written in ES6 to NPM, while still allowing people to browse/fork the original code?
The two criteria of an NPM package is that it is usable with nothing more than a
require( 'package' )
and does something software-ish.If you fulfill those two requirements, you can do whatever you wish. Even if the module is written in ES6, if the end user doesn't need to know that, I would transpile it for now to get maximum support.
However, if like koa, your module requires compatibility with users using ES6 features, then perhaps the two package solution would be a better idea.
Takeaway
require( 'your-package' )
work.The pattern I have seen so far is to keep the es6 files in a
src
directory and build your stuff in npm's prepublish to thelib
directory.You will need an .npmignore file, similar to .gitignore but ignoring
src
instead oflib
.Deppending on the anatomy of your module, this solution may not work, but if your module is contained inside a single file, and has no dependencies (does not make use of import), using the following pattern you can release your code as it is, and will be able to be imported with import (Browser ES6 Modules) and require (Node CommonJS Modules)
As a bonus, it will be suittable to be imported using a SCRIPT HTML Element.
main.js :
my-module.js :
package.json :`
To use your module, you can now use the regular syntax ...
When imported in NODE ...
When imported in BROWSER ...
Or even when included using an HTML Script Element...
TL;DR - Don't, until ~October 2019. The Node.js Modules Team has asked:
2019 May update
Since 2015 when this question was asked, JavaScript support for modules has matured significantly, and is hopefully going to be officially stable in October 2019. All other answers are now obsolete or overly complicated. Here is the current situation and best practice.
ES6 support
99% of ES6 (aka 2015) has been supported by Node since version 6. The current version of Node is 12. All evergreen browsers support the vast majority of ES6 features. ECMAScript is now at version 2019, and the versioning scheme now favors using years.
ES Modules (aka ECMAScript modules) in browsers
All evergreen browsers have been supporting
import
-ing ES6 modules since 2017. Dynamic imports are supported by Chrome (+ forks like Opera and Samsung Internet) and Safari. Firefox support is slated for the next version, 67.You no longer need Webpack/rollup/Parcel etc. to load modules. They may be still useful for other purposes, but are not required to load your code. You can directly import URLs pointing to ES modules code.
ES modules in Node
ES modules (
.mjs
files withimport
/export
) have been supported since Node v8.5.0 by callingnode
with the--experimental-modules
flag. Node v12, released in April 2019, rewrote the experimental modules support. The most visible change is that the file extension needs to be specified by default when importing:Note the mandatory
.mjs
extensions throughout. Run as:The Node 12 release is also when the Modules Team asked developers to not publish ES module packages intended for use by Node.js until a solution is found for using packages via both
require('pkg')
andimport 'pkg'
. You can still publish native ES modules intended for browsers.Ecosystem support of native ES modules
As of May 2019, ecosystem support for ES Modules is immature. For example, test frameworks like Jest and Ava don't support
--experimental-modules
. You need to use a transpiler, and must then decide between using the named import (import { symbol }
) syntax (which won't work with most npm packages yet), and the default import syntax (import Package from 'package'
), which does work, but not when Babel parses it for packages authored in TypeScript (graphql-tools, node-influx, faast etc.) There is however a workaround that works both with--experimental-modules
and if Babel transpiles your code so you can test it with Jest/Ava/Mocha etc:Arguably ugly, but this way you can write your own ES modules code with
import
/export
and run it withnode --experimental-modules
, without transpilers. If you have dependencies that aren't ESM-ready yet, import them as above, and you'll be able to use test frameworks and other tooling via Babel.Previous answer to the question - remember, don't do this until Node solves the require/import issue, hopefully around October 2019.
Publishing ES6 modules to npm, with backwards compatibility
To publish an ES module to npmjs.org so that it can be imported directly, without Babel or other transpilers, simply point the
main
field in yourpackage.json
to the.mjs
file, but omit the extension:That's the only change. By omitting the extension, Node will look first for an mjs file if run with --experimental-modules. Otherwise it will fall back to the .js file, so your existing transpilation process to support older Node versions will work as before — just make sure to point Babel to the
.mjs
file(s).Here's the source for a native ES module with backwards compatibility for Node < 8.5.0 that I published to NPM. You can use it right now, without Babel or anything else.
Install the module:
Create a test file test.mjs:
Run node (v8.5.0+) with the --experimental-modules flag:
TypeScript
If you develop in TypeScript, you can generate ES6 code and use ES6 modules:
Then, you need to rename
*.js
output to.mjs
, a known issue that will hopefully get fixed soon sotsc
can output.mjs
files directly.@Jose is right. There's nothing wrong with publishing ES6/ES2015 to NPM but that may cause trouble, specially if the person using your package is using Webpack, for instance, because normally people ignore the
node_modules
folder while preprocessing withbabel
for performance reasons.So, just use
gulp
,grunt
or simply Node.js to build alib
folder that is ES5.Here's my
build-lib.js
script, which I keep in./tools/
(nogulp
orgrunt
here):Here's a last advice: You need to add a .npmignore to your project. If
npm publish
doesn't find this file, it will use.gitignore
instead, which will cause you trouble because normally your.gitignore
file will exclude./lib
and include./src
, which is exactly the opposite of what you want when you are publishing to NPM. The.npmignore
file has basically the same syntax of.gitignore
(AFAIK).