832

I have a Node.js project that requires Node version 12 or higher. Is there a way to specify this in the packages.json file, so that the installer will automatically check and inform the users if they need to upgrade?

5
  • 3
    A similar way to Adam's response, also using node.version: stackoverflow.com/a/48691987/3032209 Commented Feb 8, 2018 at 18:08
  • 1
    Possible duplicate of How to enforce a specific node.js version to use? Commented Feb 6, 2019 at 15:09
  • Question was already asked here: How to enforce a specific node.js version to use? Commented Feb 6, 2019 at 15:09
  • I wonder if there is any tool that can automatically set this field to an appropriate value by inspecting API usage. Commented Dec 5, 2019 at 5:00
  • NOTE: If using yarn version 2+, you will need to install the yarn engines plugin found here: github.com/devoto13/yarn-plugin-engines Commented Oct 26, 2021 at 16:41

8 Answers 8

1051

You can set the engines field in your package.json and set version requirements for node, npm or both:

 "engines" : { "npm" : ">=8.0.0 <9.0.0", "node" : ">=16.0.0 <17.0.0" } 

As your code definitely won't work with any lower versions, you can enforce this via npm. You need to create an .npmrc file, commit it to the repository, and set the engine-strict option to true, which will cause npm commands such as npm install to fail if the required engine versions to not match:

# .npmrc engine-strict=true 

Without that file, every developer will need to run npm config set engine-strict true in their local workspace to switch on this option.


Original answer (for npm <3.0.0)

You'll want to set the "engineStrict" flag in your package.json:

{ "engineStrict" : true } 

Note that the engineStrict setting in package.json is deprecated: As of npm 3 it will only give a warning. You will have to either configure the .npmrc file as above, or each user will have to run npm config set engine-strict true if they want npm to enforce the required engine versions.

Documentation for the package.json file can be found on the npmjs site

Sign up to request clarification or add additional context in comments.

10 Comments

Remember to cd .. && npm i <folder-name> in order to check for the project itself. However, this will trigger a whole build in it self.
I dont't get a warning on windows when engine-strict=false and { "engineStrict" : true }
npm install respects engine=strict but yarn install ignores it
must be >=12 not >=0.12
how can i set this with yarn
|
368

Add the following to package.json:

"engines": { "npm": ">=8.0.0 <9.0.0", "node": ">=16.0.0 <17.0.0" }, 

Add the following to .npmrc (same directory as package.json):

engine-strict=true 

5 Comments

This is the easiest solution that gives the end user a nice fat error about not having the right version of node when they run npm install; works with yarn as well
This seems to have no effect at all. I set up my package.json with an "engines" section similar to the above (11.13.0 and 6.7.0), and a .npmrc with nothing but content specified above. I had nvm switch me to an older node version, then ran npm install, but it just installs the dependencies and doesn't even mention the engine version mismatch.
Adding engine-strict=true to your .npmrc file only enforces you to use the right engine when you install packages. It does not enforce anything for your end user. If you want your users to use the engines listed under the "engines: {}" property in your package.json when they install it, you should tell them to add engine-strict=true to their .npmrc file.
@chharvey you could add to the package.json the script "preinstall": "echo 'engine-strict=true' >> .npmrc"
engine-strict usage in .npmrc is currently not supported by direnv's .envrc github.com/direnv/direnv/wiki/Node (Found '.nvmrc' with version engine-strict=trueN/A: version "engine-strict=true -> N/A" is not yet installed.
80

Just like said Ibam, engineStrict is now deprecated. But I've found this solution:

check-version.js:

import semver from 'semver'; import { engines } from './package'; const version = engines.node; if (!semver.satisfies(process.version, version)) { console.log(`Required node version ${version} not satisfied with current version ${process.version}.`); process.exit(1); } 

package.json:

{ "name": "my package", "engines": { "node": ">=50.9" // intentionally so big version number }, "scripts": { "requirements-check": "babel-node check-version.js", "postinstall": "npm run requirements-check" } } 

Find out more here: https://medium.com/@adambisek/how-to-check-minimum-required-node-js-version-4a78a8855a0f#.3oslqmig4

.nvmrc

And one more thing. A dotfile '.nvmrc' can be used for requiring specific node version - https://github.com/creationix/nvm#nvmrc

But, it is only respected by npm scripts (and yarn scripts).

3 Comments

This is the best answer in 2019, in light of set engine deprecation and the reality that many are (likely) encountering this due to switching versions with nvm.
This assumes @babel/node is installed.
Great solution! I just replaced babel-node with node, and import with require, and it worked fine for me.
70

.nvmrc

If you are using NVM like this, which you likely should, then you can indicate the nodejs version required for given project in a git-tracked .nvmrc file:

node --version > .nvmrc 

or:

echo v10.15.1 > .nvmrc 

This does not take effect automatically on cd, which is sane: the user must then do a:

nvm use 

and now that version of node will be used for the current shell.

You can list the versions of node that you have with:

nvm list 

.nvmrc is documented at: https://github.com/creationix/nvm/tree/02997b0753f66c9790c6016ed022ed2072c22603#nvmrc

How to automatically select that node version on cd was asked at: Automatically switch to correct version of Node based on project

Tested with NVM 0.33.11.

.nvmrc vs package.json engines

What you might want to do is:

much like package.json vs package-lock.json.

Heroku does respect package.json engines:

Worth mentioning, as documented here, Heroku does play it nice and obey the engines: entry e.g.:

 "engines": { "node": "14.17.0", "npm": "6.14.13" }, 

So you should Always, Always set that to what you are using locally.

This had been previously mentioned on this self deleted answer to this thread.

4 Comments

nvm use doesn't pick up .nvmrc for nvm version 1.1.7
@AakashVerma hmmm, on a quick look nvm only goes to version 0.37.2, and nvmrc is still documented on master: github.com/nvm-sh/nvm/tree/… let me know if you figure it out.
@AakashVerma I'm guessing you're using github.com/coreybutler/nvm-windows/releases As mentioned on their README "The original nvm is a completely separate project for Mac/Linux only. This project uses an entirely different philosophy and is not just a clone of nvm" so it's not surprising. Consider opening a feature request on their tracker.
Seems there's a recent PR waiting for this github.com/coreybutler/nvm-windows/pull/594
18

There's another, simpler way to do this:

  1. npm install Node@8 (saves Node 8 as dependency in package.json)
  2. Your app will run using Node 8 for anyone - even Yarn users!

This works because node is just a package that ships node as its package binary. It just includes as node_module/.bin which means it only makes node available to package scripts. Not main shell.

See discussion on Twitter here: https://twitter.com/housecor/status/962347301456015360

7 Comments

I disagree, this would potentially hide the issue and would sideload a different version of node if it wasn't installed.
Sounds like a great idea to me. Separate node versions for separate projects. Can safely upgrade one without upgrading the others. Only catch is have to run in .bin ./node node-sass rather than just node-sass. Not sure if same for all .bin files.
This is a simple and elegant solution - as long as the team members working on the product know this is happening, I think it's a great answer. We are using this technique at a large company to deal with the variety of Node versions for a dozen web front-end products. Removes the need for constant switching with nvm when going back and forth between products.
This solution has its own pros and cons. Node version encapsulation is potentially its biggest pro. The downside is bloated docker image size if you are going to deploy it this way.
Am I misunderstanding something but, couldn't this be installed as a 'dev' dependency, so npm I -D Node@8, I'm guessing it wouldn't (shouldn't) then be bundled into a Docker image (or executable), which would simply provide it's own NodeJS runtime, right?
|
6

Here's my complete ready-to-use script, based on Adam's answer.

check-version.js :

/* eslint-disable no-console */ const fs = require('fs'); const semver = require('semver'); const childProcess = require('child_process'); // checks that current node and npm versions satisfies requirements in package.json // to run manually: node check-version.js [verbose] const VERBOSE_FORCED = false; const args = process.argv.slice(2); const VERBOSE = VERBOSE_FORCED || (args.length > 0 && args[0] === 'verbose'); const printErrAndExit = (x) => { console.error(x); console.error('Aborting'); process.exit(1); }; const checkNpmVersion = (npmVersionRequired) => { if (!npmVersionRequired) { console.log('No required npm version specified'); return; } const npmVersion = `${childProcess.execSync('npm -v')}`.trim(); if (VERBOSE) console.log(`npm required: '${npmVersionRequired}' - current: '${npmVersion}'`); if (!semver.satisfies(npmVersion, npmVersionRequired)) { printErrAndExit(`Required npm version '${npmVersionRequired}' not satisfied. Current: '${npmVersion}'.`); } }; const checkNodeVersion = (nodeVersionRequired) => { if (!nodeVersionRequired) { console.log('No required node version specified'); return; } const nodeVersion = process.version; if (VERBOSE) console.log(`node required: '${nodeVersionRequired}' - current: '${nodeVersion}'`); if (!semver.satisfies(nodeVersion, nodeVersionRequired)) { printErrAndExit(`Required node version '${nodeVersionRequired}' not satisfied. Current: '${nodeVersion}'.`); } }; const json = JSON.parse(fs.readFileSync('./package.json')); if (!json.engines) printErrAndExit('no engines entry in package json?'); checkNodeVersion(json.engines.node); checkNpmVersion(json.engines.npm); 

It should be placed in the root project directory.

It checks node and/or npm version, as specified in package.json (engines entry), eg

 "engines": { "node": ">=16.0.0 <17.0.0", "npm": ">=8.0.0 <9.0.0" }, 

You can invoke it manually as

node check-version.js [verbose]

or include it as script inside package json, either as standalone script or as a prerequisite for other scripts, eg

"scripts" : { "start": "node check-version.js && vite", "build": "node check-version.js && vite build", "lint": "node check-version.js && eslint .", "check-version": "node check-version.js verbose" }, 

1 Comment

but this cannot prevent user running npm install right?
3
"engines": { "node": ">=20.0.0" } 

Comments

-1

A Mocha test case example:

describe('Check version of node', function () { it('Should test version assert', async function () { var version = process.version; var check = parseFloat(version.substr(1,version.length)) > 12.0; console.log("version: "+version); console.log("check: " +check); assert.equal(check, true); });}); 

13 Comments

Should not be a unit test, use package.json /dotfiles
But whhhhhhhy, a unit test is designed for this >.-
Because you need Node to run a unit test. If the node version present is too outdated, the tests will simply not run or they'll fail with syntax error or smth. similar, which defeats the point of unit testing. It's like hiding a password reset form behind an authorization form. If you can't remember the password, you need to use reset password function, but now you can't use it, because you don't remember the password.
My assumption being there is at least a minimal packages installed. why else enforce a specific one.
As a number of node runtimes grows (deno.js, bun.js, ts-node etc.) this "unit" test becomes even more useless =D And again, OP asked about a fail-safe for an installer. Tests are run after an installation already completed, at best. You have a chicken and an egg problem here.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.