v12.0.0-alpha.17
Breaking Changes📦 pnpmView on GitHub →
⚠ 2 breaking✨ 11 features🐛 7 fixes🔧 11 symbols
Summary
This release enhances pnpm's Git dependency handling, brings `pnpm list` and `pnpm why` to feature parity, and introduces new settings for workspace and dependency management. It also includes several bug fixes for build processes, dependency resolution, and engine checks.
⚠️ Breaking Changes
- The `enableModulesDir: false` setting now correctly prevents the creation of a `node_modules` directory, unless the global virtual store is enabled. Previously, this setting might not have been fully honored in all scenarios.
- Command shims now set `NODE_PATH` in a new way, reflecting the isolated `nodeLinker` with a hoist pattern. This might affect how Node.js resolves modules from shims if your project relies on specific `NODE_PATH` configurations. The new `extendNodePath: false` setting can be used to disable this behavior.
Migration Steps
- If you were relying on `node_modules` being created when `enableModulesDir: false`, you may need to adjust your setup if you are not using the global virtual store.
- Review your `NODE_PATH` configurations if your project relies on specific module resolution paths from command shims. Consider using `extendNodePath: false` if the new behavior is problematic.
✨ New Features
- Added support for alias-less Git dependency adds.
- Preserved locked Git commits during unrelated dependency changes.
- Reported Git package versions in install logs.
- `pnpm list` and `pnpm why` are now feature complete and behaviorally identical to the TypeScript CLI.
- `pnpm list` gained `--only-projects`, `--find-by` (finders declared in `.pnpmfile.cjs`), search by version range (`pnpm ls "foo@^2"`), subtree deduplication with `[deduped]` markers, peer/skipped annotations, the package-count summary, `--long` manifest details, resolved tarball URLs and absolute paths in `--json`/`--parseable` output, and `--depth` support for globally installed packages.
- `pnpm why` gained `--json`, `--parseable`, `--long`, `--prod`/`--dev`/`--no-optional`, `--find-by`, workspace project names in the reverse tree, dependency-field annotations, `[circular]`/`[deduped]` markers, peer-variant hashes, and the `Found N versions` summary.
- Added support for the `cleanupUnusedCatalogs` setting: when enabled, `pnpm add`, `pnpm update`, and `pnpm remove` drop catalog entries from `pnpm-workspace.yaml` that no workspace project references.
- Added the `--force` flag to `pnpm install` and `pnpm add`: optional dependencies whose `cpu` / `os` / `libc` / `engines` don't match the host are installed instead of skipped, and a forced install relinks packages that an earlier install already materialized.
- `sharedWorkspaceLockfile: false` is now supported by the install family: a workspace install runs one dedicated install per project, each with its own `pnpm-lock.yaml`, `node_modules`, and virtual store (a custom `virtualStoreDir` resolves per project), and `pnpm add` / `update` / `remove` in a project operate on that project's own lockfile.
- Added PnP install materialization.
- Fixed recovery from expired module caches and broken private lockfiles.
🐛 Bug Fixes
- Fixed recovery from interrupted dependency builds in the global virtual store.
- Made `pnpm fetch` populate the virtual store without linking dependencies into projects.
- Fixed workspace lifecycle ordering and bin linking across isolated and hoisted installs.
- Auto-installed peer dependencies wanted by multiple packages under distinct but compatible ranges now resolve through the ranges' semver intersection (`2` + `^2.2.0` install one provider matching `>=2.2.0 <3.0.0-0`), matching pnpm. Previously such peers were only auto-installed when every consumer declared the identical range or `autoInstallPeersFromHighestMatch` was enabled.
- `engineStrict` now fails the install when an incompatible package is reached through a regular dependency edge of an installable package, even if the package is also optionally reachable — matching pnpm. Packages reachable only through optional edges or skipped parents are still skipped.
- Engine checks (`engines.node` / `engines.pnpm`) now match npm-semver's `includePrerelease` semantics exactly: a prerelease version no longer satisfies a fully specified `>=` bound (`9.0.0-alpha.1` does not satisfy `>=9.0.0`), while still satisfying expanded ranges like `9`, `>=9`, and `^9.0.0`.
- Fixed a rare hang where `pnpm install` or `pnpm add` could wait forever: when two tasks fetched the same tarball concurrently, the waiting task could miss the downloader's completion notification and never wake up.