fix(arborist): validate peerOptional conflicts in no-save mutations#9605
Merged
owlstronaut merged 1 commit intoJun 24, 2026
Merged
Conversation
d013a56 to
566b700
Compare
566b700 to
fd519a0
Compare
fd519a0 to
0354ba9
Compare
owlstronaut
approved these changes
Jun 24, 2026
owlstronaut
approved these changes
Jun 24, 2026
Contributor
|
This usually means the cherry-pick had conflicts. Please create a manual backport: git fetch origin release/v11
git checkout -b backport/v11/9605 origin/release/v11
git cherry-pick -x 2aa1c7cd25399c49f12aab684341cc1743ad1635
# resolve any conflicts, then:
git push origin backport/v11/9605Error details |
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.
Problem
Arborist currently treats
save=falseas a signal that invalidpeerOptionaledges from the lockfile can be trusted and skipped. That is correct for non-mutating lockfile reads such asnpm ci, but it is wrong for commands that still mutate the ideal tree and write a new lockfile while leavingpackage.jsonunchanged.For example,
npm updateruns withsave=false, but it can still update package versions inpackage-lock.json. If one of those updates introduces or exposes an invalid optional peer relationship, Arborist can finish successfully and write a lockfile that a later plainnpm installrejects withERESOLVE.Fix
This separates two concepts that were previously conflated:
#requestedTreeMutation: whether the command explicitly requested an add, remove, or update operation.#mutateTree: whether the ideal-tree build has actually changed the tree during placement.Invalid
peerOptionalpolicy now uses#requestedTreeMutationinstead of#mutateTree:save=false+ explicit add/rm/update: invalidpeerOptionaledges are treated as resolver problems.save=false+ no requested mutation: invalidpeerOptionaledges remain trusted, preserving the existingnpm ci-style behavior.The requeue path also uses
#requestedTreeMutation, so asave=falseupdate can reprocess an already-seen node when a later placement invalidates one of itspeerOptionaledges.Behavior Changed
This fixes bad lockfile output for
save=falsecommands that mutate the dependency graph, including:npm updatenpm install <pkg> --no-savesave=falseThose commands should not silently produce an ideal tree or lockfile containing invalid
peerOptionaledges that a subsequent install rejects.Behavior Preserved
Non-mutating
save=falsebuilds continue to trust the lockfile. In those cases, an invalidpeerOptionaledge is still ignored rather than forcing re-resolution, preserving the behavior needed bynpm ci-style lockfile reads.Fixes #9604.
Tests
Added regression coverage for:
npm update-stylesave=falsemutation rejecting an invalidpeerOptionalgraph instead of writing a bad lockfile.npm install <pkg> --no-save-style mutation rejecting the same class of invalid graph.save=falseupdate requeueing an already-seen node when later placement invalidates itspeerOptionaledge.save=falsebuilds continuing to ignore invalidpeerOptionalproblem edges.Validated with: