Skip to main content

pnpm 11.8

· 5 min read
Zoltan Kochan
Lead maintainer of pnpm

pnpm 11.8 adds install dry-run previews, Node.js package map generation, richer SBOM output, pnpm view defaulting to the current package, and correct pnpm run --no-bail exit codes. It also includes a config-dependency lockfile traversal fix and many install/update determinism fixes.

Minor Changes

pnpm install --dry-run

pnpm install now supports --dry-run. It runs full dependency resolution and reports what a real install would change, but writes nothing to disk: no lockfile, no manifests, and no node_modules updates.

A completed dry run exits with code 0, matching the preview behavior of npm install --dry-run (#7340).

Node.js package maps

pnpm now generates node_modules/.package-map.json during isolated and hoisted installs. The map describes how packages should resolve through the installed node_modules layout.

The new nodeExperimentalPackageMap setting injects the generated map into pnpm-managed Node.js script environments by adding Node's --experimental-package-map option to NODE_OPTIONS. The nodePackageMapType setting chooses between standard maps, which expose declared dependencies only, and loose maps, which also map packages reachable through the installed layout.

SBOM improvements

pnpm sbom can now write SBOMs to files with --out, generate one SBOM per selected workspace package with --split, and use the selected package's metadata as the root component when --filter selects a single package.

CycloneDX output now marks components reachable only through devDependencies with scope: "excluded" and the cdx:npm:package:development property. Runtime components, including installed optional dependencies, keep the default required scope.

CLI behavior

pnpm run --no-bail now continues running every matched script, but exits with a non-zero code if any script failed. Recursive runs already behaved this way; non-recursive runs now match them (#8013).

pnpm view can now be run without a package name. In that case it searches upward for the nearest project manifest and uses its name field.

Patch Changes

  • Security: Validate config dependency names and versions from the env lockfile before using them to build filesystem paths. A committed lockfile can no longer use traversal-shaped configDependencies names or versions to write outside node_modules/.pnpm-config or the store. Names must be valid npm package names, and versions must be exact semver versions. See GHSA-qrv3-253h-g69c.
  • Fixed pnpm update overriding the version range policy of a named catalog whose name parses as a version, such as catalog:express4-21 (#10321).
  • Fixed link: workspace protocol dependencies switching to file: after pnpm rm when injectWorkspacePackages: true is set and the target workspace dependency has its own dependencies.
  • Stopped warning about matching packageManager and devEngines.packageManager values when both pin the same package manager, version, and integrity hash (#12028).
  • Fixed interactive progress output leaving characters behind when external processes write to the terminal, such as SSH passphrase prompts (#12350).
  • Fixed pnpm approve-builds missing packages whose build approval was revoked and then re-added (#12221).
  • Skipped the redundant Windows warning about an existing node.exe when it already matches the target (#12203).
  • Fixed macOS Gatekeeper blocking native binaries imported from the store by removing the com.apple.quarantine extended attribute from native binaries after import (#11056).
  • Fixed optimisticRepeatInstall incorrectly reporting "Already up to date" when only pnpm-lock.yaml changed, and fixed the same checks for git branch lockfiles (#12100).
  • Fixed recursive updates that mix transitive dependency patterns with direct dependency selectors, such as pnpm up -r "@babel/core" uuid (#12103).
  • Registered pnpm update --no-save in CLI help and option parsing.
  • Fixed pnpm import for Yarn v2 lockfiles when js-yaml v4 is installed.
  • Fixed repeated reinstall prompts when enableGlobalVirtualStore is enabled by keeping the virtual store directory recorded during post-install builds aligned with the install step (#12307).
  • Documented the --cpu, --os, and --libc flags in pnpm install --help (#12359).
  • Avoided reading README.md from disk during publish when the publish manifest already provides a readme field.
  • Fixed pnpm peers check rejecting loose peer dependency ranges that the installed peer satisfies (#12149).
  • Preserved workspace: dependencies that point at local paths during pnpm update (#3902).
  • Fixed a lockfile non-convergence case where incremental installs could keep a duplicate transitive dependency that a fresh install would remove.
  • pnpm install detects changes inside local file: dependencies and local tarballs again, bypassing the optimistic fast path for those projects (#11795).
  • Preserved the existing Node.js runtime version prefix when resolving node@runtime:<range> to a concrete version.
  • Shortened CAFS temporary package directories to leave room for lifecycle scripts that create IPC socket paths under TMPDIR.
  • Reporter output for pnpm store and pnpm config subcommands now goes to stderr, so scripts can safely capture stdout.
  • Avoided relinking unchanged child dependencies and removed stale child links during warm installs.
  • Fixed lockfile churn where transitivePeerDependencies could be dropped or shifted when a package participates in a dependency cycle (#5108).
  • Fixed pnpm install reporting "Already up to date" after a catalog entry in pnpm-workspace.yaml was reverted to a previous version (#12418).
  • Kept lockfile overrides that resolve through a catalog in sync when pnpm update bumps the catalog entry.
  • Fixed pnpm version --recursive so it honors workspace selection instead of always bumping every workspace package (#11348).