Skip to content

Specify exact Node/npm versions for development - #2432

Open
TheJaredWilcurt wants to merge 3 commits into
beautifier:mainfrom
TheJaredWilcurt:dev-engines
Open

Specify exact Node/npm versions for development#2432
TheJaredWilcurt wants to merge 3 commits into
beautifier:mainfrom
TheJaredWilcurt:dev-engines

Conversation

@TheJaredWilcurt

@TheJaredWilcurt TheJaredWilcurt commented Apr 20, 2026

Copy link
Copy Markdown

Description

  • Source branch in your fork has meaningful name (not main)

Fixes Issue:

The devEngines field is an official part of the Node/npm package.json specification. Unlike engines which communicates what versions of Node are compatible with the built version of the library, devEngines communicates what version of Node and npm is used for local development, when working on the library. Since devDependencies, like ESLint, may require newer Node/npm versions to work than the built code. This makes it clear to contributors what Node and npm version they should switch to when working with this repo. There are many tools (Volta, Proto, Mise, etc) that can automatically download and switch to the exact version of Node/npm specified in the package.json.

My process for determining the Node/npm versions without devEngines:

  1. Start with the engines support, which is just Node 14. npm is not specified, so I used the latest Node 14 and latest npm.
    • Node 14.21.3
    • npm 11.12.1
  2. However, npm 11 is not compatible with this old of a Node version, so set npm to the same version that shipped with Node 14 (6).
    • Node 14.21.3
    • npm 6.14.18
  3. Running npm i changes the package-lock.json from version 3 to version 1. Revert back.
  4. Switch to npm 9, the first version to use Lock file version 3 format
    • Node 14.21.3
    • npm 9.9.4
  5. Find the CONTRIBUTING.md which mentions "Node 16+"
    • Node 16.20.2
    • npm 9.9.4
  6. Run npm i and see the following errors about mismatched version:
    npm WARN EBADENGINE Unsupported engine {
    npm WARN EBADENGINE   package: 'mocha@11.7.5',
    npm WARN EBADENGINE   required: { node: '^18.18.0 || ^20.9.0 || >=21.1.0' },
    npm WARN EBADENGINE   current: { node: 'v14.21.3', npm: '9.9.4' }
    npm WARN EBADENGINE }
    npm WARN EBADENGINE Unsupported engine {
    npm WARN EBADENGINE   package: 'webpack-cli@7.0.2',
    npm WARN EBADENGINE   required: { node: '>=20.9.0' },
    npm WARN EBADENGINE   current: { node: 'v14.21.3', npm: '9.9.4' }
    npm WARN EBADENGINE }
    npm WARN EBADENGINE Unsupported engine {
    npm WARN EBADENGINE   package: 'commander@14.0.3',
    npm WARN EBADENGINE   required: { node: '>=20' },
    npm WARN EBADENGINE   current: { node: 'v14.21.3', npm: '9.9.4' }
    npm WARN EBADENGINE }
    npm WARN EBADENGINE Unsupported engine {
    npm WARN EBADENGINE   package: 'webpack-merge@6.0.1',
    npm WARN EBADENGINE   required: { node: '>=18.0.0' },
    npm WARN EBADENGINE   current: { node: 'v14.21.3', npm: '9.9.4' }
    
  7. Since commander requires Node >=20, and that is compatible with the other engine errors, update to 20:
    • Node 20.20.2
    • npm 9.9.4
    • Doing an npm i with these versions works without errors and does not mutate the package-lock.json at all.
  8. It is generally best (for security and feature reasons) to stick with the latest LTS (Long Term Support) release of Node and the latest npm version. So I tried that as well:
    • Node 24.15.0
    • npm 11.12.1
    • Doing npm i also works and causes no changes to the package-lock.json
    • Running make js works without any errors or issues.

You can set the devEngines to a less specific value or a range, like "version": "24.x.x" or "version": ">=24", but it's considered a best practice to specify the exact version to ensure everyone working on a project is using the exact version. There can be slight differences that cause issues, even within minor releases. For example, 18.16.0 made a small change to date formatting which caused unit tests around time zones to start breaking, which was then undone in 18.18.0 when caught. This affected my team at work where 3 devs were on Node 18.14.0, 18.16.0, and 18.20.0, and couldn't figure out why one of them kept getting failing tests and the other two didn't. Once everyone was on the same version, weird issues like that go away. This is also true for npm.

TLDR:

  • engines is best being the lowest version the library can work with and accepting a large range (you are already doing this 👍)
  • devEngines is best being the latest version of Node/npm that works when developing the library and it should be an exact version for both (Node/npm) to avoid issues when working on the library.
  • How often you update the devEngines is up to you. Some do it roughly once a year when new LTS versions come out. However, if like me, you use a tool like Proto that switches your Node versions automatically, so you can never be on the wrong version, you may update the Node version often, even on every PR or new release.
    • The only downside to that is potentially annoying people that are still using 2011 era technology like nvm that requires them to manually install and switch Node versions, but they can just switch to better, newer, cross-platform, fully-automated solutions, like Proto.
    • If you want to set up proto for automatic installs I have a short guide here:
    • Setting up Proto as a Volta replacement TheJaredWilcurt/blog#41

Before Merge Checklist

These items can be completed after PR is created.

  • JavaScript implementation
  • Python implementation (NA if HTML beautifier)
  • Added Tests to data file(s)
  • Added command-line option(s) (NA if
  • README.md documents new feature/option(s)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants