npm update is not updating packages in semver range

What I Wanted to Do

npm update is not updating packages to the latest versions that satisfy the semver ranges. Here’s the output from npm-check-updates:

$ ncu
Checking D:\Users\Jordan\Desktop\test\package.json
[====================] 18/18 100%

 dotenv                            ^7.0.0  →   ^8.0.0
 @commitlint/config-conventional   ^7.5.0  →   ^7.6.0
 eslint-config-prettier            ^4.2.0  →   ^4.3.0
 eslint-plugin-import             ^2.17.2  →  ^2.17.3
 eslint-plugin-node                ^8.0.1  →   ^9.1.0
 husky                             ^2.1.0  →   ^2.3.0
 lint-staged                       ^8.1.5  →   ^8.1.7
 prettier                         ^1.17.0  →  ^1.17.1
 tap                              ^13.0.1  →  ^14.2.0

Run ncu -u to upgrade package.json

Running npm update does nothing and returns no output. Here’s the output from npm update --dd:

$ npm update --dd
npm info it worked if it ends with ok
npm verb cli [ 'C:\\Users\\Jordan\\scoop\\apps\\nodejs-lts\\current\\node.exe',
npm verb cli   'C:\\Users\\Jordan\\scoop\\apps\\nodejs-lts\\current\\bin\\node_modules\\npm\\bin\\npm-cli.js',
npm verb cli   'update',
npm verb cli   '--dd' ]
npm info using npm@6.9.0
npm info using node@v10.15.3
npm verb npm-session b7ba5dbe1648ece8
npm verb update computing outdated modules to update
npm http fetch GET 304 https://registry.npmjs.org/scryptsy 265ms (from cache)
npm http fetch GET 304 https://registry.npmjs.org/nedb-promises 276ms (from cache)
npm http fetch GET 304 https://registry.npmjs.org/inquirer 329ms (from cache)
npm http fetch GET 304 https://registry.npmjs.org/dotenv 369ms (from cache)
npm http fetch GET 304 https://registry.npmjs.org/husky 371ms (from cache)
npm http fetch GET 304 https://registry.npmjs.org/cross-env 381ms (from cache)
npm http fetch GET 304 https://registry.npmjs.org/npm-run-all 377ms (from cache)
npm http fetch GET 304 https://registry.npmjs.org/lint-staged 381ms (from cache)
npm http fetch GET 304 https://registry.npmjs.org/eslint-plugin-node 391ms (from cache)
npm http fetch GET 304 https://registry.npmjs.org/eslint-config-prettier 398ms (from cache)
npm http fetch GET 304 https://registry.npmjs.org/eslint-plugin-promise 399ms (from cache)
npm http fetch GET 304 https://registry.npmjs.org/eslint-plugin-import 402ms (from cache)
npm http fetch GET 304 https://registry.npmjs.org/prettier 407ms (from cache)
npm http fetch GET 304 https://registry.npmjs.org/sinon 421ms (from cache)
npm http fetch GET 304 https://registry.npmjs.org/tap 431ms (from cache)
npm http fetch GET 304 https://registry.npmjs.org/@commitlint%2fconfig-conventional 452ms (from cache)
npm http fetch GET 304 https://registry.npmjs.org/@commitlint%2fcli 457ms (from cache)
npm http fetch GET 304 https://registry.npmjs.org/eslint 456ms (from cache)
npm verb outdated not updating dotenv because it's currently at the maximum version that matches its specified semver range
npm verb outdated not updating eslint-plugin-node because it's currently at the maximum version that matches its specified semver range
npm verb outdated not updating tap because it's currently at the maximum version that matches its specified semver range
npm verb exit [ 0, true ]
npm timing npm Completed in 2311ms
npm info ok

However npm-check-updates still shows the old versions.

$ ncu
Checking D:\Users\Jordan\Desktop\test\package.json
[====================] 18/18 100%

 dotenv                            ^7.0.0  →   ^8.0.0
 @commitlint/config-conventional   ^7.5.0  →   ^7.6.0
 eslint-config-prettier            ^4.2.0  →   ^4.3.0
 eslint-plugin-import             ^2.17.2  →  ^2.17.3
 eslint-plugin-node                ^8.0.1  →   ^9.1.0
 husky                             ^2.1.0  →   ^2.3.0
 lint-staged                       ^8.1.5  →   ^8.1.7
 prettier                         ^1.17.0  →  ^1.17.1
 tap                              ^13.0.1  →  ^14.2.0

Run ncu -u to upgrade package.json

Platform Info

$ npm --versions
{ 'dupe-finder': '0.1.0',
  npm: '6.9.0',
  ares: '1.15.0',
  cldr: '33.1',
  http_parser: '2.8.0',
  icu: '62.1',
  modules: '64',
  napi: '3',
  nghttp2: '1.34.0',
  node: '10.15.3',
  openssl: '1.1.0j',
  tz: '2018e',
  unicode: '11.0',
  uv: '1.23.2',
  v8: '6.8.275.32-node.51',
  zlib: '1.2.11' }

$ node -p process.platform
win32

I am not sure if you have are seeing anything unexpected as not sure what semver ranges you have.

npm-check-updates is a tool which will optionally update your package.json to specify the latest versions of packages. Running npm update will not apply all the updates listed by npm-check-updates unless you had npm-check-updates actually update the package.json with new server ranges.

To understand just the npm behaviour, what does npm outdated show and what are the dependencies in your package.json?

After some testing, I think I realize what’s happening here. npm update compares the versions of packages that are in node_modules with the versions in the registry based on the semver ranges in package.json. I was expecting it to just compare the versions in package.json with the versions in the registry like npm-check-updates does.

For example, the latest version of prettier is 1.17.1, but my package.json has ^1.17.0. If just run npm install in a freshly cloned repository with no package-lock.json, then npm will install prettier@1.17.1. In this case, npm update will not do anything because node_modules already has the latest version. It also won’t update package.json.

However, if I have a package-lock.json that is locking prettier@1.17.0, then npm install will install prettier@1.17.0, and running npm update will bump prettier to 1.17.1 and update package.json.

So, I guess my question is how do I get npm update to bump all versions in package.json regardless of what’s in node_modules, while still respecting semver ranges? I.e. how do I get npm update to bump prettier to 1.17.1 in package.json, even if I already have 1.17.1 in node_modules, without having to manually specify each package?

The “normal” workflow for working in cloned package is that an install gives you what is in the package-lock.json. When there are newer versions npm update will update both package.json and package-lock.json.

So, I guess my question is how do I get npm update to bump all versions in package.json regardless of what’s in node_modules , while still respecting semver ranges?

I don’t know that you can using npm update, but isn’t that what npm-check-updates can do?
Based on reading (not trying) I think you want:

ncu -u
npm install

Unfortunately, npm-check-updates does not have a mode that respects the semver ranges in package.json. You can specify the --semverLevel option, but that only allows you to choose major or minor. The --semverLevel minor option will work for most of my projects since I usually use caret ranges, but there are edge cases when I use a tilde or some other semver range. Plus it’s cumbersome to type --semverLevel minor as there is no abbreviation for that option. I suppose I could add an npm script to do this though.

Thanks for the help. Maybe there’s another tool that can replace the functionality of npm-check-updates. It’s too bad this isn’t baked into the npm CLI yet.

Oh, and regarding package-lock.json, I was copying the dependencies section of package.json from one project to the dependencies section of another project. Some of the packages from the source project did not yet exist in the destination project, so after running npm install the package-lock.json of the destination project had newer locked versions than the package-lock.json in the source project.

For example, the source project had prettier@^1.17.0 in package.json and prettier@1.17.0 in package-lock.json. After manually copying the dependencies over and running npm install, the destination project had prettier@^1.17.0 in package.json but prettier@1.17.1 in its package-lock.json. So running npm update had no effect on node_modules or package.json, which is non-intuitive behavior in my opinion.

(Thanks for bonus message explaining why you didn’t have package-lock.json, I was wondering.)