[BUG] `install` with `--install-links` and local path dependencies "dedups" packages even when the local path is different
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 10.1k
- Forks
- 4.7k
- Avg merge
- 2d 2h
- Merged PRs (30d)
- 19
Description
Is there an existing issue for this?
- I have searched the existing issues
This issue exists in the latest npm version
- I am using the latest npm
Current Behavior
Using a local registry, where all package versions use local paths, and installing with --install-links, packages that are referenced multiple times get deduped even though they are different versions (in this case, have a different local path).
Consider package copy-props 2.0.5.
This package has the following truncated dependency tree, where there are two versions of is-plain-object used, 2.0.4 and 5.0.0:
copy-props (2.0.5)
├── each-props (1.3.2)
│ ├── is-plain-object (2.0.4)
│ └── ...
└── is-plain-object (5.0.0)
Normally this has the following tree when installed via the NPM registry, where you can see both version 2.0.4 and 5.0.0 of is-plain-object present in node_modules:
.
├── node_modules
│ ├── array-each
│ │ └── ...
│ ├── array-slice
│ │ └── ...
│ ├── copy-props
│ │ └── ...
│ ├── each-props
│ │ ├── node_modules
│ │ │ └── is-plain-object <------------- 2.0.4
│ │ │ └── ...
│ │ └── ...
│ ├── for-in
│ │ └── ...
│ ├── for-own
│ │ └── ...
│ ├── isobject
│ │ └── ...
│ ├── is-plain-object <------------------- 5.0.0
│ │ └── ...
│ └── object.defaults
│ └── ...
└── ...
However, if you create your own local registry with all of these packages available, and use local paths to define the package version in the list of dependencies in package.json, and if you use --install-links option, the resulting node_modules structure is as follows, where it's missing version 2.0.4 of is-plain-object:
.
├── node_modules
│ ├── array-each
│ │ └── ...
│ ├── array-slice
│ │ └── ...
│ ├── copy-props
│ │ └── ...
│ ├── each-props <- ⚠️ no inner node_modules, no is-plain-object 2.0.4
│ │ └── ...
│ ├── for-in
│ │ └── ...
│ ├── for-own
│ │ └── ...
│ ├── isobject
│ │ └── ...
│ ├── is-plain-object <-------------------------- 5.0.0
│ │ └── ...
│ └── object.defaults
│ └── ...
└── ...
In the above example, here is an excerpt from various package.json files in the local registry:
local-repo/copy-props/package.json:
{
"dependencies": {
"each-props": "/path/to/local-repo/each-props",
"is-plain-object": "/path/to/local-repo/is-plain-object@5"
}
}
local-repo/each-props/package.json:
{
"dependencies": {
"is-plain-object": "/path/to/local-repo/is-plain-object@2",
"object.defaults": "/path/to/local-repo/object.defaults"
}
}
If the user does NOT use --install-links, both versions 2.0.4 and 5.0.0 are installed.
Expected Behavior
Regardless if using --install-links or not, when using npm install with a local registry using local paths, the output node_modules files structure should be the same (except with installed files instead of symlinks).
Steps To Reproduce
- Download, unzip, and chdir into test.zip
- Delete
broken/node_modules/andbroken/package-lock.json - In all of the 11
broken/**/package.jsonfiles (includingbroken/package.json) replace all occurances ofC:/Users/Dan/test/brokenwith$(pwd)/broken - cd into
brokenand runnpm --install-links install - Navigate to the newly created
node_modulesand notice the missingis-plain-objectversion2.0.4
Environment
- npm: 11.3.0
- Node.js: v24.2.0
- OS Name: Windows 10
- System Model Name:
- npm config:
; node bin location = C:\Program Files\nodejs\node.exe
; node version = v24.2.0
; npm local prefix = C:\Users\Dan\test\broken
; npm version = 11.3.0
; cwd = C:\Users\Dan\test\broken
; HOME = C:\Users\Dan
This also happens on Guix System (Linux) with NPM 10.9.2
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the package.json dependency paths and reproduce the install using the provided test.zip and npm --install-links install. Compare the node_modules trees produced with and without --install-links, focusing on the missing is-plain-object 2.0.4 dependency. Done means both modes retain dependencies from distinct local paths while installing linked files rather than symlinks.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, nodejs
- Domain
- cli, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100