What is the minimum version of Node your NPM module requires? You might think it is Node 8 or Node 10 or even Node 6. But in reality you don't know - because the direct or transient NPM dependencies your project uses might require a different higher version, without even declaring it explicitly in the
engines field of their
Recently, we have experienced sudden "bumps" in minimum Node version required for our Cypress NPM package because of chalk and execa dependencies. While we promised supporting Node v8.0.0, due to these dependencies our true minimum Node version turned out to be v8.12.0!
The only reliable way to determine if your project runs on Node 8.0.0 is to run your NPM package on Node 8.0.0. In this blog post I will show how to bundle and transpile your NPM package so it truly runs on Node v8.0.0 or even on Node v6.
Note: you can find the source code at bahmutov/support-node-v6
Let's start a new NPM application that uses
execa to print the list of files. We want to use the latest versions of all the tools, so we are working on Node v12
~/git/support-node-v6 on master
Here is our application file
const chalk = require('chalk')
The app runs and the colors show up (on Node v12)
Now let's try the same application on Node v6.
chalk uses spread operator, it does not run on Node v6 - and you would need to downgrade to
[email protected]. But then the same happens with
execa - it also uses the spread operator!
You would need to downgrade
execa all the way to v1 to get the syntax compatible with Node v6. In the process you just lost soooo many features and fixes from
chalk, it is almost sad.
Let's switch tactics. Instead of searching for an old version of
$ npm i -S chalk execa
We can use
index.js entry using
npm run build script
$ npm run build
The bundle we produced does not run on Node 6 yet.
But the bundle has all dependencies included. We can remove
node_modules folder and run it to prove this.
Now we can transpile this single file to make it work on (almost) any Node version.
I like using TypeScript compiler to transpile code.
This almost works.
The bundle is transpiled - the spread operators in
execa have been replaced, but the bundle still has
Object.entries method that Node v6 does not understand. We can polyfill this method though.
$ npm i -S babel-polyfill
Then require the polyfill from
Let's build and transpile again.
Nice, the latest dependencies do work on Node v6!
Since we only plan to distribute a single bundle, we can generate the intermediate bundle in the
build folder, and run the transpile step post-build.
Specify bundled dependencies
We can see the produced bundle using
npm pack --dry command.
$ npm run build
The zipped archive has size 114 kB, which looks like a lot. But remember, that anyone installing this NPM app should only download this single file, which should be as fast as downloading multiple production dependencies. But we still list
chalk as production dependencies, thus our users will get two copies of them - the second one coming from the
Hmm, NPM understands bundled dependencies list, but this list forces
execa into the TGZ archive twice!
$ npm pack --dry
Thus my advice is to move all production dependencies into
devDependencies - they will be included in the bundle as needed and skip using
Let's make sure the bundled dependencies work. I have created a new NPM package in a different folder and will install and run the above project using Node v6.
typescript can both generate source maps, I could not find a way to connect the two to get the source maps to link an error back to the original source file. If you know how to do this, open a pull request in bahmutov/support-node-v6, please.
Some ES6+ syntax and features cannot be transpiled down, for example if your project requires WeakMaps or Proxies - you are out of luck.
alternative: use Parcel
@zeit/ncc we can use Parcel bundler to bundle code like this: