Writing NPM packages with ES6 using the Babel 6 CLI

This guide is part of The Complete Guide to ES6 with Babel 6 series. If you’re having trouble upgrading to Babel 6, start with Six Things You Need To Know About Babel 6.

Even if you write your NPM package with ES2015, it is important that the code you publish is compatible with everyone else’s JavaScript environments. That means compiling your code to ES5 before publishing it. And of course, the best way to do so is with the Babel CLI.

Before we set up compilation with Babel 6, make sure to delete any previous Babel-related packages from your project’s node_modules directory — babel, babel-core, etc.. Once you’ve done so, install the babel-cli package and ES2015 preset:

npm install babel-cli babel-preset-es2015 --save-dev

If you’d like to use proposed ES7 features, you’ll also need to install the babel-preset-stage-0 package:

npm install babel-preset-stage-0 --save-dev

To instruct Babel to use the installed presets, pass them to to the --presets option as a comma-separated list. So assuming your source is in a src directory and you want to place your output in lib, you’d do this:

./node_modules/.bin/babel --presets es2015,stage-0 -d lib/ src/

We call Babel from ./node_modules/.bin/babel instead of installing babel globally with npm install babel-cli -g to ensure that we’re not messing up the environment of any other projects using Babel 5. But typing out this command sure doesn’t sound like much fun, so let’s record this in package.json as a compile script:

"scripts": {
    "compile": "babel --presets es2015,stage-0 -d lib/ src/"

You can now compile your project by just typing npm run compile from your project root.

Finally, let’s add a prepublish script to ensure compilation happens automatically before uploading to NPM:

"scripts": {
    "compile": "babel --presets es2015,stage-0 -d lib/ src/",
    "prepublish": "npm run compile"

Don’t forget to add lib to your .gitignore file, and src/ to your .npmignore file!

# .gitignore - the files to not push via git

# .npmignore - the files to not push to NPM
Hint: .babelrc

Note that you could omit the --presets es2015,stage-0, and instead add a .babelrc file:

  "presets": ["es2015","stage-0"]

However, my recommendation is to keep the options to your command-line tools on the command line.

Babel Runtime

Babel can’t support all of ES6 with compilation alone – it also needs to add some code to your application.

This is generally solved by requiring the babel-polyfill package at the entry point of your application. This package polyfills new ES6 Built-ins like Map, Set and Promise, while also requiring babel-runtime to add runtime support for Babel’s generators.

However. If you’re publishing a library to NPM, it is bad practice to mess with the entire global environment. In this case, you have three options:

  1. Avoid features which require runtime support (including new Built-ins and generators)
  2. Require babel-runtime directly, allowing usage of generators, but not polyfills which would modify the global environment
  3. Require babel-polyfill as a peer dependency and document that the consuming app must require it


For an example of an NPM package configured with Babel 6, see react-pacomo.

More ways to Babel

Now that you’ve got your library building with ES6, why not test it with ES6 too? And write your tasks with it. And write your app with it…

Keeping up to date

So you’ve made Babel 6 work. But the JavaScript world moves fast — how long will it stay working?

If trying to keep up feel overwhelming, I’d love to help! Just subscribe to my free Newsletter to receive news and guides on the most important tools for people making small apps with React. And in return for your e-mail, you’ll immediately receive three bonus print-optimised PDF cheatsheets – on React (see preview), ES6 and JavaScript promises. All for free!

I will send you useful articles, cheatsheets and code.

I won't send you useless inbox filler. No spam, ever.
Unsubscribe at any time.

One more thing – I love hearing your questions, offers and opinions. If you have something to say, leave a comment or send me an e-mail at james@jamesknelson.com. I’m looking forward to hearing from you!

Read More

10 Comments Writing NPM packages with ES6 using the Babel 6 CLI

  1. dmitry

    Can you help me with compile decorators? I wrote the following commands:
    $ npm install -g babel-cli
    $ npm install babel-preset-es2015 babel-preset-stage-0 –save-dev
    $ npm install core-decorators –save

    Сreate file with name src/main.js:

    The last command:
    $ babel –presets es2015,stage-0 -d lib/ src/

    Result in console:
    src\main.js -> lib\main.js

    result in file:
    ‘use strict’;

    var _coreDecorators = require(‘core-decorators’);

    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(“Cannot call a class as a function”); } }

    var Meal = function Meal() {
    _classCallCheck(this, Meal);

    var dinner = new Meal();
    dinner.entree = ‘salmon’;

  2. Jesper Ek

    Thanks for the awesome guide! I really liked how you cleared up the usage of polyfill/runtime.

    However, I’m a bit troubled how to run our applications in production. The official documentation states that you should not use babel-node in production: “You should not be using babel-node in production. It is unnecessarily heavy, with high memory usage due to the cache being stored in memory. You will also always experience a startup performance penalty as the entire app needs to be compiled on the fly.”

    But the docs doesn’t say anything about the require-hook / babel-register. My understanding is that they are quite similar in function, they both compile on the fly and cache files and they both include the polyfill. So, if you want to run your application in production properly, is the only way to transpile ahead of time? I wouldn’t mind seeing a guide on this topic 😉

  3. Ajedi32

    > However, my recommendation is to keep the options to your command-line tools on the command line.

    I would argue instead that you should keep your config options for your project in a config file.

    Just because you *can* pass something as a command line argument doesn’t mean that’s the right way to do it.


Leave a Reply

Your email address will not be published.