cli icon indicating copy to clipboard operation
cli copied to clipboard

[BUG] `npm install --production` can delete local workspaces if they've been renamed

Open jenseng opened this issue 4 months ago • 0 comments

Is there an existing issue for this?

  • [x] I have searched the existing issues

This issue exists in the latest npm version

  • [x] I am using the latest npm

Current Behavior

If you rename a local workspace package that's a dev dependency of another local workspace, and add it as a new dev dependency without removing the old one, this can cause lockfile/symlink cruft which can then cause --production installs to fully delete the local workspace directory.

Expected Behavior

Renaming a local workspace package should never cause --production installs to delete local workspaces. Ideally:

  • Obsolete local symlinks should be cleaned up, e.g. if bar gets renamed to baz, then node_modules/bar should get deleted.
  • Related to the above, depending on an obsolete local symlink (e.g. "dependencies": {"bar": "1.0.0"}) should cause an error.
  • Local workspaces should not have "dev": true set, even if they are dev deps of another workspace. This seems to get set when there are 2+ local symlinks for a package, and is likely what causes --production to delete things.

If any of the above are not feasible to implement, then npm ls or similar commands should alert users to these sorts of lockfile / tree inconsistencies.

Steps To Reproduce

Given

package.json

{
  "workspaces": ["workspaces/*"]
}

workspaces/foo/package.json

{
  "name": "foo",
  "version": "1.0.0",
  "private": true
}

workspaces/bar/package.json

{
  "name": "bar",
  "version": "1.0.0",
  "private": true
}

Run

  1. npm install --save-dev bar -w workspaces/foo
  2. Edit workspaces/bar/package.json and change the name to baz
  3. npm install --save-dev baz -w workspaces/foo

Alternatively, just check out this repo and run npm install which will get you to this point.

Observe

  • There are now symlinks at node_modules/bar and node_modules/baz
  • package-lock.json contains:
    "workspaces/bar": {
      "name": "baz",
      "version": "1.0.0",
      "dev": true
    },
    
  • npm ls reports no problems (though it does indicate it was dedup'ed)
  • npm install --production deletes workspaces/bar 💥

Additional Notes

  • This only happens with devDependencies
  • The lexicographical order of the old and new package names seems to matter. I.e. going from bar -> baz causes the issue. Going from baz -> bar does not; you still get two symlinks, but "dev": true won't get set in the lockfile, so a --production install won't delete the workspace.

Environment

  • npm: 11.5.2
  • Node.js: 22.14.0
  • OS Name: Mac OS Sequoia 15.6
  • System Model Name: Macbook Pro
  • npm config:
; copy and paste output from `npm config ls` here

jenseng avatar Aug 19 '25 20:08 jenseng