fix: Exclude peer dependencies from workspace external dep resolution#12050
Merged
Conversation
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
Coverage Report
|
87b18ad to
f9048da
Compare
f9048da to
a454c8d
Compare
a454c8d to
909035f
Compare
909035f to
58ddac9
Compare
58ddac9 to
ee56eab
Compare
After pruning, a package may be nested under a workspace's node_modules (e.g. apps/web/node_modules/next@15) because the original tree had a different version hoisted for another workspace. When that other workspace is pruned away the nested version should be promoted to the hoisted position so npm ci sees a consistent tree. NpmLockfile::subgraph now runs a rehoist_packages pass that detects workspace-nested packages whose hoisted counterpart was not explicitly requested by any workspace's transitive closure. These are promoted to the hoisted slot with their sub-dependencies relocated. Closes #10985
ee56eab to
7e93091
Compare
1 task
github-actions Bot
added a commit
that referenced
this pull request
Feb 28, 2026
## Release v2.8.13-canary.7 Versioned docs: https://v2-8-13-canary-7.turborepo.dev ### Changes - release(turborepo): 2.8.13-canary.6 (#12055) (`4f190a8`) - test: Port 14 more prysk tests to Rust (single-package + commands) (#12054) (`a967dc0`) - test: Port 13 more prysk tests to Rust (#12057) (`f484a47`) - fix: Exclude peer dependencies from workspace external dep resolution (#12050) (`3a75547`) - test: Port all 15 workspace-configs prysk tests to Rust (#12058) (`55442be`) --------- Co-authored-by: Turbobot <turbobot@vercel.com>
github-actions Bot
added a commit
that referenced
this pull request
Feb 28, 2026
## Release v2.8.13-canary.8 Versioned docs: https://v2-8-13-canary-8.turborepo.dev ### Changes - fix: Exclude peer dependencies from workspace external dep resolution (#12050) (`3a75547`) - test: Port all 15 workspace-configs prysk tests to Rust (#12058) (`55442be`) - release(turborepo): 2.8.13-canary.7 (#12060) (`495afdc`) - perf: Stream file contents during hashing to lower memory usage (#12059) (`f03cdce`) - fix: Treat `npm: alias` dependencies as external, not workspace references (#12061) (`b179cb8`) - test: Port 18 more prysk tests to Rust (other/ + lockfile-aware-caching/) (#12062) (`7887af2`) --------- Co-authored-by: Turbobot <turbobot@vercel.com>
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.
Summary
Fixes
turbo pruneproducing invalid npm lockfiles when workspace packages have peer dependencies satisfied by different versions across apps.Closes #10985
Problem
When two apps depend on different versions of a package (e.g.
next@14andnext@15), npm nests one under the app'snode_modules/while hoisting the other. Afterturbo pruneremoves the app that needed the hoisted version, the pruned lockfile still has the old tree structure — a nested version that should now be hoisted.npm cirejects this as inconsistent.Fix
NpmLockfile::subgraphnow runs arehoist_packagespass after building the pruned package set. It detects packages nested under a workspace'snode_modules/whose hoisted counterpart was not explicitly requested by any workspace's transitive closure, and promotes them to the hoisted position with sub-dependencies relocated.The condition
!requested.contains(hoisted_key)is what distinguishes the issue-10985 case (hoisted v14 pulled in only by a peer dep from the wrong path context — not in any workspace's real transitive closure) from the npm-lock case (hoisted@types/reactv18 genuinely needed byweb's transitive closure).One file changed:
crates/turborepo-lockfiles/src/npm.rs(+86 lines).Testing
Verified locally against all relevant fixtures:
issue-10985: all 3 targets PASS (app-one, app-two, @repo/components)npm-peer-dep: all 3 PASSpnpm-peer-dep: all 3 PASSnpm-lock → web:@types/reactstays at v18.0.17 (no incorrect rehoisting)