27 Commits

Author SHA1 Message Date
Zoltan Kochan
0e8042e6dc revert: "feat: add allowBuildsOfTrustedDeps setting (true by default) (#11078)"
This reverts commit 5a3dc4ab2f.
2026-03-26 15:19:24 +01:00
Victor Sumner
ee9fe5853e perf(importer): skip staging directory and write package.json as completion marker (#11088)
## Problem

The indexed package importer always creates a staging temp directory, imports files there, then renames to the final location. For cold installs where the target doesn't exist (the common case), the staging + rename is unnecessary overhead.

## Solution

- **Fast path**: callers already verify the target package is missing before calling `importIndexedDir`, so we can write directly into the final directory and skip the temp dir + rename. Falls back to the atomic staging path on EEXIST (concurrent import race) or when `keepModulesDir` is set (hoisted linker needs to merge existing `node_modules`).

- **Completion marker**: `package.json` is written last by `tryImportIndexedDir`, so `pkgExistsAtTargetDir()` (which checks for `package.json`) won't consider a partially-imported directory as complete after a crash.

- **Atomic copy**: the copy import path (non-COW filesystems) uses a temp file + `renameOverwriteSync` for the `package.json` write, since `copyFileSync` is not atomic. Hard links and reflinks are inherently atomic. This is expressed via the `Importer` interface (`importFile` + `importFileAtomic`), passed as the first argument to `importIndexedDir`.

- **Synthetic package.json**: packages that lack a `package.json` (e.g. injected Bit workspace packages) now get a synthetic empty `{}` added to the store, so the completion marker works universally.

- **DRY**: extracted `retryWithSanitizedFilenames()` to deduplicate the ENOENT handler used by both the fast path and staging path.
2026-03-25 23:16:08 +01:00
Zoltan Kochan
5a3dc4ab2f feat: add allowBuildsOfTrustedDeps setting (true by default) (#11078)
* feat: load default trusted deps list from @pnpm/plugin-trusted-deps

Add a new `use-default-trusted-deps` setting (default: true) that
automatically loads a curated list of known-good packages into
`allowBuilds` from @pnpm/plugin-trusted-deps. User-configured
allowBuilds entries take precedence over the defaults. Set
`use-default-trusted-deps=false` to disable.

* fix: use catalog reference for @pnpm/plugin-trusted-deps

* fix: use default import for @pnpm/plugin-trusted-deps CJS compat

The package uses Object.defineProperty for DEFAULT_ALLOW_BUILDS,
which Node.js/Jest ESM interop can't detect as a named export.
Switch to a default import to fix test failures.

* fix: use named ESM import from @pnpm/plugin-trusted-deps@0.3.0-1

The package now ships an ESM entry point with proper named exports,
so we can use a clean named import instead of the default import
workaround.

* fix: update @pnpm/plugin-trusted-deps to 0.3.0-2

Uses static JSON import attributes in ESM entry, fixing the bundle
issue where createRequire resolved paths relative to the bundle
output instead of the original package.

* refactor: rename setting to allow-builds-for-trusted-deps

* test: disable default trusted deps in approveBuilds tests

The tests assert exact allowBuilds contents, so the default trusted
list must be disabled to avoid polluting the expected values.

* fix: don't persist default trusted deps list to pnpm-workspace.yaml

Track the user's original allowBuilds separately as userAllowBuilds
before merging the default trusted list. Use userAllowBuilds when
writing back to pnpm-workspace.yaml to avoid persisting the ~370
default entries from @pnpm/plugin-trusted-deps.

* refactor: rename setting to allow-builds-of-trusted-deps

* docs: use camelCase for setting name in changeset

* fix: include userAllowBuilds in install command opts types

Without this, userAllowBuilds wasn't passed through to
handleIgnoredBuilds, causing the default trusted list to be
written to pnpm-workspace.yaml during e2e tests.

* fix: set userAllowBuilds to empty object when user has no config

When the user has no allowBuilds configured, userAllowBuilds was
undefined, causing handleIgnoredBuilds to fall back to the merged
allowBuilds (with defaults). Use empty object instead so the
fallback doesn't trigger.

* fix: read allowBuilds from workspace manifest when writing back

Instead of tracking userAllowBuilds separately (which gets stale
when other code writes to pnpm-workspace.yaml mid-install), read
the current allowBuilds directly from pnpm-workspace.yaml before
writing. This avoids persisting the default trusted list and
preserves entries written by --allow-build earlier in the flow.

Also update e2e test expectation: esbuild is now in the default
trusted list, so it builds instead of being ignored.

* chore: update tsconfig references for new dependencies

* test: disable default trusted deps in approveBuilds e2e install

The execPnpmInstall helper runs the bundled CLI which picks up
the default allowBuildsOfTrustedDeps=true. This causes extra
placeholder entries in pnpm-workspace.yaml that break assertions.

* fix: revert approveBuilds to use config-based allowBuilds

approveBuilds.handler should use opts.allowBuilds from getConfig()
(which excludes trusted deps defaults when disabled) rather than
reading the workspace manifest. The handler's job is to write
approve/deny decisions, not merge with auto-populated placeholders.

* test: add config reader tests for allowBuildsOfTrustedDeps

Cover: (1) default enabled with trusted defaults merged,
(2) user allowBuilds overrides defaults, (3) setting
allow-builds-of-trusted-deps=false disables the merge.
2026-03-25 16:42:36 +01:00
Devendr Mishra
74cdef5e46 fix: resolve patch file paths during pnpm fetch (#11054)
* fix: ensure patches are applied during pnpm fetch

* test: add coverage for patch file resolution during pnpm fetch fallback

* fix(test): remove invalid pnpm property in fetch tests

* fix: resolve lint errors in fetch test
2026-03-24 14:38:18 +01:00
Zoltan Kochan
606f53e78f feat: add dedupePeers option to reduce peer dependency duplication (#11071)
* 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)
2026-03-24 13:51:17 +01:00
Victor Sumner
615bd240eb perf: skip redundant GVS internal linking on warm reinstall (#11073)
* perf: skip redundant GVS internal linking on warm reinstall

When GVS is enabled and the store is warm (added === 0), skip
re-creating internal symlinks, re-linking bins inside the GVS store,
and re-importing packages since they already persist outside
node_modules/. Also filter directPkgDirs by hasBin to avoid
unnecessary package.json reads when linking direct dep bins.

* fix: preserve link: deps in hasBin filter for bin linking

The hasBin filter was dropping directories not present in the dep graph
(e.g. link: dependencies), which would silently break bin linking for
linked local packages that expose binaries.

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-03-24 01:22:12 +00:00
Zoltan Kochan
54ffb948bd refactor: add recursiveByDefault property to CommandDefinition (#11062)
Replace the hardcoded command name list in main.ts with a declarative
recursiveByDefault property on CommandDefinition. Each command that
should run workspace-wide by default now exports this property.

Also adds recursiveByDefault to list, ll, and why commands.
2026-03-22 16:04:20 +01:00
Brandon Cheng
f98a2db373 fix: invalid specifiers for peers on all non-exact version selectors (#11049)
* test: add test for hoist peers when given all range version selectors

* fix: invalid specifiers for peers on non-string version selectors

In tests, the bare specifier for the `@pnpm.e2e/peer-a` dependency
became ` || 1.0.0`. This was because the `versions` array could be
empty, causing the `.join(' || ')` operation to execute on a holey
array.

This caused a test in `installing/commands/test/update/update.ts` to
fail.
2026-03-22 01:47:12 +01:00
Trevor Burnham
d0aea45b28 feat: warn when optimistic-repeat-install skips shouldRefreshResolution hooks (#10995)
* feat: warn when optimistic-repeat-install skips shouldRefreshResolution hooks

* Fix log message for optimistic repeat install

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-03-21 23:52:01 +01:00
Brandon Cheng
41dc031a67 test: use resolution-mode=highest in tests (#10989)
* fix: configure default resolution-mode to highest in pkg-manager/core

* test: update catalog tests for resolution-mode=highest

* test: fix `--fix-lockfile` test for new resolution-mode default

```
  ● fix broken lockfile with --fix-lockfile

    expect(received).toBeTruthy()

    Received: undefined

      55 |   const lockfile: LockfileFile = readYamlFileSync(WANTED_LOCKFILE)
      56 |   expect(Object.keys(lockfile.packages as PackageSnapshots)).toHaveLength(2)
    > 57 |   expect(lockfile.packages?.['@types/semver@5.3.31']).toBeTruthy()
         |                                                       ^
      58 |   expect(lockfile.packages?.['@types/semver@5.3.31']?.resolution).toEqual({
      59 |     integrity: 'sha512-WBv5F9HrWTyG800cB9M3veCVkFahqXN7KA7c3VUCYZm/xhNzzIFiXiq+rZmj75j7GvWelN3YNrLX7FjtqBvhMw==',
      60 |   })

      at Object.<anonymous> (test/install/fixLockfile.ts:57:55)
```

* test: fix lockfile conflict test

  ● a lockfile v6 with merge conflicts is autofixed

    expect(received).toHaveProperty(path, value)

    Expected path: "version"

    Expected value: "100.1.0"
    Received value: "101.0.0"

      1284 |
      1285 |   const lockfile = project.readLockfile()
    > 1286 |   expect(lockfile.importers?.['.'].dependencies?.['@pnpm.e2e/dep-of-pkg-with-1-dep']).toHaveProperty('version', '100.1.0')
           |                                                                                       ^
      1287 | })
      1288 |
      1289 | test('a lockfile with duplicate keys is fixed', async () => {

      at Object.<anonymous> (test/lockfile.ts:1286:87)

* test: fix deploy shared lockfile test

  ● deploy with a shared lockfile that has peer dependencies suffix in workspace package dependency paths

    expect(received).toMatchObject(expected)

    - Expected  - 6
    + Received  + 1

    @@ -1,11 +1,11 @@
      Object {
        "importers": Object {
          "packages/project-0": Object {
            "dependencies": Object {
              "project-1": Object {
    -           "version": "file:packages/project-1(is-negative@1.0.0)(project-2@file:packages/project-2(is-positive@1.0.0))",
    +           "version": "file:packages/project-1(is-negative@2.1.0)(project-2@file:packages/project-2(is-positive@1.0.0))",
              },
              "project-2": Object {
                "version": "file:packages/project-2(is-positive@1.0.0)",
              },
            },
    @@ -31,13 +31,8 @@
              "type": "directory",
            },
          },
        },
        "snapshots": Object {
    -     "project-1@file:packages/project-1(is-negative@1.0.0)(project-2@file:packages/project-2(is-positive@1.0.0))": Object {
    -       "dependencies": Object {
    -         "project-2": "file:packages/project-2(is-positive@1.0.0)",
    -       },
    -     },
          "project-2@file:packages/project-2(is-positive@1.0.0)": Object {},
        },
      }

      950 |     workspaceDir: process.cwd(),
      951 |   })
    > 952 |   expect(assertProject('.').readLockfile()).toMatchObject({
          |                                             ^
      953 |     importers: {
      954 |       'packages/project-0': {
      955 |         dependencies: {

      at Object.<anonymous> (test/shared-lockfile.test.ts:952:45)

* test: fix injectLocalPackages test
2026-03-21 23:21:04 +01:00
Brandon Cheng
021f70d0b0 fix: handle non-string version selectors in hoistPeers (#11048)
* test: add test for version selector with weight in hoistPeers

* fix: handle non-string version selectors in hoistPeers
2026-03-21 23:17:24 +01:00
Zoltan Kochan
cd2dc7d481 refactor: prefix internal scripts with . to hide them (#11051)
* 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>
2026-03-21 14:30:56 +01:00
Zoltan Kochan
9b801c888d fix: check allowBuild for packages with cached side-effects (#11039)
Closes #11035

## Summary

### Root cause fix: don't apply cached side-effects for unapproved packages
When importing packages from the store, side-effects cache was applied for any package not explicitly denied (`allowBuild !== false`). This meant unapproved packages (`allowBuild === undefined`) got cached build artifacts, setting `isBuilt: true` and bypassing the `allowBuild` check in `buildModules`.

**Fix:** Only apply side-effects cache when `allowBuild` returns `true` (explicitly approved). Changed in three locations:
- `installing/deps-restorer/src/index.ts` (isolated linker)
- `installing/deps-restorer/src/linkHoistedModules.ts` (hoisted linker)
- `installing/deps-installer/src/install/link.ts` (non-headless install)

### Revocation detection
When a package's build approval is revoked between installs (was `true` in `.modules.yaml`, now undefined), detect it in `mutateModules` and add to `ignoredBuilds` so `strictDepBuilds` fails.

### Status messages in `_rebuild`
Users now see what happened to each package during rebuild:
- `pkg@version: built successfully`
- `pkg@version: skipped (no build scripts)`
- `pkg@version: skipped (not allowed)`
- `pkg@version: reused from store cache`

And during install:
- `pkg@version: reused from store (side effects cache)`

### `buildSelectedPkgs` fixes
- Preserve `storeDir`, `virtualStoreDir`, `virtualStoreDirMaxLength` from existing `.modules.yaml` instead of overwriting with config-derived values (which caused "reinstall from scratch" prompt)
- Write `allowBuilds` to `.modules.yaml` so GVS doesn't detect a mismatch on next install
- Merge `ignoredBuilds` with existing entries for packages not being rebuilt
2026-03-21 12:51:24 +01:00
Zoltan Kochan
9fc552d37a fix: update GVS symlinks after approve-builds by running install (#11043)
Fixes #11042

- **Root cause**: When `enableGlobalVirtualStore` is true and `allowBuilds` is not configured, `createAllowBuildFunction()` returned `undefined`, causing all GVS hashes to include `ENGINE_NAME`. When `approve-builds` later configured `allowBuilds`, the hash didn't change because the engine was already included.
- **Fix**: Default `allowBuilds` to `{}` in GVS mode so hashes are engine-agnostic by default, and have `approve-builds` call `install.handler()` in GVS mode instead of the low-level `install()` function, so it properly handles workspaces and updates symlinks.
- **Refactor**: Broke circular dependencies between `building/commands`, `installing/commands`, and `global/commands` using dependency injection via a `commands` map passed as the third argument to command handlers. Added `CommandHandler` and `CommandHandlerMap` types to `@pnpm/cli.command`.

## Changes

### Architecture
- Command handlers now receive a `commands` map as an optional third argument `(opts, params, commands?)`
- The CLI dispatcher in `main.ts` passes the full commands map to every handler
- Handlers that need other commands (e.g., `globalAdd` needs `approve-builds`, `recursive` needs `rebuild`) access them from this map
- This replaces direct cross-package imports that would create circular dependencies

### Packages changed
- `@pnpm/cli.command` — new `CommandHandler` and `CommandHandlerMap` types
- `@pnpm/building.commands` — `approve-builds` uses `install.handler` for GVS
- `@pnpm/global.commands` — removed `building/commands` dependency; receives `approve-builds` via commands map
- `@pnpm/installing.commands` — receives `rebuild` via commands map instead of direct import
- `@pnpm/installing.deps-installer` / `@pnpm/installing.deps-restorer` — default `allowBuilds` to `{}` in GVS mode
- `pnpm` CLI — dispatcher passes commands map to all handlers
2026-03-21 12:50:46 +01:00
Zoltan Kochan
996284f8cc feat(approve-builds): positional args, !pkg deny syntax, and auto-populate allowBuilds (#11030)
### `pnpm approve-builds` positional arguments
- `pnpm approve-builds foo` — approves `foo`, leaves everything else untouched
- `pnpm approve-builds !bar` — denies `bar`, leaves everything else untouched
- `pnpm approve-builds foo !bar` — approves `foo`, denies `bar`
- Only mentioned packages are modified; unmentioned packages remain pending
- `--all` cannot be combined with positional arguments
- Contradictory arguments (`pkg !pkg`) are rejected

### Auto-populate `allowBuilds` during install
- When `pnpm install` encounters packages with build scripts that aren't yet in `allowBuilds`, they are automatically written to `pnpm-workspace.yaml` with a `'set this to true or false'` placeholder
- Users can then edit the config directly instead of running `approve-builds`
- The placeholder behaves like a missing entry: builds are skipped and `strictDepBuilds` still fails
- Existing `allowBuilds` entries are preserved (only new packages get placeholders)
2026-03-20 14:58:56 +01:00
Zoltan Kochan
cd0e887db3 refactor: remove unused @pnpm/fs.msgpack-file package and lockfile-directory setting (#11033)
Remove the @pnpm/fs.msgpack-file package which was never imported in
source code (only in its own tests). Also remove the deprecated
lockfile-directory CLI option alias — users should use lockfile-dir.
2026-03-20 00:38:02 +01:00
Zoltan Kochan
0d88df854f chore: update all dependencies to latest versions (#11032)
* chore: update all dependencies to latest versions

Update all outdated dependencies across the monorepo catalog and fix
breaking changes from major version bumps.

Notable updates:
- ESLint 9 → 10 (fix custom rule API, disable new no-useless-assignment)
- @stylistic/eslint-plugin 4 → 5 (auto-fixed indent changes)
- @cyclonedx/cyclonedx-library 9 → 10 (adapt to removed SPDX API)
- esbuild 0.25 → 0.27
- TypeScript 5.9.2 → 5.9.3
- Various @types packages, test utilities, and build tools

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: update unified/remark/mdast imports for v11/v4 API changes

Update imports in get-release-text for the new ESM named exports:
- mdast-util-to-string: default → { toString }
- unified: default → { unified }

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: resolve typecheck errors from dependency updates

- isexe v4: use named import { sync } instead of default export
- remark-parse/remark-stringify v11: add vfile as packageExtension
  dependency so TypeScript can resolve type declarations
- get-release-text: remove unused @ts-expect-error directives

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: revert runtime dependency major version bumps

Revert major version bumps for runtime dependencies that are bundled
into pnpm to fix test failures where pnpm add silently fails:
- bin-links: keep ^5.0.0 (was ^6.0.0)
- cli-truncate: keep ^4.0.0 (was ^5.2.0)
- delay: keep ^6.0.0 (was ^7.0.0)
- filenamify: keep ^6.0.0 (was ^7.0.1)
- find-up: keep ^7.0.0 (was ^8.0.0)
- isexe: keep 2.0.0 (was 4.0.0)
- normalize-newline: keep 4.1.0 (was 5.0.0)
- p-queue: keep ^8.1.0 (was ^9.1.0)
- ps-list: keep ^8.1.1 (was ^9.0.0)
- string-length: keep ^6.0.0 (was ^7.0.1)
- symlink-dir: keep ^7.0.0 (was ^9.0.0)
- terminal-link: keep ^4.0.0 (was ^5.0.0)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: restore runtime dependency major version bumps

Re-apply all runtime dependency major version bumps that were
previously reverted. All packages maintain their default exports
except isexe v4 which needs named imports.

Updated runtime deps:
- bin-links: ^5.0.0 → ^6.0.0
- cli-truncate: ^4.0.0 → ^5.2.0
- delay: ^6.0.0 → ^7.0.0
- filenamify: ^6.0.0 → ^7.0.1
- find-up: ^7.0.0 → ^8.0.0
- isexe: 2.0.0 → 4.0.0 (fix: use named import { sync })
- normalize-newline: 4.1.0 → 5.0.0
- p-queue: ^8.1.0 → ^9.1.0
- ps-list: ^8.1.1 → ^9.0.0
- string-length: ^6.0.0 → ^7.0.1
- symlink-dir: ^7.0.0 → ^9.0.0
- terminal-link: ^4.0.0 → ^5.0.0

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: revert tempy to 3.0.0 to fix bundle hang

tempy 3.2.0 pulls in temp-dir 3.0.0 which uses async fs.realpath()
inside its module init. When bundled by esbuild into the __esm lazy
init pattern, this causes a deadlock during module initialization,
making the pnpm binary hang silently on startup.

Keeping tempy at 3.0.0 which uses temp-dir 2.x (sync fs.realpathSync).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add comment explaining why tempy cannot be upgraded

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: revert nock to 13.3.4 for node-fetch compatibility

nock 14 changed its HTTP interception mechanism in a way that doesn't
properly intercept node-fetch requests, causing audit tests to hang
waiting for responses that are never intercepted.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: add comment explaining why nock cannot be upgraded

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: update symlink-dir imports for v10 ESM named exports

symlink-dir v10 removed the default export and switched to named
exports: { symlinkDir, symlinkDirSync }.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: revert @typescript/native-preview to working version

Newer tsgo dev builds (>= 20260318) have a regression where
@types/node cannot be resolved, breaking all node built-in types.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: vulnerabilities

* fix: align comment indentation in runLifecycleHook

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: pin msgpackr to 1.11.8 for TypeScript 5.9 compatibility

msgpackr 1.11.9 has broken type definitions that use Iterable/Iterator
without required type arguments, causing compile errors with TS 5.9.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 23:28:53 +01:00
zybo
194d36856e feat(ci): implement pnpm ci command (#11003)
* feat(ci): implement pnpm ci command

This implements the `pnpm ci` (clean-install) command, which is similar
to `npm ci`. The command:

- Removes `node_modules` before installation (clean install)
- Installs dependencies from the lockfile with `--frozen-lockfile`
- Fails if the lockfile is missing or out of sync with `package.json`
- Supports workspaces (removes `node_modules` from all workspace projects)

This is useful for CI/CD environments where you want to ensure
reproducible builds.

Aliases: `pnpm clean-install`, `pnpm ic`, `pnpm install-clean`

Closes #6100

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: update tsconfig.json references

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(ci): simplify pnpm ci to compose clean + install --frozen-lockfile

Per maintainer feedback, simplify the ci command to just call
`clean.handler()` then `install.handler()` with frozenLockfile: true,
following the same composition pattern as installTest.

Moved ci command from installing/commands to pnpm/src/cmd/ where it
can import both clean and install handlers directly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(ci): remove ci stub from installing/commands

The ci command now lives entirely in pnpm/src/cmd/ci.ts,
so the old stub in installing/commands is no longer needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(ci): rename ci.ts to cleanInstall.ts

Per reviewer feedback, use the full command name for the file.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(ci): use non-dotfile marker in ci test

The clean command preserves dotfiles in node_modules (except pnpm's
own .bin, .modules.yaml, .pnpm), so the test marker starting with "."
was not being removed. Renamed to a regular file name.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: zubeyralmaho <zubeyralmaho@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 14:43:05 +01:00
Rohan Santhosh Kumar
4d1cb4749f docs: fix --force help text grammar (#11023)
Co-authored-by: rohan436 <rohan.santhoshkumar@googlemail.com>
2026-03-19 10:46:49 +01:00
Zoltan Kochan
1701a65845 chore: reduce noisy warnings in test output (#11022)
* 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>
2026-03-19 10:43:12 +01:00
Zoltan Kochan
8acf2708c9 refactor: rename deps-resolver and env-installer packages (#11013)
Rename @pnpm/installing.resolve-dependencies to @pnpm/installing.deps-resolver
for consistency with the <domain>.<leaf> naming convention.
2026-03-18 21:52:01 +01:00
Zoltan Kochan
303ca410f5 feat!: stop reading settings from the pnpm field of package.json (#10086)
Settings should be read from pnpm-workspace.yaml
2026-03-18 14:46:07 +01:00
Zoltan Kochan
d0ae78821a refactor: rename workspace functions from packages to projects (#11002)
Align function, type, and file names with the packages-to-projects
rename in workspace packages (projects-filter, projects-reader,
projects-sorter).
2026-03-18 11:38:02 +01:00
Zoltan Kochan
dba4153767 refactor: rename packages and consolidate runtime resolvers (#10999)
* 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>
2026-03-18 00:19:58 +01:00
Zoltan Kochan
4a36b9a110 refactor: rename internal packages to @pnpm/<domain>.<leaf> convention (#10997)
## Summary

Rename all internal packages so their npm names follow the `@pnpm/<domain>.<leaf>` convention, matching their directory structure. Also rename directories to remove redundancy and improve clarity.

### Bulk rename (94 packages)

All `@pnpm/` packages now derive their name from their directory path using dot-separated segments. Exceptions: `packages/`, `__utils__/`, and `pnpm/artifacts/` keep leaf names only.

### Directory renames (removing redundant prefixes)

- `cli/cli-meta` → `cli/meta`, `cli/cli-utils` → `cli/utils`
- `config/config` → `config/reader`, `config/config-writer` → `config/writer`
- `fetching/fetching-types` → `fetching/types`
- `lockfile/lockfile-to-pnp` → `lockfile/to-pnp`
- `store/store-connection-manager` → `store/connection-manager`
- `store/store-controller-types` → `store/controller-types`
- `store/store-path` → `store/path`

### Targeted renames (clarity improvements)

- `deps/dependency-path` → `deps/path` (`@pnpm/deps.path`)
- `deps/calc-dep-state` → `deps/graph-hasher` (`@pnpm/deps.graph-hasher`)
- `deps/inspection/dependencies-hierarchy` → `deps/inspection/tree-builder` (`@pnpm/deps.inspection.tree-builder`)
- `bins/link-bins` → `bins/linker`, `bins/remove-bins` → `bins/remover`, `bins/package-bins` → `bins/resolver`
- `installing/get-context` → `installing/context`
- `store/package-store` → `store/controller`
- `pkg-manifest/manifest-utils` → `pkg-manifest/utils`

### Manifest reader/writer renames

- `workspace/read-project-manifest` → `workspace/project-manifest-reader` (`@pnpm/workspace.project-manifest-reader`)
- `workspace/write-project-manifest` → `workspace/project-manifest-writer` (`@pnpm/workspace.project-manifest-writer`)
- `workspace/read-manifest` → `workspace/workspace-manifest-reader` (`@pnpm/workspace.workspace-manifest-reader`)
- `workspace/manifest-writer` → `workspace/workspace-manifest-writer` (`@pnpm/workspace.workspace-manifest-writer`)

### Workspace package renames

- `workspace/find-packages` → `workspace/projects-reader`
- `workspace/find-workspace-dir` → `workspace/root-finder`
- `workspace/resolve-workspace-range` → `workspace/range-resolver`
- `workspace/filter-packages-from-dir` merged into `workspace/filter-workspace-packages` → `workspace/projects-filter`

### Domain moves

- `pkg-manifest/read-project-manifest` → `workspace/project-manifest-reader`
- `pkg-manifest/write-project-manifest` → `workspace/project-manifest-writer`
- `pkg-manifest/exportable-manifest` → `releasing/exportable-manifest`

### Scope

- 1206 files changed
- Updated: package.json names/deps, TypeScript imports, tsconfig references, changeset files, renovate.json, test fixtures, import ordering
2026-03-17 21:50:40 +01:00
Zoltan Kochan
7a304b17c4 refactor: rename directories and unify command packages per domain (#10993)
- Rename `installing/core` → `installing/deps-installer` and `installing/headless` → `installing/deps-restorer` for clearer naming
- Rename all `plugin-commands-*` directories to use `-commands` suffix convention
- Merge multiple command packages per domain into a single `commands/` directory (one commands package per domain rule):
  - `building/{build-commands,policy-commands}` → `building/commands`
  - `deps/compliance/{audit-commands,licenses-commands,sbom-commands}` → `deps/compliance/commands`
  - `deps/inspection/{listing-commands,outdated-commands}` → `deps/inspection/commands`
  - `store/{store-commands,inspecting-commands}` → `store/commands`
  - `releasing/{publish-commands,deploy-commands}` → `releasing/commands`
  - `cli/{completion-commands,doctor-commands}` → `cli/commands`
  - `engine/pm/{self-updater-commands,setup-commands}` → `engine/pm/commands`
  - `engine/runtime/{runtime-commands,env-commands}` → `engine/runtime/commands`
  - `cache/cache-commands` → `cache/commands`
- Fix relative paths in merged test files (pnpmBin, __typings__ references)
- Update jest config to ignore `utils/` dirs at any nesting depth under `test/`
- Fix stale package names in changeset files
2026-03-17 17:42:20 +01:00
Zoltan Kochan
f47ef4b125 refactor: reorganize monorepo domain structure (#10987)
Reorganize the monorepo's top-level domain directories for clarity:

- pkg-manager/ split into:
  - installing/ (core, headless, client, resolve-dependencies, etc.)
  - installing/linking/ (hoist, direct-dep-linker, modules-cleaner, etc.)
  - bins/ (link-bins, package-bins, remove-bins)
- completion/ merged into cli/
- dedupe/ moved to installing/dedupe/
- env/ renamed to engine/ with subdomains:
  - engine/runtime/ (node.fetcher, node.resolver, plugin-commands-env, etc.)
  - engine/pm/ (plugin-commands-setup, plugin-commands-self-updater)
- env.path moved to shell/
- tools/ and runtime/ dissolved
- reviewing/ and lockfile audit packages moved to deps/:
  - deps/inspection/ (list, outdated, dependencies-hierarchy)
  - deps/compliance/ (audit, licenses, sbom)
- registry/ moved to resolving/registry/
- semver/peer-range moved to deps/
- network/fetching-types moved to fetching/
- packages/ slimmed down, moving packages to proper domains:
  - calc-dep-state, dependency-path -> deps/
  - parse-wanted-dependency -> resolving/
  - git-utils -> network/
  - naming-cases -> text/
  - make-dedicated-lockfile -> lockfile/
  - render-peer-issues -> installing/
  - plugin-commands-doctor -> cli/
  - plugin-commands-init -> workspace/

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 13:45:54 +01:00