npm removes packages on install

What I Wanted to Do

Install packageA without saving to package.json (–no-save)
Install packageB
Have both packages are installed in my node_modules directory

What Happened Instead

packageA gets removed when packageB gets installed

Reproduction Steps

npm install gulp --no-save
Observe that “gulp” exists in node_modules

npm install gulp-sass
Observe that “gulp” has been removed from node_modules

Note: The packages used for reproduction are arbitrary. Any two packages will work as long as packageA is not a dependency of packageB.


I realize this functionality might be intended but I don’t think it should be. There are many scenarios where someone may want to install a package without saving it to package.json. For example when using “npm link” or installing local packages. If a package has been installed and is not present in package.json and npm install is run the package will get removed.

Platform Info

This issue is present using the latest version of npm v6.10.3 as well as with previous versions after v5. This has been reproduced on both Windows 10 and OSX

$ npm --versions
{ test: '1.0.0',
  npm: '6.10.3',
  ares: '1.15.0',
  brotli: '1.0.7',
  cldr: '35.1',
  http_parser: '2.8.0',
  icu: '64.2',
  modules: '64',
  napi: '4',
  nghttp2: '1.34.0',
  node: '10.16.0',
  openssl: '1.1.1b',
  tz: '2019a',
  unicode: '12.1',
  uv: '1.28.0',
  v8: '',
  zlib: '1.2.11' }

$ node -p process.platform

I found a relevant paragraph on:

The --no-package-lock argument will prevent npm from creating a package-lock.json file. When running with package-lock’s disabled npm will not automatically prune your node modules when installing.

I realize this functionality might be intended but I don’t think it should be.

I agree it is as intended. You could optionally suggest changing the behaviour through #ideas, or ask about specific scenarios though #support for approaches, but this #bugs message will be seen too.

Why is this intended behaviour? An install command shouldn’t uninstall packages, especially when they’re completely unrelated to the package being installed.

This is extremely surprising behaviour and makes it basically impossible to have genuinely optional packages.

1 Like

My understanding is that once you have a package-lock.json, keeping that and node_modules in sync by default is more useful than convenience of preserving unsaved changes to node_modules. You can opt-out of the behaviour by using --no-package-lock.

package-lock.json is automatically generated for any operations where npm modifies either the node_modules tree, or package.json . It describes the exact tree that was generated, such that subsequent installs are able to generate identical trees, regardless of intermediate dependency updates.

(I think at one time the package-lock was updated to match the node_modules rather than the other way around, but that caused worse problems.)

Ok, so npm can’t tell the difference between a package that was removed from dependencies in package.json, and one that was previously installed with --no-save. However, that should really only be relevant when doing a plain npm install (i.e. no specific packages requested).

If I explicitly request a single package to be installed, why does npm need to look at anything other than making sure that package is installed correctly?

This seems all the more pertinent when dealing with --no-save installs; npm isn’t updating package.json or package-lock.json in that instance, so it shouldn’t need to sync at all.