mirror of
https://github.com/pnpm/pnpm.git
synced 2026-06-28 09:55:39 -04:00
## Summary Preserve compatible peer contexts already recorded in the lockfile during a writable re-resolution. A fresh install still resolves peers normally. When a lockfile already records multiple valid peer contexts, pnpm keeps those contexts instead of collapsing them into one compatible context and rewriting unrelated lockfile entries. ## Why [#12075](https://github.com/pnpm/pnpm/pull/12075) fixed optional-peer candidate selection: pnpm no longer discards a compatible optional-peer version merely because it came from the lockfile. This PR addresses a separate source of lockfile churn. A writable install could still replace one valid peer context with another valid peer context even when the existing provider remained present and satisfied the peer range. Public reproduction: <https://github.com/sharmila-oai/pnpm-optional-peer-lockfile-repro> The nested reproduction starts with two valid `vitest@3.2.4` contexts: ```text context-low -> vitest@3.2.4(jsdom@26.1.0) context-high -> vitest@3.2.4(jsdom@27.4.0) ``` Running a writable lockfile regeneration should retain both contexts: ```sh ./reproduce-nested-context.sh ``` ## Behavior pnpm reuses a locked peer provider only when: - The provider is still present in the current dependency graph. - The provider still satisfies the peer range. Current manifest choices remain authoritative. In particular, pnpm does not replace: - A newly added direct peer provider. - An explicitly updated direct peer provider. - A changed nested provider. - A direct provider installed through an alias. The reuse pass runs only when the dependency tree contains locked peer contexts, so fresh installs do not pay for a second peer-resolution pass. ## Tradeoff This change favors lockfile stability over reducing the number of peer contexts. A writable install may retain multiple compatible peer contexts where a fresh install would select one. ## Implementation The resolver performs its normal peer-resolution pass first. When the dependency tree contains locked peer contexts, it performs a second pass that may reuse compatible provider paths from the lockfile while respecting current manifest choices. pacquet now mirrors this behavior. Its lockfile-reuse path rebuilds child dependencies from the package manifest and skips peer dependencies recorded in the snapshot, so the peer pass derives each dependency instance's peer context. --------- Co-authored-by: Zoltan Kochan <z@kochan.io>