Risks of Using NPM for Front-End Packages

A few years ago, there was a big push to use npm instead of Bower as the preferred package manager for JavaScript packages. The thought was, “it’s all JavaScript, why not put it all in the same place?” What followed was much pooh-poohing of Bower, and many front-end devs jumped on the npm bandwagon. At the time, I was wary of such a move, since npm is the Node package manager, and Bower is the web package manager. However, for the most part, devs have been able to use npm to manage JavaScript dependencies for Node, browser, and isomorphic applications without much difficulty.

Until recently.

Node 4 LTS reached end-of-support in April of this year, which opened the floodgates to packages incorporating more ES6 features into their npm packages destined for Node. I bumped against this recently when using camelcase, which had a recent update that included dropping support for Node 4, which included a move to using ES6 arrow functions. This was problematic in my case, because we were using camelcase on a web project, and Sindre Sorhus doesn’t believe in transpiling code to ES5 before publishing on npm. His rationale for this is sound—he’s publishing a Node module to npm with explicit version requirements listed in the package, and there’s no need to transpile the package to work with the listed Node versions. The problem is when developers use that package in a web context, for which it was not designed, and for which it will not natively work in any browser that requires it to be transpiled.

Many (most?) Babel configurations ignore the node_modules directory, because a) most of what has historically been loaded from npm has not required transpilation in order to work in the browser and b) project standards differ, so if you run node_modules through Babel and, for example, a module fails a strict standards check, it can fail your build. Plus, transpiling everything in node_modules is expensive, and slows down build and deployment tasks. It is therefore up to the individual developer to know which modules require transpilation, and to whitelist them in the Babel configuration.

This problem also exists in the other direction. Take, for example, the whatwg-fetch package, which is a polyfill for window.fetch() that only works in the browser, a fact which is noted about a quarter of the way down the page on npmjs.com:

This project doesn’t work under Node.js environments. It’s meant for web browsers only. You should ensure that your application doesn’t try to package and run this on the server.

(For those interested in a polyfill that works in both the Node and browser contexts, you should check out isomorphic-fetch.)

With the sunset of Node 4, and what I am sure will be an increase in the number of npm packages that ship using ES6 code that is supported by Node 6 that wasn’t supported by Node 4, I predict that this problem is going to get worse. At this point, it’s unlikely that a proposal to split Node packages from browser packages will gain much traction among JavaScript developers, especially since so many of them can be used in isomorphic contexts. Therefore, I would advocate that, at a minimum, npmjs.com include a feature to tag modules as “intended for Node” or “intended for the browser” or “intended for both (isomorphic).” This idea could be further extended by making better use of the browser property in package.json, which indicates that the module is intended specifically for the browser, and providing warnings when including Node modules that are not ready for the browser out-of-the-box.

Until and unless tooling catches up, I would recommend performing IE11 testing after every feature build to catch these issues early. Including untranspiled ES6 in a minified JS bundle yields an extremely unhelpful “syntax error on line 1, column 1623452” error in IE, which does little to point to the specific package that is causing the problem.

Leave a comment

Your email address will not be published. Required fields are marked *