Skip to content

fix: Exclude peer dependencies from workspace external dep resolution#12050

Merged
anthonyshew merged 1 commit into
mainfrom
shew/fix-issue-10985
Feb 28, 2026
Merged

fix: Exclude peer dependencies from workspace external dep resolution#12050
anthonyshew merged 1 commit into
mainfrom
shew/fix-issue-10985

Conversation

@anthonyshew
Copy link
Copy Markdown
Contributor

@anthonyshew anthonyshew commented Feb 27, 2026

Summary

Fixes turbo prune producing 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@14 and next@15), npm nests one under the app's node_modules/ while hoisting the other. After turbo prune removes 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 ci rejects this as inconsistent.

Fix

NpmLockfile::subgraph now runs a rehoist_packages pass after building the pruned package set. It detects packages nested under a workspace's node_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/react v18 genuinely needed by web'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 PASS
  • pnpm-peer-dep: all 3 PASS
  • npm-lock → web: @types/react stays at v18.0.17 (no incorrect rehoisting)

@anthonyshew anthonyshew requested a review from a team as a code owner February 27, 2026 22:32
@anthonyshew anthonyshew requested review from tknickman and removed request for a team February 27, 2026 22:32
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Feb 27, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
examples-basic-web Ready Ready Preview, Comment, Open in v0 Feb 28, 2026 4:22am
examples-designsystem-docs Ready Ready Preview, Comment, Open in v0 Feb 28, 2026 4:22am
examples-gatsby-web Ready Ready Preview, Comment, Open in v0 Feb 28, 2026 4:22am
examples-kitchensink-blog Ready Ready Preview, Comment, Open in v0 Feb 28, 2026 4:22am
examples-nonmonorepo Ready Ready Preview, Comment, Open in v0 Feb 28, 2026 4:22am
examples-svelte-web Ready Ready Preview, Comment, Open in v0 Feb 28, 2026 4:22am
examples-tailwind-web Ready Ready Preview, Comment, Open in v0 Feb 28, 2026 4:22am
examples-vite-web Ready Ready Preview, Comment, Open in v0 Feb 28, 2026 4:22am
turbo-site Ready Ready Preview, Comment, Open in v0 Feb 28, 2026 4:22am
turborepo-agents Ready Ready Preview, Comment, Open in v0 Feb 28, 2026 4:22am
turborepo-test-coverage Ready Ready Preview, Comment, Open in v0 Feb 28, 2026 4:22am

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 27, 2026

Coverage Report

Metric Coverage
Lines 81.34%
Functions 53.14%
Branches 0.00%

View full report

Comment thread crates/turborepo-lockfiles/src/npm.rs Outdated
Comment thread crates/turborepo-lockfiles/src/npm.rs
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
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Local package peer dependencies cause install errors after prune

1 participant