跳至主要内容

pnpm 11.28

· 閱讀時間約 10 分鐘
Zoltan Kochan
pnpm 的首席維護者

pnpm 11.28 adds the forceIgnoresPlatform setting and pnpm update --peer, and brings a large batch of fixes from pnpm 12 to the JavaScript CLI: pnpm deploy, --filter, nodeLinker: hoisted, and custom modulesDir setups all behave better. It also carries security fixes for shell completion, bin shims on Nix, lifecycle scripts inside a custom modulesDir, and userAgent placeholders in pnpm-workspace.yaml.

Minor Changes​

forceIgnoresPlatform​

pnpm install --force installs every optional dependency, including those built for another os, cpu, or libc. The new forceIgnoresPlatform setting turns that part off, so --force still refetches packages and lifts engineStrict but skips optional dependencies for other platforms (#6133):

pnpm-workspace.yaml
forceIgnoresPlatform: false

The default stays true in pnpm 11. In pnpm 12, --force skips such dependencies by default.

pnpm update --peer​

pnpm update --peer also updates the ranges in peerDependencies, which pnpm update otherwise leaves alone (#8081):

pnpm update --peer react

Security​

  • pnpm no longer expands environment variables in a userAgent set in a project's pnpm-workspace.yaml. Such a userAgent is ignored. Before, pnpm sent the variable's value to the configured registry (#15415).
  • Packages inside a custom modulesDir are no longer treated as workspace projects. With a modulesDir such as vendor and a packages pattern such as **, a repeat install ran lifecycle scripts that allowBuilds had not approved (#15412).
  • On Nix, a dependency's bin named like a system utility such as sed can no longer redirect a POSIX bin shim or the pnpm, pn, pnpx, and pnx launchers. Install again to replace the shims already in node_modules (#14883).
  • Shell completion omits candidates containing control or invisible formatting characters, so package and script names can no longer inject completion records or terminal escape sequences.
  • pnpm runtime set and pnpm env use no longer run a script that another package installed under the name pnpm or pn as though it were pnpm.
  • pnpm store prune leaves a dlx cache root that is a symlink or Windows junction untouched.

Notable fixes​

Installing​

  • Requests to a server whose TLS certificate fails verification now fail at once, naming the certificate problem, instead of retrying for more than a minute (#9134).
  • Git dependencies fetched over SSH no longer appear to hang on a hidden passphrase or host key prompt: pnpm runs ssh in batch mode, so load a key that needs a passphrase into an SSH agent first (#2227). Committed submodules of git dependencies are now fetched (#1470).
  • bzip2-compressed tarballs can be installed (#6761).
  • The root project's preinstall script now runs before dependencies are resolved and linked, so a guard such as npx only-allow pnpm stops the install before node_modules is populated (#3760).
  • Installs that skip devDependencies, such as pnpm install --prod, no longer run pnpm:devPreinstall or prepare (#7065), and they no longer install a devDependency that only satisfies an optional peer of a production dependency (#15344).
  • pnpm install no longer adds allowBuilds placeholders to pnpm-workspace.yaml in CI or without a terminal (#11574).
  • pnpm install --engine-strict respects engines relaxed by readPackage hooks (#15482), and the project's pnpmfile now runs after the pnpmfiles of config dependency plugins (#9891).
  • pnpm install and pnpm run reinstall a project that was moved together with its node_modules (#9512), and relink a direct dependency whose link points to a missing target (#9758).
  • pnpm fetch also installs the pnpm version that pnpm-lock.yaml pins, so a later offline install can switch to it (#11808).
  • pnpm no longer crashes when TMPDIR, TEMP, or TMP points to a missing directory (#4960).

Resolving and linking​

  • A dependency that a package also declares as an optional peer, such as lightningcss in some vite builds, is now installed (#8912).
  • Removing an overrides entry re-resolves the packages it targeted (#4587).
  • With trustPolicy: no-downgrade, pnpm resolves the newest matching version that is not a trust downgrade instead of failing (#14176).
  • Ranges resolve to the newest matching version that is not deprecated (#15128).
  • pnpm add followed by pnpm remove of the same package leaves pnpm-lock.yaml unchanged; unrelated transitive dependencies no longer move (#11859).
  • pnpm install --frozen-lockfile succeeds when an optional dependency was unresolvable and skipped by the install that wrote the lockfile (#3960).
  • hoistWorkspacePackages works with nodeLinker: hoisted (#7553), and hoisted workspace projects are hoisted on every install, not only after node_modules is deleted (#3642).
  • A workspace package's bin that a build script creates after install is now linked (#10007).

Workspaces and filtering​

  • --filter "[<since>]" compares against the merge base with <since> (#9907), and selects projects that use a changed catalog entry (#8718).
  • --filter selectors are applied in order, so a later inclusion can re-include a project that an earlier exclusion removed (#9354).
  • pnpm --filter <project> <command> and pnpm -r <command> run a command installed in the projects' dependencies when none of them has a script by that name (#10151).
  • Workspace projects reached through a symlink are installed (#1044).
  • A catalog: dependency that points at a workspace project counts as a workspace dependency for ordering and filtering (#15587).
  • workspace: ranges match projects whose version is not valid semver, such as 1.0 (#4567), or carries build metadata (#6483).
  • pnpm install --filter with nodeLinker: hoisted installs only the selected projects' dependencies (#8882).
  • pnpm warns when a workspace install covers a project with its own pnpm-workspace.yaml, whose settings do not apply (#11724).

Custom modulesDir​

  • pnpm run, pnpm exec, pnpm version hooks, and install-time lifecycle scripts find commands in a custom modulesDir, and tools installed there can load CommonJS plugins installed next to them (#3604). See extendNodePath.
  • A repeat install in a workspace with a custom modulesDir takes the up-to-date fast path.

Adding, updating, and removing​

  • pnpm remove runs the project's own preuninstall, uninstall, and postuninstall scripts (#3276), and pnpm remove -r fails before changing any manifest if a dependency is absent from every selected project (#2319).
  • pnpm unlink removes the link: dependency that pnpm link <dir> added (#4219).
  • pnpm add saves the requested exact version (#6040), writes package.json before lifecycle scripts run (#8627), and keeps a specifier a readPackage hook rewrote (#15156).
  • pnpm update keeps ranges such as <= 3.0.0 that the new version still satisfies (#6714), updates auto-installed peers by name (#10486), and moves a package off a locked version the registry no longer serves (#9953).
  • minimumReleaseAgeExcludePrune and trustPolicyExcludePrune work with sharedWorkspaceLockfile: false (#14612).
  • pnpm import converts Yarn patch: dependencies into patchedDependencies (#10278).

Scripts​

  • A script run without a terminal ends when pnpm is killed, as Playwright's webServer does (#15555).
  • pnpm exec and pnpm dlx set npm_execpath, INIT_CWD, npm_node_execpath, and NODE (#7037), and pnpm exec sets PWD (#1550).
  • pnpm restart runs stop and start when there is no restart script (#4750).
  • pnpm install-test accepts --no-bail (#3777).
  • pnpm dlx keeps a separate cache per Node.js major version (#8611).
  • A runtime: range containing || or a space installs the runtime instead of the npm package of the same name (#14817).

Publishing, packing, and deploying​

  • pnpm publish resolves workspace: dependencies without node_modules (#6567) and honors publishConfig["@scope:registry"] (#12071).
  • pnpm pack includes bundled dependencies with the isolated linker (#1643), keeps symlinks inside the package (#8208), and preserves executable permissions.
  • pnpm deploy copies the root packageManager field (#9079), puts the virtual store at virtualStoreDir (#8787), and no longer runs the deployed project's prepare script (#7282).

Configuration and registries​

  • .npmrc files support npm's ${VAR?} placeholder (#14404), and _auth tokens from the global config.yaml or pnpm_config__auth expand environment variables (#12828).
  • pnpm login logs back in to an existing user on registries without web login, such as verdaccio (#12055).
  • pnpm doctor checks the configured default registry (#15618).
  • pnpm preserves CRLF line endings when it modifies manifests.

Windows and WSL​

  • pnpm install in WSL waits out Windows file locks on /mnt/c and similar drives (#6155), and saving pnpm-lock.yaml on Windows retries while another process holds it (#9461).
  • pnpm add and pnpm update write file: and link: specifiers with forward slashes on Windows (#7497).
  • Node.js runtime resolution supports Windows ARM64 (#7123).
  • Bin shims run from Git Bash, MSYS2, or Cygwin pass NODE_PATH as Windows paths (#3360).

Inspecting dependencies​

  • pnpm audit honors --filter (#10982), lists a path from every affected project (#12200), and --fix=update fixes npm-aliased dependencies (#15155).
  • pnpm list and pnpm licenses list inside a workspace package list only that package unless --recursive or --filter is given (#14494, #5689).
  • pnpm -r outdated --json includes every outdated dependency when projects use different versions of the same package (#7693).
  • The ignored build scripts warning and the update notice print as plain lines in CI and non-terminal output (#9421).

For the complete list of changes, see the v11.28.0 release notes.