Commit Graph

214 Commits

Author SHA1 Message Date
Zoltan Kochan
e21ff63267 feat: add minimumReleaseAgeLoose setting
When true (the default), pnpm falls back to versions that don't meet the
minimumReleaseAge constraint if no mature versions satisfy the range
being resolved.
2026-04-09 12:14:30 +02:00
Zoltan Kochan
68a951006e test: update registry-mock to 6.0.0 stable and use pnpm view in tests (#11227)
* test: update registry-mock to 6.0.0 stable and use pnpm view in tests

Update @pnpm/registry-mock from 6.0.0-6 to 6.0.0 stable release.
Replace npm view with pnpm view in test helpers now that pnpm has
native view/dist-tag commands. Unskip the nodeRuntime test that was
blocked on the registry-mock republish.

* chore: update pnpm to beta 8

* feat: support versions, dist-tags, and time field selectors in pnpm view

The view command now exposes versions (as an array of version strings),
dist-tags, and time from registry metadata. Single-field --json output
returns the raw value instead of wrapping it in an object, matching npm
behavior. This allows tests to use pnpm view instead of npm view.
2026-04-08 15:14:53 +02:00
Zoltan Kochan
70fa43562e feat: add registry as supported flag of the view command 2026-04-08 11:55:24 +02:00
Zoltan Kochan
7721d2e7f0 feat(audit): add patched versions to minimumReleaseAgeExclude on audit --fix (#11216)
When `pnpm audit --fix` adds overrides to fix vulnerabilities, it now
also adds the minimum patched version for each advisory to
`minimumReleaseAgeExclude` in pnpm-workspace.yaml. This allows
`pnpm install` to install the security fix without waiting for it to
satisfy the minimumReleaseAge constraint.

Closes #10263
2026-04-07 16:38:57 +02:00
Zoltan Kochan
b103439d9a refactor(test): extract shared DEFAULT_OPTS into @pnpm/testing.command-defaults (#11208)
12 command test suites had near-identical ~50-field DEFAULT_OPTS objects
copy-pasted between them. Extract the common fields into a single shared
package so each suite only declares its overrides.
2026-04-05 21:22:24 +02:00
Zoltan Kochan
45a6cb6b2a refactor(auth): unify auth/SSL into structured configByUri (#11201)
Replaces the dual `authConfig` (raw .npmrc) + `authInfos` (parsed auth) + `sslConfigs` (parsed SSL) pattern with a single structured `configByUri: Record<string, RegistryConfig>` field on Config.

### New types (`@pnpm/types`)
- **`RegistryConfig`** — per-registry config: `{ creds?: Creds, tls?: TlsConfig }`
- **`Creds`** — auth credentials: `{ authToken?, basicAuth?, tokenHelper? }`
- **`TlsConfig`** — TLS config: `{ cert?, key?, ca? }`

### Key changes
- Rewrite `createGetAuthHeaderByURI` to accept `Record<string, RegistryConfig>` instead of raw .npmrc key-value pairs
- Eliminate duplicate auth parsing between `getAuthHeadersFromConfig` and `getNetworkConfigs`
- Remove `authConfig` from the install pipeline (`StrictInstallOptions`, `HeadlessOptions`), replaced by `configByUri`
- Remove `sslConfigs` from Config — SSL fields now live in `configByUri[uri].tls`
- Remove `authConfig['registry']` mutation in `extendInstallOptions` (default registry now passed directly to `createGetAuthHeaderByURI`)
- `authConfig` remains on Config only for raw .npmrc access (config commands, error reporting, config inheritance)

### Security
- tokenHelper in project .npmrc now throws instead of being silently stripped
- tokenHelper execution uses `shell: false` to prevent shell metacharacter injection
- Basic auth uses `Buffer.from().toString('base64')` instead of `btoa()` for Unicode safety
- Dispatcher only creates custom agents when entries actually have TLS fields
2026-04-05 20:15:10 +02:00
Zoltan Kochan
b5d93c6ba9 refactor(config): remove rawLocalConfig and force* hoist flags (#11199)
rawLocalConfig detected whether hoist settings were explicitly
set. In v11, config values are always authoritative.

- Remove rawLocalConfig from ConfigContext, config reader,
  inheritPickedConfig, UniversalOptions
- Remove forceHoistPattern, forcePublicHoistPattern,
  forceShamefullyHoist — validateModules always checks now
- Simplify save-workspace-protocol check
- Remove dead rawLocalConfig overrides in deploy/patchCommit
2026-04-05 11:36:36 +02:00
Zoltan Kochan
3033bee430 refactor(config): split Config interface into settings + runtime context (#11197)
* refactor(config): split Config interface into settings + runtime context

Create ConfigContext for runtime state (hooks, finders, workspace graph,
CLI metadata) and keep Config for user-facing settings only. Functions
use Pick<Config, ...> & Pick<ConfigContext, ...> to express which fields
they need from each interface.

getConfig() now returns { config, context, warnings }. The CLI wrapper
returns { config, context } and spreads both when calling command
handlers (to be refactored to separate params in follow-up PRs).

Closes #11195

* fix: address review feedback

- Initialize cliOptions on pnpmConfig so context.cliOptions is never undefined
- Move rootProjectManifestDir assignment before ignoreLocalSettings guard
- Add allProjectsGraph to INTERNAL_CONFIG_KEYS

* refactor: remove INTERNAL_CONFIG_KEYS from configToRecord

configToRecord now accepts Config and ConfigContext separately, so
context fields are never in scope. Only auth-related Config fields
(authConfig, authInfos, sslConfigs) need filtering.

* refactor: eliminate INTERNAL_CONFIG_KEYS from configToRecord

configToRecord now receives the clean Config object and explicitlySetKeys
separately (via opts.config and opts.context), so context fields are
never in scope. main.ts passes the original split objects alongside
the spread for command handlers that need them.

* fix: spelling

* fix: import sorting

* fix: --config.xxx nconf overrides conflicting with --config CLI flag

When `pnpm add` registers `config: Boolean`, nopt captures
--config.xxx=yyy as the --config flag value instead of treating it
as a nconf-style config override. Fix by extracting --config.xxx args
before nopt parsing and re-parsing them separately.

Also rename the split config/context properties on the command opts
object to _config/_context to avoid clashing with the --config CLI option.
2026-04-04 23:44:25 +02:00
Zoltan Kochan
96704a1c58 refactor(config): rename rawConfig to authConfig, add nodeDownloadMirrors, simplify config reader (#11194)
Major cleanup of the config system after migrating settings from `.npmrc` to `pnpm-workspace.yaml`.

### Config reader simplification
- Remove `checkUnknownSetting` (dead code, always `false`)
- Trim `npmConfigTypes` from ~127 to ~67 keys (remove unused npm config keys)
- Replace `rcOptions` iteration over all type keys with direct construction from defaults + auth overlay
- Remove `rcOptionsTypes` parameter from `getConfig()` and its assembly chain

### Rename `rawConfig` to `authConfig`
- `rawConfig` was a confusing mix of auth data and general settings
- Non-auth settings are already on the typed `Config` object — stop duplicating them in `rawConfig`
- Rename `rawConfig` → `authConfig` across the codebase to clarify it only contains auth/registry data from `.npmrc`

### Remove `rawConfig` from non-auth consumers
- **Lifecycle hooks**: replace `rawConfig: object` with `userAgent?: string` — only user-agent was read
- **Fetchers**: remove unused `rawConfig` from git fetcher, binary fetcher, tarball fetcher, prepare-package
- **Update command**: use `opts.production/dev/optional` instead of `rawConfig.*`
- **`pnpm init`**: accept typed init properties instead of parsing `rawConfig`

### Add `nodeDownloadMirrors` setting
- New `nodeDownloadMirrors?: Record<string, string>` on `PnpmSettings` and `Config`
- Replaces the `node-mirror:<channel>` pattern that was stored in `rawConfig`
- Configured in `pnpm-workspace.yaml`:
  ```yaml
  nodeDownloadMirrors:
    release: https://my-mirror.example.com/download/release/
  ```
- Remove unused `rawConfig` from deno-resolver and bun-resolver

### Refactor `pnpm config get/list`
- New `configToRecord()` builds display data from typed Config properties on the fly
- Excludes sensitive internals (`authInfos`, `sslConfigs`, etc.)
- Non-types keys (e.g., `package-extensions`) resolve through `configToRecord` instead of direct property access
- Delete `processConfig.ts` (replaced by `configToRecord.ts`)

### Pre-push hook improvement
- Add `compile-only` (`tsgo --build`) to pre-push hook to catch type errors before push
2026-04-04 20:33:43 +02:00
sotanengel
c7203b99ad feat!: set default minimumReleaseAge to 1 day (1440 minutes) (#11158)
set default minimumReleaseAge to 1 day

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-04-04 13:26:22 +02:00
Zoltan Kochan
421d120972 perf: use If-Modified-Since for conditional metadata fetches (#11161)
Before fetching package metadata from the registry, stat the local cache
file and send its mtime as an If-Modified-Since header. If the registry
returns 304 Not Modified, read the local cache instead of downloading
the full response body. This saves bandwidth and latency for packages
whose metadata hasn't changed since the last fetch.

Registries that don't support If-Modified-Since simply return 200 as
before, so there is no behavior change for unsupported registries.
2026-04-01 12:39:13 +02:00
Zoltan Kochan
6c480a4375 perf: replace node-fetch with undici (#10537)
Replace node-fetch with native undici for HTTP requests throughout pnpm.

Key changes:
- Replace node-fetch with undici's fetch() and dispatcher system
- Replace @pnpm/network.agent with a new dispatcher module in @pnpm/network.fetch
- Cache dispatchers via LRU cache keyed by connection parameters
- Handle proxies via undici ProxyAgent instead of http/https-proxy-agent
- Convert test mocking from nock to undici MockAgent where applicable
- Add minimatch@9 override to fix ESM incompatibility with brace-expansion
2026-03-29 12:44:00 +02:00
Zoltan Kochan
d6b8e281b6 chore: use pn instead of pnpm (#11124) 2026-03-28 11:55:51 +01:00
Alessio Attilio
d3d6938414 feat: add native view/info/show/v command (#11064)
* feat: add native view/info command

* test: add unit tests for native view command

* fix(view): support ranges, aliases, and tags

* chore: update lockfile and tsconfig

* refactor(view): reuse pickPackageFromMeta from npm-resolver

- Share version resolution logic with the npm-resolver instead of
  reimplementing tag/range/version matching in the view command.
- Export pickPackageFromMeta and pickVersionByVersionRange from
  @pnpm/resolving.npm-resolver.
- Remove redundant double HTTP fetch (metadata already contains all
  version data).
- Remove duplicate author/repository fields from PackageInRegistry
  (already inherited from BaseManifest).
- Consolidate four changesets into one.
- Revert unrelated .gitignore change.
- Drop direct semver dependency from deps.inspection.commands.

* refactor(view): reuse fetchMetadataFromFromRegistry from npm-resolver

Use the npm-resolver's fetchMetadataFromFromRegistry instead of
hand-rolled fetch logic. This fixes:
- Broken URL encoding for scoped packages (@scope/pkg)
- Missing auth header, proxy, SSL, and retry config
- Duplicated fetch + error handling code

Also pass proper Config options (rawConfig, userAgent, SSL, proxy,
retry, timeout) through to createFetchFromRegistry and
createGetAuthHeaderByURI so the view command works with private
registries and corporate proxies.

* test(view): improve test coverage for view command

Add tests for:
- non-registry spec rejection (git URLs)
- no matching version error
- version range resolution (^1.0.0)
- dist-tag resolution (latest)
- nested field selection (dist.shasum)
- field selection with --json
- text output format (header, dist section, dist-tags)
- scoped package lookup (@pnpm.e2e/pkg-with-1-dep)
- deps count / deps: none in header
- object field rendering as JSON

* revert: undo rename of @pnpm/resolving.registry.types

The rename from @pnpm/resolving.registry.types to
@pnpm/registry.types (and the move from resolving/registry/types/
to registry/types/) is a separate refactoring concern unrelated to
the view command. Revert all rename-related changes.

Keep the legitimate type additions to PackageInRegistry:
maintainers, contributors, and dist.unpackedSize.

* revert: restore pnpm-workspace.yaml (remove registry/* glob)

* fix(view): handle edge cases in formatBytes and unpackedSize

- Use explicit null check for unpackedSize so 0 B is still rendered
- Add TB/PB units and clamp index to prevent undefined output

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-03-27 19:01:10 +01:00
Zoltan Kochan
263a8bce95 feat: add pnpm peers check command (#11061)
Adds a `--check-peers` flag to `pnpm list` that detects unmet and
missing peer dependency issues by reading the lockfile. This allows
users to check for peer dependency problems without triggering a
full resolution, which is especially useful in CI or after pulling
a lockfile from another developer.

Closes #7087
2026-03-23 10:31:09 +01: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
Zoltan Kochan
421ceac0b3 chore: compile pnpm CLI bundle before tests that use it (#11059)
Packages whose tests spawn the local pnpm CLI (pnpm/bin/pnpm.mjs) need
the bundle (pnpm/dist/pnpm.mjs) to exist. Add `pnpm --filter pnpm run
compile` to their test scripts so the bundle is built before tests run.
2026-03-22 10:56:36 +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
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
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
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
Zoltan Kochan
5d5818e44f style: enforce node: protocol for builtin imports (#10951)
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.
2026-03-13 07:59:51 +01:00
Zoltan Kochan
1c8c4e49f5 style: add eslint-plugin-simple-import-sort (#10947)
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
2026-03-13 02:02:38 +01:00
Zoltan Kochan
a8f016ca59 feat: store config deps and package manager integrities in pnpm-lock.env.yaml (#10912)
## Summary

Store config dependency and package manager integrity info in a separate `pnpm-lock.env.yaml` lockfile instead of inlining it in `pnpm-workspace.yaml`. The workspace manifest now contains only clean version specifiers for `configDependencies`, while the resolved versions, integrity hashes, and tarball URLs are recorded in the new env lockfile.

### Key changes

- **New `pnpm-lock.env.yaml` lockfile**: Uses the standard lockfile format (`importers`, `packages`, `snapshots`) to store resolved config dependencies and package manager dependencies with integrity hashes and tarball URLs.
- **Automatic migration**: Projects using the old inline-hash format in `pnpm-workspace.yaml` are automatically migrated on install.
- **Global Virtual Store (GVS) for version switching**: When switching pnpm versions via the `packageManager` field, pnpm is installed to the global virtual store (`$STORE_DIR/links/`) instead of `globalPkgDir`, reusing the content-addressable store for deduplication.
- **Self-update uses headless install**: `pnpm self-update` performs frozen headless installs using integrity hashes from the env lockfile, then links bins to `PNPM_HOME`.
- **`packageManagerDependencies`**: The env lockfile also stores resolved `packageManagerDependencies` during version switching and self-update.
- **`@pnpm/exe` support**: Replicates `@pnpm/exe`'s postinstall script (linking platform-specific binaries) since install scripts are disabled.
- **`pnpm setup` refactored**: Uses `pnpm add -g` instead of copying the CLI binary directly.
- **Extracted `toLockfileResolution`** to `@pnpm/lockfile.utils` and **deduplicated `iteratePkgMeta`** into `@pnpm/calc-dep-state`.
- **Removed unused `@pnpm/tools.path` package**.
2026-03-11 00:39:37 +01:00
Zoltan Kochan
aeb06caae9 refactor: simplify patchedDependencies lockfile format (#10911)
* refactor: simplify patchedDependencies lockfile format to map selectors to hashes

Remove the `path` field from patchedDependencies in the lockfile, changing the
format from `Record<string, { path: string, hash: string }>` to
`Record<string, string>` (selector → hash). The path was never consumed from
the lockfile — patch file paths come from user config, not the lockfile.

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

* fix: migrate old patchedDependencies format when reading lockfile

When reading a lockfile with the old `{ path, hash }` format for
patchedDependencies, extract just the hash string. This ensures
backwards compatibility with existing lockfiles.

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

* fix: carry patchFilePath through patch groups for runtime patch application

The previous commit removed `path` from the lockfile format but also
accidentally dropped it from the runtime PatchInfo type. This broke
patch application since `applyPatchToDir` needs the file path.

- Add optional `patchFilePath` to `PatchInfo` for runtime use
- Build patch groups with resolved file paths in install
- Fix `build-modules` to use `patchFilePath` instead of `file.path`
- Fix `calcPatchHashes` call site in `checkDepsStatus` (extra arg)

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

* fix: update remaining references to old PatchFile type

- Update getPatchInfo tests to use { hash, key } instead of { file, key }
- Fix createDeployFiles to handle patchedDependencies as hash strings
- Fix configurationalDependencies test assertion

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

* fix: throw when patch exists but patchFilePath is missing

Also guard against undefined patchedDependencies entry when
ignorePackageManifest is true.

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

* fix: don't join lockfileDir with already-absolute patch file paths

opts.patchedDependencies values are already absolute paths, so
path.join(opts.lockfileDir, absolutePath) created invalid doubled
paths like /project/home/runner/work/pnpm/...

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

* fix: use path.resolve for patch file paths and address Copilot review

- Use path.resolve instead of path.join to correctly handle both
  relative and absolute patch file paths
- Use PnpmError instead of plain Error for missing patch file path
- Only copy patchedDependencies to deploy output when manifest
  provides the patch file paths

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

* fix: pass rootProjectManifest in deploy patchedDependencies test

The test was missing rootProjectManifest, so createDeployFiles could
not find the manifest's patchedDependencies to propagate to the
deploy output.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 19:26:48 +01:00
Brandon Cheng
01914345d5 build: enable @typescript-eslint/no-import-type-side-effects (#10630)
* build: enable `@typescript-eslint/no-import-type-side-effects`

* build: disable `@typescript-eslint/consistent-type-imports`

* chore: apply fixes for `no-import-type-side-effects`

pnpm exec eslint "**/src/**/*.ts" "**/test/**/*.ts" --fix
2026-03-08 00:02:48 +01:00
Zoltan Kochan
cd743ef57f fix(gvs): engine-agnostic hashes and build failure recovery (#10846)
* feat(calc-dep-state): use allowBuilds to compute engine-agnostic GVS hashes

Use the allowBuilds config to determine which packages need ENGINE_NAME
in their GVS hash. Packages that are not allowed to build (and don't
transitively depend on packages that are) now get engine-agnostic hashes,
so they survive Node.js upgrades and architecture changes without
re-import.

Closes #10837

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

* feat(modules-yaml): persist allowBuilds and re-link GVS on change

Store the allowBuilds config in modules.yaml so that when it changes
between installs, the headless installer detects the difference and
re-processes all packages with updated GVS hashes.

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

* refactor: deduplicate computeBuiltDepPaths into iterateHashedGraphNodes

Move builtDepPaths computation inside iterateHashedGraphNodes, which now
accepts an AllowBuild function instead of a precomputed Set. This
eliminates duplicate logic in iteratePkgsForVirtualStore and
resolve-dependencies.

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

* fix(gvs): recover from failed or interrupted builds using .pnpm-needs-build marker

When a GVS package needs building, a .pnpm-needs-build marker file is added
to the filesMap before import. The import pipeline treats it as a regular file,
so it's atomically included in the staged directory and renamed with the
package. On the next install, GVS fast paths detect the marker and force a
re-fetch/re-import/re-build.

On build success, the marker is removed. On build failure, the entire hash
directory is removed so the next install starts fresh.

The marker is only checked for packages that are allowed to build (via
allowBuild), minimizing filesystem operations. It is also skipped when cached
side effects will be applied, since the package is already built.

Closes #10837

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

* fix: remove .pnpm-needs-build marker before uploading side effects

Move the marker removal before the side effects upload so the marker
file is not included in the side effects diff. Add a test assertion
that verifies the marker does not appear in the cached side effects.

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

* fix(calc-dep-state): return undefined from computeBuiltDepPaths when allowBuild is not configured

Previously, computeBuiltDepPaths returned an empty Set when allowBuild
was undefined, causing all GVS hashes to become engine-agnostic even
without allowBuilds configured. Now the function is only called when
allowBuild is provided, and iterateHashedGraphNodes avoids materializing
the iterator when it's not needed.

Also restore upfront filtering in extendGraph so non-GVS installs only
hash runtime dep paths, and only pass allowBuild when GVS is on.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 19:03:39 +01:00
Victor Sumner
97f049fb6a fix: skip re-importing packages when global virtual store is warm (#10709)
* fix: skip re-importing packages when global virtual store is warm

When node_modules is deleted but the global virtual store directories
survive, pnpm previously re-fetched every package because the skip
logic required currentLockfile to be present. Add a fast-path that
checks pathExists(dir) for GVS directories even when currentLockfile
is missing, since the GVS directory hash encodes engine, integrity,
and full dependency subgraph.

* fix: remove includeUnchangedDeps guard from GVS fast-path

The includeUnchangedDeps flag is true whenever currentHoistPattern
differs from the desired hoistPattern. After deleting node_modules,
currentHoistPattern is always undefined (read from .modules.yaml),
so the flag is always true when hoisting is configured — defeating
the optimization in the exact scenario it targets.

The guard is unnecessary because the fast-path only skips fetch/import
(fetchResponse = {}), not graph inclusion. The package is still added
to the graph with children populated, so hoisting recalculation works.

* perf: add GVS warm reinstall benchmark scenario

Adds benchmark 6: frozen lockfile reinstall with a warm global virtual
store after deleting node_modules. This measures the reattach fast-path
where all packages are skipped (no fetch/import) because their GVS
hash directories already exist.

* fix: use proper types in fetchPackage spy to pass tsgo strict checks
2026-02-27 22:51:08 +01:00
Zoltan Kochan
03c502c1a0 fix: detect overrides and other lockfile-affecting setting changes in optimisticRepeatInstall (#10654)
* 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.
2026-02-20 14:00:25 +01:00
Zoltan Kochan
e18a879d72 feat!: drop Node.js 22.12 support 2026-02-18 14:54:09 +01:00
Brandon Cheng
1a5b5beea2 build: replace ts-jest with simple transformer (#10579)
* test: use `import type` in more places

Several tests are failing because a module isn't being mocked. This is
due to the mocked module being imported before the mock being set up.

Switching to `import type` should elide the import fully.

* build: replace ts-jest with simple transformer

* chore: remove `ts-jest`

* chore: remove babel dependencies from root project

* ci: use Node.js 22.13.0 (instead of 22.12.0)

Node.js 22.13.0 introduces the `stripTypeScriptTypes` function

* fix: copilot feedback
2026-02-09 11:35:22 +01:00
Zoltan Kochan
1b4df57a01 feat!: drop Node.js 20 and 21 support (#10569) 2026-02-08 19:16:24 +01:00
Alessio Attilio
312226cbf0 fix: skip local file: protocol dependencies during pnpm fetch (#10514)
This fixes an issue where pnpm fetch would fail in Docker builds when
local directory dependencies (file: protocol) were not available.

The fix adds an ignoreLocalPackages option that is passed from the fetch
command to skip local dependencies during graph building, since pnpm
fetch only downloads packages from the registry and doesn't need local
packages that won't be available in Docker builds.

close #10460
2026-02-06 17:28:39 +01:00
Zoltan Kochan
e3b35b6f37 style: update eslint to v9 (#10474) 2026-01-17 12:01:23 +01:00
Zoltan Kochan
a00f9e515c chore: use typescript-go (#10452) 2026-01-14 01:18:13 +01:00
Brandon Cheng
db72923b5c fix: forward $NODE_OPTIONS when running jest for debug terminals (#10364)
* fix: forward existing `$NODE_OPTIONS` when running jest

* chore: update `package.json` files for meta-updater changes

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2025-12-27 22:10:52 +01:00
Zoltan Kochan
394d88cf5e feat: enable injected local packages to work with global virtual store (#10366)
* feat: enable injected local packages to work with global virtual store

by leveraging `pkgLocationsByDepPath` for `file:` dependencies.

* fix: populate `pkgLocationsByDepPath` directly for directory dependencies in the graph builder

* refactor: store directory dependencies as a Map instead of an object

* refactor: improve file: dependency target directory resolution

by prioritizing `directoryDepsByDepPath` and providing a lockfile fallback.

* refactor: remove `pkgLocationsByDepPath` from hoisted dependency graph generation parameters

* test: fix

* test: fix

* refactor: simplify directory lookup for injected workspace packages

by directly using the dependency graph

* refactor: move extendProjectsWithTargetDirs to headless module and update imports

* refactor: make `directoryDepsByDepPath` required

in `LockfileToDepGraphOptions` and remove its nullish coalescing in headless

* refactor: directory dependency tracking

by renaming `directoryDepsByDepPath` to `injectionTargetsByDepPath`
and extracting related logic, and remove an unused export.

* docs: add changesets

* fix: implemented CR suggestions
2025-12-27 12:21:19 +01:00
Oren
ba065f6a8b fix(git-fetcher): block git dependencies from running prepare scripts unless allowed (#10288)
* fix(git-fetcher): block git dependencies from running prepare scripts unless allowed

* Update exec/prepare-package/src/index.ts

Co-authored-by: Zoltan Kochan <z@kochan.io>

* Also implement in gitHostedTarballFetcher

* refactor: move allowBuild function creation to the store manager

* refactor: pass allowBuild function to fetch function directly

* refactor: revert not needed changes and update changesets

* test: fix

* fix: implemented CR suggestions

* test: fix

* test: fix

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2025-12-09 18:25:07 +01:00
Zoltan Kochan
5f73b0f2b6 perf: always link runtimes from the global virtual store directory (#10233) 2025-12-01 14:27:18 +01:00
Trevor Burnham
38b8e357b5 feat: add custom resolvers and fetchers (#10246) 2025-11-30 14:19:04 +01:00
Brandon Cheng
69ebe38764 fix: throw a frozen lockfile error when catalogs change (#10231)
* fix: throw a frozen lockfile error when catalogs change

* fix: work around lockfile mismatch when installing `__fixtures__`

```
> @ step1 /home/runner/work/pnpm/pnpm/__fixtures__
> node ../pnpm/dist/pnpm.mjs install -rf --frozen-lockfile --no-shared-workspace-lockfile --no-link-workspace-packages

.                                        |  WARN  using --force I sure hope you know what you are doing
Scope: all 26 workspace projects
circular                                 | Progress: resolved 1, reused 0, downloaded 0, added 0
circular                                 |   +4 +
fixture                                  | Progress: resolved 1, reused 0, downloaded 0, added 0
fixture                                  |  +12 +
fixture-with-no-pkg-name-and-no-version  | Progress: resolved 1, reused 0, downloaded 0, added 0
fixture-with-no-pkg-name-and-no-version  |  +12 +
fixture-with-no-pkg-version              | Progress: resolved 1, reused 0, downloaded 0, added 0
fixture-with-no-pkg-version              |  +12 +
circular                                 | Progress: resolved 4, reused 0, downloaded 4, added 4, done
fixture                                  | Progress: resolved 12, reused 6, downloaded 6, added 12, done
fixture-with-no-pkg-name-and-no-version  | Progress: resolved 12, reused 0, downloaded 0, added 12, done
fixture-with-no-pkg-version              | Progress: resolved 12, reused 0, downloaded 0, added 12, done
general                                  | Progress: resolved 1, reused 0, downloaded 0, added 0
general                                  |  +13 +
has-2-outdated-deps                      | Progress: resolved 1, reused 0, downloaded 0, added 0
has-2-outdated-deps                      |   +2 +
undefined
/home/runner/work/pnpm/pnpm/__fixtures__/has-outdated-deps-using-catalog-protocol:
 ERR_PNPM_LOCKFILE_CONFIG_MISMATCH  Cannot proceed with the frozen installation. The current "catalogs" configuration doesn't match the value found in the lockfile

Update your lockfile using "pnpm install --no-frozen-lockfile"
```

close #9369
2025-11-26 01:09:37 +01:00
Zoltan Kochan
7e2910e70f chore(release): 11.0.0-alpha.0 2025-11-13 15:44:27 +01:00
Zoltan Kochan
3ce5f82bd7 Merge remote-tracking branch 'origin/main' into v11 2025-10-28 18:40:05 +01:00
Zoltan Kochan
49f03d14ee chore(release): 10.20.0 2025-10-28 17:35:21 +01:00
Zoltan Kochan
dab9abef5c Merge remote-tracking branch 'origin/main' into v11 2025-10-24 14:19:07 +02:00