Repository navigation
Conversation
…fying it An optional peer that is only reachable through its peerOptional edge is pruned as extraneous. When it was nested to avoid a conflict, pruning it leaves the edge resolving to a node further up that does not satisfy it.
jeffkl
approved these changes
Oct 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #10086. Related to #9249 (same prune of extraneous optional peers, different outcome).
calcDepFlagsleaves a node extraneous when it is only reachable through a peerOptional edge, so#idealTreePruneremoves it after a lockfile-based install that changed the tree. That is fine when the edge just ends up missing. But when the pruned copy was nested to avoid a conflict, the edge falls through to a node further up that does not satisfy it, and the saved lockfile has an invalid edge (npm lsfails with ELSPROBLEMS, npm 10npm cirefuses the lockfile).Now such a node is no longer marked extraneous when the node it shadows (the same name resolved from above its parent) exists and does not satisfy the edge. An optional peer that only fills an empty slot, or shadows a version that also satisfies the edge, is still pruned as before.
Tests: a unit test in
test/calc-dep-flags.jscovers both cases and fails without the change. Thenest when peerOptional conflictssnapshot inbuild-ideal-treeloses one"extraneous": trueline: that test builds exactly this layout, and the nested optional peer it asserts on was flagged for removal on the next install.End to end with the repro from the issue: after
npm install --save-exact jsdom@30.0.1the lockfile keeps the nested@noble/hashes@2.3.0copies,npm lsreports nothing invalid, and both this branch'snpm ciand npm 10npm ciaccept it. The arborist suite passes with 100% coverage, and so do theinstall,ciandlscommand tests.