mirror of
https://github.com/pnpm/pnpm.git
synced 2026-07-14 01:32:40 -04:00
When deduplicating a missing peer onto the workspace's preferred versions, hoistPeers matched candidates against '*' unless the wanted range was an exact version. In a multi-importer workspace, allPreferredVersions aggregates versions from every importer, so a peer declared as ^1.0.0 inside one importer's closure could be auto-installed as a 2.x resolved for another importer, silently producing a peer graph that mixes incompatible majors. This survived pnpm/pnpm#12847 (which fixed the single-importer case in resolvePeers) and is independent of the pnpm/pnpm#12921 deadlock fix: the wrong version is chosen upstream of peer resolution, during peer hoisting. Both stacks now dedupe onto the highest preferred version that satisfies the wanted range and fall back to resolving the range from the registry when no candidate satisfies it. Non-semver specifiers (workspace:, npm: aliases, dist-tags) keep the dedupe-to-highest behavior. Fixing this exposed a pacquet-only divergence the old behavior masked: resolve_peers indexed a node's children into ParentRefs only by their install alias, so an npm-alias child could never provide a peer under its real package name below the importer level, and the binding fell through to the importer-level hoisted entry. Descent-time children are now indexed by both alias and real name, matching resolvePeers.ts. Measured on the multi-importer reproduction: 46 poisoned snapshots on pnpm 11.11.0, 0 with this change, in both the TypeScript CLI and pacquet. Additionally, hoistPeers now binds the same peer on a re-resolve with an existing lockfile as on a fresh install of the same manifest: root dependencies reused from the lockfile skip full resolution and were invisible to the workspace-root branch of the hoist, so the picked version depended on whether a lockfile was present. The root-dep table falls back to the importer's wanted specifiers, and its entry type is narrowed to HoistableRootDep — the fields hoistPeers reads, matching the Rust port's WorkspaceRootDep. Related to pnpm/pnpm#12847 and pnpm/pnpm#12921. --------- Signed-off-by: C. Spencer Beggs <spencer@beggs.codes> Co-authored-by: Zoltan Kochan <z@kochan.io>