Skip to content

fix(pnpm): merge colliding peer-suffixed v9 snapshot keys#1724

Draft
zlav wants to merge 1 commit into
masterfrom
fix-pnpm-v9-peer-collision-devdeps
Draft

fix(pnpm): merge colliding peer-suffixed v9 snapshot keys#1724
zlav wants to merge 1 commit into
masterfrom
fix-pnpm-v9-peer-collision-devdeps

Conversation

@zlav

@zlav zlav commented Jun 24, 2026

Copy link
Copy Markdown
Member

Merge (instead of last-wins overwrite) pnpm v9 snapshot entries whose peer-suffixed keys collapse to the same key, so dev-only transitives stop leaking as production.

Overview

  • In pnpm v9, snapshot keys carry a peer-dep suffix that's stripped for lookup against the (unsuffixed) packages section.
  • A package installed under multiple peer variants produces several snapshot keys that collapse to the same stripped key. The old HashMap.mapKeys withoutPeerDepSuffix collapse was last-wins, silently dropping all but one variant's dependency list.
  • Edges living only on a dropped variant vanished, so dev-only transitives got no dev path → empty env → reported as production. Densely-shared "core" packages collide (leak); single-variant leaf plugins don't (filter correctly) — matching the 10-leak/9-filter asymmetry in the ticket.
  • Fix: collapse with HashMap.fromListWith (<>), unioning colliding entries' dependency lists. Only the v9 snapshots decoder changes; v5/v6/v7 paths untouched.

Acceptance criteria

  • A devDependency reachable only via a dropped peer variant (and its transitives) is classified EnvDevelopment / filtered, not production.

Testing plan

  • New fixture test/Pnpm/testdata/pnpm-9-peer-collision/pnpm-lock.yaml: a dev dep under two peer-suffixed snapshot keys with distinct children.
  • cabal test unit-tests --match "Pnpm.PnpmLock" → 26/26 pass.
  • Confirmed the new spec fails without the fix (child leaks with empty env, edge missing) and passes with it. v6 transitive-peer spec still green.

Confidence / for reviewer

  • Medium (~60%). Root cause inferred from code, not yet reproduced against the affected project's real pnpm-lock.yaml. Reviewer should confirm against the actual lockfile (the leaking edge could be a genuine prod path rather than a collision) and watch for v6/v7 regressions.

Why not to merge this

  • Symptom vs source — merge restores edges, but if a package legitimately appears under a prod path anywhere in the monorepo, prod-wins would still (correctly) mark it prod; this PR can't distinguish that without the real lockfile.
  • Unreproduced — no repro against the affected project's actual lockfile; the exact dropped edge is inferred, so the fix may be necessary-but-insufficient (prior fix [ANE-2852] pnpm v9 lockfile and dev deps #1668 also looked complete and wasn't).
  • List union, not set(<>) concatenates dep-pair lists, so duplicate (name, rev) pairs across variants are kept; harmless (edges are idempotent) but slightly larger intermediate lists.
  • Secondary factor untouched — the investigation flagged v9 peerDependencies ranges in deepDependencies as a possible spurious prod→dev bridge; deliberately left out to keep this surgical.
  • Counterweight: low blast radius, sits squarely on the most likely root cause, and ships with a regression test that demonstrably fails without the change.

References

In pnpm v9, snapshot keys carry a peer-dependency suffix that is stripped
so they can be looked up against the (unsuffixed) packages section. A
package installed under multiple peer variants yields several snapshot
keys that collapse to the same stripped key; the previous `HashMap.mapKeys`
collapse was last-wins and silently dropped all but one variant's
dependency list. Edges living only on a dropped variant disappeared, so
dev-only transitives lost their dev path and were reported as production.

Collapse with `HashMap.fromListWith (<>)` instead, unioning the colliding
entries' dependency lists so no edges are lost. v5/v6/v7 paths are
unaffected (only the v9 snapshots decoder changes).

Adds a v9 regression fixture + spec where a dev dependency is present
under two peer-suffixed snapshot keys with distinct children.

Refs: ANE-2852

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.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.

1 participant