fix(npm): peerDependency resolution leading to multiple versions being installed + hanging#32358
Merged
fix(npm): peerDependency resolution leading to multiple versions being installed + hanging#32358
Conversation
…ency installations We had a bunch of issues where we were either repeatedly re-resolving the same dependencies over and over again when cyclic peer dependencies were present, or growing module ids unboundly which also caused hangs. This was all caused by using a 1 pass algorithm prior to this change, whereas proper peer dependency resolution requires a 2 pass algorithm. The reason for that is that peer dependency resolution is based around the "peer context" that is passed around and can influence which version a package is resolved to. We need to be super careful when those should bubble up to parents. Pure dependencies without any peers should be resolved from a local cache, then check if we resolved a dep with the same peer context and lastly do the expensive thing of checking directly.
798261b to
26682ba
Compare
dsherret
reviewed
Mar 2, 2026
Verify that the resolved peer dep actually points to package-peer@1.0.0 and that package-c's package-b dependency references a package-a with the peer resolved, confirming propagation through the correct scope. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
dsherret
reviewed
Mar 2, 2026
dsherret
approved these changes
Mar 2, 2026
Member
dsherret
left a comment
There was a problem hiding this comment.
Amazing work!! This is going to be so much better.
Thanks a lot for looking into this.
littledivy
pushed a commit
to littledivy/deno
that referenced
this pull request
Mar 2, 2026
…g installed + hanging (denoland#32358) Essentially, this PR rewrites our dependency resolution code to use a two-phase approach, which fixes duplicate peer dependencies or hanging installation. Peer deps were resolved inline during BFS traversal, making results dependent on traversal order. If package lib (which peers with `react`) was encountered before `react` was added to the graph by a sibling, it would auto-resolve `react@18.2.0 (latest)`. When encountered again in a subtree where `react@18.3.0` already existed, it got a different resolution, which creates a duplicate lib entry. With patterns like Expo/React Native (100+ plugins all peering with `react`, `react-native`, and `expo`), this caused exponential blowup.
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.
There are still weekly reports of Fresh users having errors because of duplicated
preactor@preact/signalspackages. So I spend the day hunting down various peer dependency installation issues.Essentially, this PR rewrites our dependency resolution code to use a two-phase approach, which fixes duplicate peer dependencies or hanging installation.
Peer deps were resolved inline during BFS traversal, making results dependent on traversal order. If package lib (which peers with
react) was encountered beforereactwas added to the graph by a sibling, it would auto-resolvereact@18.2.0 (latest). When encountered again in a subtree wherereact@18.3.0already existed, it got a different resolution, which creates a duplicate lib entry. With patterns like Expo/React Native (100+ plugins all peering withreact,react-native, andexpo), this caused exponential blowup.This issue affects practically every frontend project as the framework is always a peerDependency (
react/preact/vue/etc) and most of them have some form of singletons which only work when one copy of the framework is installed.Fixes #31179
Fixes #21394