Skip to content

feat: add dedupePeers option to reduce peer dependency duplication#11071

Merged
zkochan merged 13 commits into
mainfrom
dedupe-peers
Mar 24, 2026
Merged

feat: add dedupePeers option to reduce peer dependency duplication#11071
zkochan merged 13 commits into
mainfrom
dedupe-peers

Conversation

@zkochan

@zkochan zkochan commented Mar 23, 2026

Copy link
Copy Markdown
Member

Summary

  • Adds a new opt-in dedupePeers setting that reduces peer dependency duplication
  • Uses version-only peer identifiers (name@version) instead of full dep paths when building peer suffixes
  • Eliminates nested suffixes like (@emotion/react@11(react@18))(@emotion/react@11.0.0)
  • Transitive peers are still tracked (per-project isolation preserved), just identified by version instead of full dep path
  • Works correctly with autoInstallPeers

Closes #11070

Before/After

Nested peer suffixes eliminated:

BEFORE: @emotion/styled@11(@emotion/react@11(react@18))(react@18)
AFTER:  @emotion/styled@11(@emotion/react@11.0.0)(react@18.0.0)

Multi-project correctness preserved:

project-1 (peer-c@1.0.0): abc-grand-parent@1.0.0(@pnpm.e2e/peer-c@1.0.0)
project-2 (peer-c@2.0.0): abc-grand-parent@1.0.0(@pnpm.e2e/peer-c@2.0.0)

Test plan

  • Unit tests: version-only suffixes, transitive peers with version-only IDs, multi-project isolation
  • Integration test: single project with transitive peer chain (abc-grand-parent)
  • Integration test: workspace with two projects using different peer-c versions
  • All existing tests pass (40/40 in deps-resolver, 67/67 in deps-installer peerDependencies)
  • All modified packages compile successfully

zkochan added 12 commits March 23, 2026 17:25
When enabled, this option applies two optimizations to peer dependency resolution:

1. Version-only peer suffixes: Uses name@version instead of full dep paths
   (including nested peer suffixes) when building peer identity hashes.
   This eliminates deeply nested suffixes like (foo@1.0.0(bar@2.0.0)).

2. Transitive peer pruning: Only directly declared peer dependencies are
   included in a package's suffix. Transitive peers from children are not
   propagated upward, preventing combinatorial explosion while maintaining
   correct node_modules layout.

The option is scoped per-project: each workspace project defines a peer
resolution environment, and all packages within that project's tree share
that environment. Projects with different peer versions correctly produce
different instances.

Closes #11070
…r lockfile write

The frozen install path (used by approve-builds) calls getOutdatedLockfileSetting
but was missing the dedupePeers parameter. This caused a false LOCKFILE_CONFIG_MISMATCH
error because the lockfile had the key written (as undefined/null via YAML serialization)
while the check function received undefined for the config value.

Fix: pass dedupePeers to the settings check call, and use spread syntax to only write
the dedupePeers key to lockfile settings when it's truthy (avoiding undefined keys).
Write the value directly instead of spread syntax, and use the same
!= null guard pattern as autoInstallPeers in the settings checker.
When dedupePeers is false (default), don't write it to lockfile settings.
This avoids adding a new key to every lockfile.
Instead of pruning transitive peers entirely (which prevented per-project
differentiation), keep them but use version-only identifiers. This way:

- Packages like abc-grand-parent still get a peer suffix when different
  projects provide different peer versions (correct per-project isolation)
- But the suffixes use name@version instead of full dep paths, eliminating
  the nested parentheses that cause combinatorial explosion
@zkochan zkochan marked this pull request as ready for review March 23, 2026 22:49
Copilot AI review requested due to automatic review settings March 23, 2026 22:49

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces an opt-in dedupePeers setting to reduce peer dependency duplication by changing how peer suffixes are computed (using version-only peer identifiers instead of full dep paths), and wires the option through the installer/resolver and lockfile settings.

Changes:

  • Added dedupePeers to config/workspace state, installer options, and lockfile settings so it can be persisted and checked for lockfile mismatch.
  • Updated peer resolution to build peer suffixes from version-only peer IDs (name@version) to avoid nested peer suffixes.
  • Added resolver + installer tests covering version-only suffixes and multi-project isolation behavior.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
workspace/state/src/types.ts Adds dedupePeers to the persisted workspace-state settings key list.
lockfile/types/src/index.ts Extends LockfileSettings with dedupePeers.
lockfile/settings-checker/src/getOutdatedLockfileSetting.ts Adds lockfile-outdated detection for settings.dedupePeers.
installing/deps-resolver/src/resolvePeers.ts Implements version-only peer IDs via peerNodeIdToPeerId() and passes dedupePeers through context.
installing/deps-resolver/src/index.ts Plumbs dedupePeers into the resolver entrypoint options.
installing/deps-resolver/test/resolvePeers.ts Adds unit tests for dedupePeers peer suffix behavior.
installing/deps-installer/src/install/extendInstallOptions.ts Adds dedupePeers to strict install options and defaults it to false.
installing/deps-installer/src/install/index.ts Persists dedupePeers into lockfile settings and includes it in outdated-setting checks; passes option into dependency resolution.
installing/deps-installer/src/getPeerDependencyIssues.ts Adds dedupePeers to options typing for peer-issues collection.
installing/deps-installer/test/install/peerDependencies.ts Adds integration tests asserting version-only suffixes in lockfile snapshots and workspace isolation.
installing/commands/src/recursive.ts Allows recursive command options to include dedupePeers.
installing/commands/src/installDeps.ts Allows installDeps command options to include dedupePeers.
installing/commands/src/install.ts Allows install command options to include dedupePeers.
config/reader/src/Config.ts Adds dedupePeers?: boolean to the Config interface.
.changeset/dedupe-peers-option.md Documents the new dedupePeers feature for release notes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lockfile/settings-checker/src/getOutdatedLockfileSetting.ts Outdated
Comment thread .changeset/dedupe-peers-option.md Outdated
Comment thread installing/deps-resolver/test/resolvePeers.ts Outdated
Comment thread installing/deps-installer/test/install/peerDependencies.ts
Comment thread config/reader/src/Config.ts
@lajczi

lajczi commented Mar 23, 2026

Copy link
Copy Markdown

Awesome work, Zoltan!

- Register dedupe-peers in config schema, types, and defaults so
  .npmrc/pnpm-workspace.yaml settings are parsed correctly
- Use Boolean() comparison in settings checker so enabling dedupePeers
  on a pre-existing lockfile triggers re-resolution
- Fix changeset text and test names: transitive peers are still
  propagated, just with version-only IDs (no nested dep paths)
zkochan added a commit that referenced this pull request Mar 24, 2026
When enabled, peer dependency suffixes use version-only identifiers
(name@version) instead of full dep paths, eliminating nested suffixes
like (foo@1.0.0(bar@2.0.0)). Transitive peers are still tracked but
identified by version instead of full dep path.

Backport of #11071 to v10.

Closes #11070
@GiladShoham

Copy link
Copy Markdown

Amazing!
Will help our use case a lot :)

@zkochan zkochan merged commit 606f53e into main Mar 24, 2026
12 checks passed
@zkochan zkochan deleted the dedupe-peers branch March 24, 2026 12:51
zkochan added a commit that referenced this pull request Mar 24, 2026
…11079)

When enabled, peer dependency suffixes use version-only identifiers
(name@version) instead of full dep paths, eliminating nested suffixes
like (foo@1.0.0(bar@2.0.0)). Transitive peers are still tracked but
identified by version instead of full dep path.

Backport of #11071 to v10.

Closes #11070
@clemens

clemens commented Apr 29, 2026

Copy link
Copy Markdown

Am I overlooking something or isn't this included in the docs yet?

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.

Reduce peer dependency duplication with opt-in dedupePeers option

5 participants