Some time ago I ran into a terrible issue. It cost me more than an hour to debug and some new gray hairs, so here I am to warn others.
So, imagine that you have a few Node.js subprojects in a few directories, being a part of a larger project, like this:
project | \-- subprojectA \-- subprojectB
Each of them has some stuff usually associated with Node.js projects: a package.json
, a package-lock.json
and a .node-version
(this last one tells nodenv which Node version to use).
Now, the system-wide version of Node.js is different than what is needed in, say, subprojectA
. But that’s ok, because we have nodenv
.
Except that it’s not. It turns out that nodenv
is a leaky abstraction. And here is how it manifests itself.
In the main project
directory I have a project-wide Node.js script, say build.js
, which builds all of the subprojects. This means that I want to run npm ci
(or something similar) in every one of them. Unfortunately, there seems to be no reliable programmatic API for npm, so I need to run an npm
subprocess. However, when I start the build.js
script, nodenv
does its magic and sets the NODENV_DIR and NODENV_VERSION environment variables (along with a few others). Then, nodenv
’s npm
shim sees those variables, and they override the .node-version
setting. This way, the system-wide version of npm
instead of the required version is run. (This bit me because package-lock.json
is not compatible across npm
versions.)
The remedy is fairly easy – just say
delete process.env.NODENV_DIR; delete process.env.NODENV_VERSION;
before running npm
from your script. The trick is, however, that it is not at all obvious that this should be required.
Oh, and one more thing. Before you run screaming and start to tell everyone that JavaScript is a terrible technology – please don’t. This is old news, we all know that JS is broken (together with most IT we know and love). But you know what? It is way better to work on shitty technology with a fantastic team than the opposite. So shut up. (And yes, it’s even better to work on fantastic technology with a fantastic team, so my long-term plan is to convert my teammates to Clojure and Emacs, but don’t tell anyone. As most other world-domination schemes, this requires patience and should be done in secret.)