Files
pnpm/installing
Sharmila 122ab0a1ed fix(deps-resolver): preserve locked optional peer candidates (#12075)
## Summary

Preserve compatible optional-peer versions already recorded in the lockfile
when pnpm re-resolves a workspace.

## Reproduction

Public reproduction:
<https://github.com/sharmila-oai/pnpm-optional-peer-lockfile-repro>

The workspace contains:

```text
packages/uses-vitest -> vitest@3.2.4
packages/older       -> jsdom@26.1.0
```

Vitest declares `jsdom` as an optional peer dependency.

The committed lockfile was generated when another workspace package also
depended on `jsdom@27.4.0`. At that time, pnpm selected the higher compatible
version for Vitest:

```text
vitest@3.2.4(jsdom@27.4.0)
```

That additional direct dependency was then removed from its `package.json`,
without regenerating the lockfile. This simulates a normal manifest edit.

Running:

```sh
pnpm install --lockfile-only --no-frozen-lockfile
```

unnecessarily rewrites Vitest's still-valid optional-peer context:

```diff
-        version: 3.2.4(jsdom@27.4.0)
+        version: 3.2.4(jsdom@26.1.0)
```

Both versions satisfy Vitest's optional peer range. The existing `27.4.0`
resolution remains valid and should not be discarded while pnpm updates the
lockfile.

## Cause

Preferred versions loaded from the wanted lockfile are stored as weighted
selectors:

```ts
{ selectorType: 'version', weight: EXISTING_VERSION_SELECTOR_WEIGHT }
```

`getHoistableOptionalPeers()` only recognized the plain string form:

```ts
specType === 'version'
```

As a result, it ignored the compatible locked `27.4.0` candidate. It only saw
`26.1.0`, which was rediscovered from `packages/older/package.json`, and
rewrote the peer context.

## Fix

Normalize the selector before checking its type, matching the handling already
used by `hoistPeers()` for required peers:

```ts
const specType = typeof selector === 'string'
  ? selector
  : selector.selectorType
```

This restores lockfile-seeded versions to the candidate set. It does not add a
new preference rule or force pnpm to keep every locked version. Optional-peer
auto-installation continues to choose the highest version satisfying every
recorded peer range.

The equivalent fix is included in pacquet, pnpm's Rust port.

## Validation

- Added matching TypeScript and Rust regression tests.
- Verified the public reproduction against `pnpm@11.4.0` and the patched CLI.
- Ran the focused TypeScript resolver checks and pacquet test, clippy, format,
  and `cargo nextest` checks.

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-06-02 07:30:52 +02:00
..
2026-05-29 17:26:13 +02:00
2026-05-29 17:26:13 +02:00
2026-05-29 17:26:13 +02:00
2026-05-27 15:15:01 +02:00
2026-05-29 17:26:13 +02:00