[BUG] `npm ci` does not remove/recreate existing `node_modules` inside workspaces
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
Running npm ci behaves unintuitively in workspaces. When run in a project without workspaces, the behavior is straightforwards and clear:
- Validate that the
package-lock.jsonis in sync - Remove
./node_modulesif it exists - Install from
package-lock.json
This makes perfect sense, and aligns with the documentation. However, when you run npm ci in a project with workspaces, it runs the same three steps. This sounds like it's fine, except that there is one key problem with step 2: it only deletes the root node_modules folder. Any node_modules in workspaces folders are left behind. This leads to a confusing inconsistency in the behavior of the npm ci command: on first install (if there are no node_modules installed at all), it will install the full tree, including workspaces. However, when running it a second time, it only cleanly reinstalls the root packages.
Expected Behavior
I would expect that npm ci would remove all the node_modules folders referenced in package-lock.json before trying to install again.
Steps To Reproduce
Here is a set of commands that illustrate the issue. If you run this script you will see that the hoisted abbrev installation gets properly reinstalled (removing foo.txt), but the inner one does not.
npm init -y
npm init -y -w workspace-a
npm init -y -w workspace-b
npm i --save-exact [email protected] -w workspace-a
npm i --save-exact [email protected] -w workspace-b
touch node_modules/abbrev/foo.txt
touch workspace-b/node_modules/abbrev/bar.txt
npm ci
# This will _not_ include foo.txt, as it has been deleted.
ls node_modules/abbrev
# This _will_ include bar.txt, as it has _not_ been deleted.
ls workspace-b/node_modules/abbrev
Environment
- npm: 10.4.0 and 9.8.1
- Node.js: v18.18.0
- OS Name: macOS
- System Model Name: Macbook Pro
- npm config:
save-exact = true
save-prefix = ""
This is a possible duplicate of #4846, but that one was a slightly different situation (specifying a workspace to reinstall, rather than running npm ci bare), and also was closed almost 2 years ago, so I figured it would be better to open this as a new issue.
Hi, I would like to contribute.
The ci stands for "clean install" - the behavior should live up to the name. If someone needs similar behavior to work cross-platform, you can use rimraf --glob ./**/node_modules.