npm, ECMAScript 2015, and Babel
Publishing an npm package written in vanilla ECMAScript 5 is quite simple: write the code, add a package.json
file, and run npm publish
with your npm credentials set up. However, in the era of transpilers, more and more projects are written in ECMAScript 2015 and down-leveled to ECMAScript 5 using a transpiler such as Babel or TypeScript.
The problem with these projects is that you don't want to publish the raw source code as an npm package, but the transpiled version that doesn't contain any ECMAScript 2015 language constructs anymore (or type annotations, in the case of TypeScript). ECMAScript 2015 is not widely supported yet, which is why JavaScript code is down-leveled to ECMAScript 5 first so that current browsers and Node.js environments can execute it.
#Writing an ECMAScript 2015 Module
Let's say we've written a very simple math module that exports as its default value an object defining a square
method:
export default {
square: x => x * x,
};
The above module makes use of two ECMAScript 2015 language features: it exports a value via the native module system, and it implements square
via an arrow function. Neither construct is widely understood by today's browsers. Using Babel, our math module can be transpiled into the following CommonJS module:
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports["default"] = {
square: function square(x) {
return x * x;
},
};
module.exports = exports["default"];
This module can now be loaded via require
calls or through any dependency loader that supports CommonJS syntax.
#Using Babel via Gulp
In a typical JavaScript project, a build system like Gulp is used to run various tasks, one of which is Babel's transpilation process. While the babel
executable could be run on its own, Gulp provides a simpler way to run it on every file change, which is what you usually do during development. Here's a simple gulpfile.js
:
var gulp = require("gulp");
var babel = require("gulp-babel");
var libDir = "lib/";
var jsFiles = "src/**/*.js";
gulp.task("babel", function () {
return gulp.src(jsFiles).pipe(babel()).pipe(gulp.dest(libDir));
});
gulp.task("babel-watch", function () {
gulp.watch(jsFiles, ["babel"]);
});
gulp.task("default", ["babel", "babel-watch"]);
Whenever the babel
task is run, all JavaScript files in the src/
directory will be transpiled and written into the lib/
directory. The babel-watch
task can be started during development to add a file watcher that kicks off Babel's transpilation process whenever a .js
file changes.
#Excluding Files from Packages
The lib/
directory, which contains the transpiled output generated by Babel, is usually not checked into version control. Therefore, the .gitignore
typically contains the following lines:
lib/
node_modules/
On the other hand, the npm package should not contain the src/
directory, but it should contain lib/
directory. By default, npm will exclude files ignored in the .gitignore
from the package. That behavior can be overridden by providing a .npmignore
file. In this case, an empty .npmignore
can be created for the single purpose of overriding the .gitignore
. Additionally, the files
property in package.json
is used to allowlist all files to be included in the package:
{
"name": "npm-babel-example",
"files": ["lib/"],
"...": "..."
}
Note that certain files are always included, regardless of whether or not they have been allowlisted or excluded. According to the npm documentation, these files are:
package.json
README
(and its variants)CHANGELOG
(and its variants)LICENSE
/LICENCE
#Publishing an NPM Package
Lastly, we need to make sure all JavaScript are transpiled before publishing the package to the npm Registry. Add the following command to the "scripts"
section of package.json
:
{
"scripts": {
"prepublish": "gulp babel"
}
}
When publishing a package, npm looks for a script command named "prepublish"
and runs it, if present. For the simple transpilation process we have defined, only gulp babel
needs to be executed.
And that's it! This is how to write, transpile, and publish npm packages using ECMAScript 2015, Babel, and Gulp. You can find the full source code for this example on GitHub and the transpiled package on npm: