* feat: add `dedupePeers` option to reduce peer dependency duplication
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
* fix: pass dedupePeers to getOutdatedLockfileSetting and use spread for 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).
* fix: write dedupePeers to lockfile like other settings
Write the value directly instead of spread syntax, and use the same
!= null guard pattern as autoInstallPeers in the settings checker.
* test: add integration test for dedupePeers in peerDependencies.ts
* fix: only write dedupePeers to lockfile when enabled
When dedupePeers is false (default), don't write it to lockfile settings.
This avoids adding a new key to every lockfile.
* test: simplify dedupePeers test assertions
* test: check exact snapshot keys in dedupePeers integration test
* test: add workspace test for dedupePeers with different peer versions
* fix: keep transitive peers in suffix with version-only IDs
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
* refactor: extract peerNodeIdToPeerId helper in resolvePeers
* refactor: simplify peerNodeIdToPeerId return
* fix: pin peer-a dist tag in dedupePeers tests for CI stability
* fix: address review comments
- 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)
* fix: ensure PNPM_HOME/bin is in PATH during pnpm setup
When upgrading from old pnpm (global bin = PNPM_HOME) to new pnpm
(global bin = PNPM_HOME/bin), `pnpm setup` would fail because the
spawned `pnpm add -g` checks that the global bin dir is in PATH.
Prepend PNPM_HOME/bin to PATH in the spawned process env so the
check passes during the transition.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: update pnpm to v11 beta 2
* chore: update pnpm to v11 beta 2
* chore: update pnpm to v11 beta 2
* chore: update pnpm to v11 beta 2
* fix: lint
* refactor: rename _-prefixed scripts to .-prefixed scripts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: update root package.json to use .test instead of _test
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* ci: update action-setup
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: reduce noisy warnings in test output
- Suppress ExperimentalWarning and DEP0169 via --disable-warning in NODE_OPTIONS
- Fix MaxListenersExceededWarning by raising limit in StoreIndex when adding exit listeners
- Update meta-updater to generate the new _test scripts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: stop streaming pnpm subprocess output during CLI tests
Buffer stdout/stderr from execPnpm instead of writing to the parent
process in real time. Output is still included in the error message on
failure.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: pipe all subprocess output in CLI tests
Use stdio: 'pipe' for all pnpm/pnpx spawn helpers so subprocess output
is buffered instead of printed. Output is still included in error
messages on failure.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: remove duplicate @pnpm/installing.env-installer in pnpm/package.json
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: use pipe stdio in dlx and errorHandler tests
Replace stdio: 'inherit' and [null, 'pipe', 'inherit'] with 'pipe' to
prevent subprocess output from leaking into test output.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: skip maxListeners adjustment when set to unlimited (0)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: rename workspace.sort-packages and workspace.pkgs-graph
- workspace.sort-packages -> workspace.projects-sorter
- workspace.pkgs-graph -> workspace.projects-graph
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: rename packages/ to core/ and pkg-manifest.read-package-json to reader
- Rename packages/ directory to core/ for clarity
- Rename pkg-manifest/read-package-json to pkg-manifest/reader (@pnpm/pkg-manifest.reader)
- Update all tsconfig, package.json, and lockfile references
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: consolidate runtime resolvers under engine/runtime domain
- Remove unused @pnpm/engine.runtime.node.fetcher package
- Rename engine/runtime/node.resolver to node-resolver (dash convention)
- Move resolving/bun-resolver to engine/runtime/bun-resolver
- Move resolving/deno-resolver to engine/runtime/deno-resolver
- Update all package names, tsconfig paths, and lockfile references
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: update lockfile after removing node.fetcher
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: sort tsconfig references and package.json deps alphabetically
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: auto-fix import sorting
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: update __typings__ paths in tsconfig.lint.json for moved resolvers
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: remove deno-resolver from deps of bun-resolver
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Add n/prefer-node-protocol rule and autofix all bare builtin imports
to use the node: prefix. Simplify the simple-import-sort builtins
pattern to just ^node: since all imports now use the prefix.
Add eslint-plugin-simple-import-sort to enforce consistent import ordering:
- Node.js builtins first
- External packages second
- Relative imports last
- Named imports sorted alphabetically within each statement
* fix: detect overrides and other lockfile-affecting setting changes in optimisticRepeatInstall
When optimisticRepeatInstall was enabled, changing overrides,
packageExtensions, ignoredOptionalDependencies, patchedDependencies,
or peersSuffixMaxLength would not trigger a reinstall because these
settings were not tracked in the workspace state file.
* refactor: extract WORKSPACE_STATE_SETTING_KEYS to prevent type/runtime drift
The settings key list in createWorkspaceState's pick() call must stay
in sync with the WorkspaceStateSettings type. Extract a shared const
array so both the type and runtime pick are derived from a single
source, preventing the class of bug fixed in the previous commit.
* chore: `package.json` add type field
* chore: add type field to every package.json
* chore: add type field to every package.json
---------
Co-authored-by: Zoltan Kochan <z@kochan.io>