How to enforce node version compatibility during local development only

Hello, me and my team are enforcing node versions during local development on a project by adding a node version range in the package.json engines field and setting engine-strict to true in a root-level .npmrc file. This works great since running any npm commands would fail with a helpful error when the developer is using a node version not specified in the engines field. But I’ve noticed that it causes problems when the project with this setup becomes a dependency of another project. Take this example:

  1. Developer 1 has node version 12 installed on local machine
  2. Developer 1 creates repository for foo-dep package that has its engines.node field set to >=12 and has engine-strict=true in a root-level .npmrc file to enforce node version when working on the project locally
  3. Developer 1 publishes foo-dep
  4. Developer 2 has node version 11 installed on local machine
  5. Developer 2 tries to run npm i foo-dep package on Project X with its own engines.node set to <12 and has engine-strict=true in its .npmrc file

The installation will fail for Developer 2 because the developer tried to install foo-dep while using a node version that is incompatible with foo-dep. I understand this error if the foo-dep package exposed something that requires node to run, but it just publishes a bundled, transpiled javascript file for the browser that doesn’t require any node version to be used, so the error seems odd in this case.

What is the recommended way to ensure a node version is enforced when developing on the foo-dep project, but is not enforced on developers who install it?

As documented, the engines field of package.json is used to specify the runtime requirements of a package. As such, its purpose is to ensure that a package is not installable (or at least warns of incompatibility) on node versions that are incompatible. The setup you describe above is working as designed to ensure that a package declared to only support node 12 is not installable on versions less than 12.

IOW, the engines field of package.json is not to be used for development version locking. That’s not its purpose or role, and attempting to use it as such leads to complications.

If your goal is to allow developers a way to lock their development environments to a particular version of node, without impacting the runtime requirements of a package, that is what node version managers are for. I suggest nodenv. Nodenv is built to lock either an application (development or production) or a package (development) to a particular version of node. With the nodenv-aliases plugin, you can provide loose version specifiers. And with the nodenv-package-json-engine plugin, nodenv will fall back to respecting the package.json#engines field when a .node-version file does not exist.

1 Like

Thanks for the reply! The problem is that I work for a company with very very large, independently-operated teams. We have a ton of projects, that each require different local versions of node. We’ve found that simply adding node version disclaimers on READMEs or instructions to use certain binaries like node version managers are unsuccessful.

We’d like the ability to enforce a node version when working on a package locally that is not enforced at the consumer level for our client-side npm packages. How can we do this without relying on node version managers? We’d like to exit with an error code and helpful message whenever an npm command is run on a project while using an incompatible node version.

FYI I’ve also opened https://github.com/npm/rfcs/issues/50 to explore doing an RFC for this feature.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.