fix(lockfile): synthesize npm-alias entries for transitive deps in pnpm lockfiles#403
fix(lockfile): synthesize npm-alias entries for transitive deps in pnpm lockfiles#403
Conversation
…pm lockfiles pnpm encodes aliased transitive deps as `<alias>: <real>@<resolved>` inside a snapshot's dependency map (e.g. `string-width-cjs: string-width@4.2.3`), but the reader was passing the value through unchanged. The linker then keyed the symlink against `<dep_name>@<dep_value>` and produced a broken `string-width-cjs@string-width@4.2.3` path, while the resolver's lockfile reuse path enqueued a transitive with a synthetic `string-width@4.2.3` range that no `string-width-cjs` version could satisfy. Detect the alias shape during parse, rewrite the dep value to the bare resolved version (preserving any peer-context suffix), and feed the same `alias_remaps` channel the importer-level path uses to synthesize an alias-keyed `LockedPackage` with `alias_of=Some(real)`. Repro: https://github.com/stevelandeydescript/aube-bug-repros/tree/main/npm-alias-resolution-failure Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Greptile SummaryThis PR fixes a bug in pnpm lockfile parsing where transitive npm-aliased dependencies encoded as Confidence Score: 5/5Safe to merge — fix is well-scoped, correctly handles peer-suffix and duplicate alias cases, and is backed by three targeted tests. No P0 or P1 issues found. The alias detection logic is sound: No files require special attention. Important Files Changed
Reviews (2): Last reviewed commit: "fix(lockfile): apply npm-alias rewrite t..." | Re-trigger Greptile |
Greptile flagged two follow-ups on PR #403: - The local-packages absorption loop pulls a `file:` workspace package's transitive deps directly out of `raw.snapshots` and bypassed `rewrite_alias_values`, so a workspace package with a npm-aliased transitive (`"string-width-cjs": "npm:string-width@^4.2.0"`) would still produce the broken `string-width-cjs@string-width@4.2.3` virtual store path. Lift the rewrite into a free `rewrite_snapshot_alias_deps` fn and call it from both the local-packages loop and the main snapshots loop. - The `alias_remaps` synthesis error said "npm-alias importer references missing package …" but the channel is now also fed from snapshot transitives where no importer is involved. Drop "importer". Adds a regression test covering an aliased transitive declared by a `file:` local package. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
<alias>: <real>@<resolved>inside a snapshot's dependency map (e.g.string-width-cjs: string-width@4.2.3for the"string-width-cjs": "npm:string-width@^4.2.0"declared by@isaacs/cliui@8.0.2). The reader passed the value through unchanged, so the linker keyed the symlink against<dep_name>@<dep_value>and produced a brokenstring-width-cjs@string-width@4.2.3virtual store path. On a re-resolve, the resolver's lockfile-reuse path enqueued a transitive task with the literalstring-width@4.2.3range that nostring-width-cjsversion could satisfy, surfacing asno version of string-width-cjs matches range \string-width@4.2.3``.alias_remapschannel that the importer path already uses to synthesize an alias-keyedLockedPackagewithalias_of=Some(real). The linker then resolves the alias symlink against the synthetic dir, and the resolver's reuse path matches the synthetic entry by name+version with no malformed range.Test plan
cargo test -p aube-lockfile parse_synthesizes_npm_alias_for_transitive_deps(new)cargo test -p aube-lockfile parse_handles_npm_alias_for_transitive_deps_with_peer_suffix(new)cargo test -p aube-lockfile -p aube-resolver -p aube-linker— full lockfile/resolver/linker suitescargo clippy -p aube-lockfile --all-targets -- -D warningscargo fmt --checkpnpm install→rm pnpm-lock.yaml node_modules→aube install --frozen-lockfile(or fresh resolve) →node -e "require('jackspeak')"succeeds. Before the fix,string-width-cjssymlink dangled; after, it resolves through the syntheticstring-width-cjs@4.2.3entry.🤖 Generated with Claude Code
Note
Medium Risk
Changes pnpm lockfile dependency rewriting and package synthesis logic, which can affect install graph correctness if alias detection is too broad or misses edge cases; coverage is improved with targeted tests.
Overview
Fixes pnpm v9 lockfile parsing for npm-aliased transitive dependencies encoded in
snapshotsas<alias>: <real>@<resolved>(peers…). The parser now rewrites those snapshot dependency values to the bare resolved version (preserving any peer-context suffix) and feeds the existingalias_remapspath so alias-keyedLockedPackageentries are synthesized consistently.This logic is applied both in the main snapshot loop and when absorbing snapshot deps for local (
file:) workspace packages, and includes new unit tests covering plain transitive aliases, peer-suffixed aliases, and local-package transitives; also slightly clarifies the missing-package error message for alias synthesis.Reviewed by Cursor Bugbot for commit df2fe15. Bugbot is set up for automated code reviews on this repo. Configure here.