Skip to main content

pnpm 12.6

· 8 min read
Zoltan Kochan
Lead maintainer of pnpm

pnpm 12.6 ships with automatic dependency deduplication, relocatable node_modules, --save-types for installing @types/* packages alongside their counterparts, package.yaml manifest editing, and file: / link: protocols in catalogs.

Automatic deduplication

autoDedupe deduplicates compatible dependency versions during installation (#7258). Enable it in pnpm-workspace.yaml:

pnpm-workspace.yaml
autoDedupe: true

When a dependency appears at multiple versions and one satisfies every range, pnpm picks that version for the whole workspace. Frozen installs leave the lockfile unchanged.

Relocatable node_modules

pnpm install, pnpm run, and pnpm exec on macOS and Linux now reuse a node_modules directory and bin shims that moved or were copied together with their project (#6937). The first command after the move checks the tree and records the new location, so project commands in node_modules/.bin keep working.

--save-types

pnpm add --save-types saves available @types/* packages in devDependencies alongside registry dependencies (#3868):

pnpm add express --save-types
# saves express to dependencies, @types/express to devDependencies

Packages that declare bundled TypeScript types are skipped. Set saveTypes: true in pnpm-workspace.yaml to enable this by default.

package.yaml manifests

package.yaml manifests can now be updated by pnpm add, pnpm update, pnpm remove, pnpm pkg, pnpm link, pnpm set-script, and pnpm version (#2008). Existing comments and key order are preserved.

Catalog entries can now use the file: and link: protocols (#8642). A relative path or bare path in a catalog entry is measured from the directory holding pnpm-workspace.yaml:

pnpm-workspace.yaml
catalog:
my-lib: link:./packages/my-lib
my-tarball: file:./tarballs/foo.tgz

pnpm tasks status

pnpm tasks status lists running and waiting tasks in each concurrency group, and waiting tasks now take available slots in order of descending priority, with arrival order used only to break ties (#15208). If workspaces use different limits for the same group, a later task can take a free slot that earlier tasks cannot use. A package script named tasks takes precedence; use pnpm pm tasks status when that script exists.

macOS Time Machine exclusions

macosBackup.excludeModulesDir and macosBackup.excludeStoreDir on macOS can now exclude newly created modules, virtual-store, and package-store directories from Time Machine (#6440). Set either to true in global configuration or through the PNPM_CONFIG_MACOS_BACKUP_EXCLUDE_MODULES_DIR and PNPM_CONFIG_MACOS_BACKUP_EXCLUDE_STORE_DIR environment variables.

Smaller additions

  • pnpm add --tilde is now an alias for --save-prefix=~ (#12863). The Yarn -T shorthand is not supported.
  • progress setting and --no-progress option turn off dependency and download progress lines (#14065). Warnings, lifecycle output, and the dependency summary are still printed.
  • pnpm cache prune now also deletes registry metadata cache directories that this version of pnpm can no longer read (#15046). pnpm cache prune --dry-run lists what it would delete without removing anything.

Patch changes

Security

  • POSIX bin shims now take cygpath and wslpath from the system default path on Cygwin, MSYS2, and WSL2 so a dependency cannot redirect another package's shim (#14866).
  • pnpm install deprecation warnings no longer carry the text of a package's deprecation notice, naming only the deprecated package and version (#15099).
  • pnpm install and other commands now warn when environment variables in project .npmrc credentials are ignored (#15051).

Installing packages

  • pnpm install --frozen-lockfile now succeeds when an optional dependency was unresolvable and skipped by the install that wrote the lockfile (#3960).
  • pnpm install --frozen-lockfile no longer installs dependencies of projects removed from pnpm-workspace.yaml (#15248).
  • pnpm ci now empties node_modules before installing in a project that declares a clean script (#15276).
  • pnpm install --force now re-imports every package into the virtual store (#15030) and removes obsolete dependency links inside virtual-store packages when their dependencies change (#15039).
  • preinstall script for the root project now runs before dependencies are resolved and linked (#3760).
  • pnpm install --prod no longer downloads registry packages that only a devDependency reaches (#881).
  • pnpm install no longer hangs when a git dependency is fetched over SSH and ssh prompts for a passphrase or host key confirmation (#2227).
  • pnpm install now reuses an in-flight tarball download when another resolution of the same archive still needs its package.json (#15037).

Resolving and linking

  • pnpm install and pnpm update now resolve a dependency range to the newest matching version that is not deprecated (#15128).
  • pnpm add <pkg> without a version now uses the catalog entry when the workspace already catalogs that package (#14865).
  • pnpm.overrides entries written as a bare path are now measured from the directory holding pnpm-workspace.yaml (#11131).
  • pnpm peers check and strict peer dependency checks no longer reject compatible versions from named registries (#15225).
  • pnpm outdated and pnpm update --interactive --latest now include named-registry dependencies and preserve their registry prefix (#15226).
  • Sped up pnpm dedupe and pnpm install in projects with many convergence overrides by checking overrides concurrently (#15175).

Running scripts

  • pnpm run signal handling no longer delivers a redundant second SIGINT on Ctrl+C, and properly forwards termination signals without a terminal (#7374).
  • pnpm test now forwards --filter arguments to the test script when the option follows the shortcut (#15217).
  • pnpm deploy, pnpm rebuild, pnpm rb, and pnpm setup now prefer a package.json script of the same name (#14976).

Configuration

  • pnpm-workspace.yaml edits now preserve scalar YAML anchors and aliases (#8245).
  • pnpmfile configuration now loads a .js file as CommonJS or an ES module, following the nearest package.json (#15141).
  • readPackage hook changes now take added dependencies out of pnpm-lock.yaml and update dependencies when an existing lockfile is present (#3735, #15136).
  • pnpm now preserves CRLF line endings when modifying project manifests (#3529).
  • storeDir values loaded from global configuration now expand a leading ~/ to the user's home directory (#6560).

Windows

  • pnpm install on Windows now runs dependency build scripts from long global virtual store paths (#15111).
  • pnpm install across projects sharing a global virtual store on Windows no longer fails with Access is denied or file-exists errors (#15114, #15176).
  • pn, pnpx, pnx, and pnpm now run when Git Bash, MSYS2, or Cygwin launches them through a Windows path (#14884).
  • pnpm pipeline --watch now resolves Windows short paths so multiple path representations share the build cache (#15105).

Other fixes

  • pnpm remove now runs preuninstall, uninstall, and postuninstall scripts (#3276).
  • pnpm remove -r now fails before modifying manifests if any requested dependency is absent from all selected projects (#2319).
  • pnpm update --peer now updates ranges in peerDependencies (#8081).
  • pnpm update -g no longer reinstalls unchanged packages (#12002).
  • pnpm publish now allows a detached Git HEAD in CI environments (#5894).
  • pnpm store prune now removes unreferenced files from the content-addressable store (#3635) and expired pnpm dlx cache data (#15171).
  • pnpm deploy no longer triggers an install when running scripts in a read-only deployed filesystem (#11617).
  • pnpm sbom now validates SPDX identifiers before emitting them as CycloneDX license IDs (#14786).
  • pnpm --filter and pnpm -F shell completion now suggests workspace package names (#15216).
  • pnpm run shell completion now suggests package scripts (#15034).
  • pnpm --version no longer creates a temporary file in the project directory (#15264).

See the v12.6.0 release notes for the complete list of changes.