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
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.
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 the lib
directory.
You will need an .npmignore file, similar to .gitignore but ignoring src
instead of lib
.
@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 with babel
for performance reasons.
So, just use gulp
, grunt
or simply Node.js to build a lib
folder that is ES5.
Here's my build-lib.js
script, which I keep in ./tools/
(no gulp
or grunt
here):
var rimraf = require('rimraf-promise');
var colors = require('colors');
var exec = require('child-process-promise').exec;
console.log('building lib'.green);
rimraf('./lib')
.then(function (error) {
let babelCli = 'babel --optional es7.objectRestSpread ./src --out-dir ./lib';
return exec(babelCli).fail(function (error) {
console.log(colors.red(error))
});
}).then(() => console.log('lib built'.green));
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).
Node.js 13.2.0+ supports ESM without the experimental flag and there're a few options to publish hybrid (ESM and CommonJS) NPM packages (depending on the level of backward compatibility needed): https://2ality.com/2019/10/hybrid-npm-packages.html
I recommend going the full backward compatibility way to make the usage of your package easier. This could look as follows:
The hybrid package has the following files:
mypkg/
package.json
esm/
entry.js
commonjs/
package.json
entry.js
mypkg/package.json
{
"type": "module",
"main": "./commonjs/entry.js",
"exports": {
"./esm": "./esm/entry.js"
},
"module": "./esm/entry.js",
···
}
mypkg/commonjs/package.json
{
"type": "commonjs"
}
Importing from CommonJS:
const {x} = require('mypkg');
Importing from ESM:
import {x} from 'mypkg/esm';
We did an investigation into ESM support in 05.2019 and found that a lot of libraries were lacking support (hence the recommendation for backward compatibility):
mocha@7.0.0-esm1
.mjs
files:
The (widely used) "prepublish" hook is not doing anything for you.
Best thing one can do (if plans to rely on github repos, not published stuff):
src
from .npmignore (in other words: allow it). If you don't have an .npmignore
, remember: A copy of .gitignore
will be used instead in the installed location, as ls node_modules/yourProject
will show you.babel-cli
is a depenency in your module, not just a devDepenceny since you are indeed building on the consuming machine aka at the App developers computer, who is using your moduledo the build thing, in the install hook i.e.:
"install": "babel src -d lib -s"
(no added value in trying anything "preinstall", i.e. babel-cli might be missing)