Releasing for old Node

How I publish modules for older versions of Node.

I like modern Nodejs. Destructuring is probably my favorite feature.

1
const {map, prop} = require('ramda')

Yet, this syntax breaks for anyone using Node 4 (some people) or Node 0.12 (why are they still using it?!) So I usually get requests to remove an ES6 feature because Node 4 users cannot use one of my modules. Or a request to downgrade Node engine declared in the package.json file.

One can quickly check if a module at least loads on Node 4

1
2
3
4
nvm use 4
rm -rf node_modules
NODE_ENV=production npm install
node .

If the module does not load on Node 4, it is probably using one of the missing / behind a flag features. We should make our module Node 4-compatible, right?

Here is a problem. I could set up transpiler and build Node 4-compatible source code before publishing. Yet this does not fully solve the problem. Often the offending code is NOT my own, but instead comes from one of many many dependencies used during development and semantic release!

So there are really four problems:

  1. making sure my own code is Node 4 compatible. Self-discipline and transpiling help.
  2. running tests should use Node 6 because the testing tools keep improving. I am not going to downgrade my snap-shot!
  3. I still want to test if the package can be used from Node. Note that this is not even a subset of all tests, but rather is a demo script.
  4. finally, the publishing to NPM registry should use Node 6. While I use semantic-release and [semantic-action][semantic-action] which run on Node 4, their plugins might not.

Here is my typical project setup, simple-changelog is a good example.

  1. package.json and README.md say that this module works on Node 4

    1
    2
    3
    4
    5
    {
    "engines": {
    "node": ">=4"
    }
    }
  2. Travis CI runs against both Node 4 and 6. For example see .travis.yml file.

    1
    2
    3
    node_js:
    - '4'
    - '6'

Some of the modules do not really like NPM@2 that comes with Node 4, so I install NPM@3 before installing dependencies

1
2
before_install:
- npm install -g npm@3
  1. BUT the Travis CI only runs the "demo" step on all Node versions. It only runs the unit tests on Node 6. While this might miss a few edge cases, it allows to keep upgrading all devDependencies without holding back. To only run a command on Node 6 I use if-node-version

    1
    2
    3
    script:
    - npm run demo
    - $(npm bin)/if-node-version ">=6" npm test

    A demo could be complex or just node . to make sure there are no obvious syntax incompatibilities.

  2. Publishing to NPM should also happen from Node 6. Because Travis sometimes gets confused by multiple jobs with error This test run is not the build leader (see issue) we need to hack the command a little bit, since we are limiting it to Node 6 anyway (I assume that you only have single Node 6 version in the build matrix)

    1
    2
    after_success:
    - TRAVIS_JOB_NUMBER=WORKAROUND.1 $(npm bin)/if-node-version ">=6" npm run semantic-release

That's it. While dirty, this approach of Node 4 for a demo + Node 6 for everything else seems to satisfy most of my own and my users' needs.

Transpiling

What if you really wanted to transpile your own source code? The setup using Babel is very simple. Create .babelrc file and use env preset and specify target version of Node 4

1
2
3
4
5
6
7
8
9
{
"presets": [
["env", {
"targets": {
"node": "4"
}
}]
]
}

Setup transpile command in the package.json to grab the entire folder and (optionally) copy some files without transpiling them.

1
2
3
4
5
6
7
{
"scripts": {
"transpile": "babel es6 --out-dir src --ignore templates/* --copy-files"
},
"files": ["src"],
"semantic-release": "semantic-release pre && npm run transpile && npm publish && semantic-release post"
}

The above long command run the transpile step before publishing to NPM and includes just src folder in the distribution.

3rd Party Modules

What about all the 3rd party modules? How can we make them compatible with Node 4? Can we open an issue for each module and expect things to be fixed for us? Without paying money for this?

I always think that a customer can ask for IE6 support, but will have to pay for it. I am interested in modern tools, and if you want to support something I am not interested in doing, well, it should then pay my bills.

After talking to Jamie Mason about it, we have decided that the most transparent solution for this would be to implement on-demand NPM registry proxy service that would transpile source files to Node 4. That's a great idea, Jamie! It might be a little extra effort to implement, but could work.

This service would sit between you and the public registry. Every time you ask for a package with specific version, it would fetch it from the public registry, test against Node 4, and if it does not load, would transpile the files. Of course, the transpiled bundle will be stored for the future.

Anyone thinks it is a bad idea? I can see enterprise customers paying for this...

Related