pnpm 12 RC 8
- packageImportMethod: auto now tries hardlinks before cloning on Linux for faster node_modules materialization from warm store
- pnpm audit --json output now returns patched_versions: null for advisories whose inferred patch is not available
- Reduced warm update overhead by limiting virtual-store bin linking and ignored-script build bookkeeping to packages materialized by the current install
- pnpm init now pins the exact pnpm version in the packageManager field instead of a caret range
- pnpm approve-builds now removes deprecated build configuration fields from pnpm-workspace.yaml when writing allowBuilds
- pnpm audit no longer reports patched versions that were never published or are deprecated
- pnpm audit --fix no longer adds minimumReleaseAgeExclude entries for versions that do not exist
- Re-fetch full registry metadata when minimumReleaseAge is enabled and an abbreviated packument's time map omits timestamps for some versions
- Config dependencies with inline integrity now take their tarball URL from the registry's packument instead of deriving it from the registry URL
- Files like license16.json are no longer treated as a package license when deciding if the workspace LICENSE file should be included
- node-linker=hoisted installs no longer produce broken layouts on graphs with version conflicts
From pnpm
Minor Changes
-
packageImportMethod: autonow tries hardlinks before cloning on Linux. A reflink materializes a new inode and copies extent bookkeeping inside the filesystem's metadata trees, where a hardlink is one directory entry — on btrfs this roughly halves the time an install spends materializingnode_modulesfrom a warm store. ext4 installs are unchanged (cloning was never supported there, soautoalready hardlinked), and macOS keeps clone-first, where APFSclonefileis the platform's cheap primitive. Cloning remains the fallback when the store refuses hardlinks, and remains available explicitly viapackageImportMethod: clone.This ships with pnpm 12 only: pnpm 11's importer deliberately keeps clone-first, since changing what the default materializes on disk is not a point-release change.
Patch Changes
-
pnpm approve-buildsnow removesonlyBuiltDependencies,onlyBuiltDependenciesFile,neverBuiltDependencies, andignoredBuiltDependenciesfrompnpm-workspace.yamlwhen it writesallowBuilds. Those settings were replaced byallowBuildsin pnpm 11 and silently ignored since, so a workspace migrated from pnpm 10 kept them around looking active. -
pnpm auditno longer reports a patched version that was never published or is deprecated. The inferred patched range (e.g.>=4.17.24from<=4.17.23) is now checked against the registry packument, and the report is corrected to the lowest non-deprecated published version that satisfies it (e.g.>=4.18.1when4.17.24does not exist and4.18.0is deprecated). When no published version satisfies the range, the report showsPatched versions: None. This also preventspnpm audit --fixfrom adding overrides orminimumReleaseAgeExcludeentries for patches that do not exist #13824.pnpm audit --fixandpnpm audit --fix updateno longer add aminimumReleaseAgeExcludeentry when the registry packument shows that the minimum patched version was never published. Previously such entries were written for versions that do not exist, which would have let a later publish of that version bypass theminimumReleaseAgegate #11563.The
--jsonoutput ofpnpm auditnow returnspatched_versions: nullfor advisories whose inferred patch is not available (never published, skipped, yanked, or deprecated), making it easier for tooling to distinguish "no fix available" from "fix available at version X". -
Re-fetch full registry metadata when
minimumReleaseAgeis enabled and an abbreviated packument'stimemap omits timestamps for some versions. This prevents mature versions from being filtered out and resolution from falling back to the lowest matching version pnpm/pnpm#13741. -
A config dependency carrying an inline integrity (the
<version>+<integrity>form, or the object form without atarball) now takes its tarball URL from the registry's packument instead of deriving it from the registry URL, so migrating one costs an extra metadata request. On a registry that serves tarballs from a path pnpm cannot derive, GitLab's group endpoint for one, installing such a config dependency failed with a 404 while the same package installed fine as a regular dependency #13765. -
Don't treat files like
license16.jsonas a package license when deciding if the workspace LICENSE file should be included in the packed package. -
Reduced warm update overhead by limiting virtual-store bin linking and ignored-script build bookkeeping to packages materialized by the current install.
-
pnpm initnow pins the exact pnpm version instead of a^range, and records it in thepackageManagerfield alongsidedevEngines.packageManager. Corepack reads onlypackageManagerand accepts nothing but an exact version, so it rejected the generatedpackage.jsonwith "expected a semver version" pnpm/pnpm#13969. A package created inside an existing workspace is still left unpinned — it follows the pin at the workspace root — and--no-init-package-managerstill scaffolds a manifest without any pin. In pnpm 12,pnpm initalso honorsinitTypeand its--init-typeflag, so the manifest it writes is the same one pnpm 11 writes. -
node-linker=hoistedinstalls no longer produce broken layouts on graphs with version conflicts. Three hoister fixes, aligning with@yarnpkg/nm(which the TypeScript CLI delegates to):- A version-conflicted package depended on by several packages kept its conflicting transitive dependencies under only one of the dependents, so requiring them through any other dependent resolved the wrong (root-hoisted) version — for example an ESM
parse-entities@4resolvingcharacter-entities-legacyv1 instead of v3, which crashes withERR_IMPORT_ATTRIBUTE_MISSINGon Node.js 22. Hoist decisions are now made per parent path on decoupled copies (ports upstream'sdecoupleGraphNode). - Peer-resolution variants of one package version now collapse onto a single copy (ports pnpm v11's
depPathByPkgIdmapping) instead of conflict-nesting a copy under every dependent — on peer-variant-heavy graphs (such asbit's) the old behavior also made the per-path walk explode. - Hoisting no longer shadows names a subtree resolves through an ancestor directory: a candidate is refused when a nearer ancestor holds a different version of its name (upstream's "filled by parent" scan) or when the hoist root's subtree already resolves that name from above (upstream's
usedDependenciesgate).
- A version-conflicted package depended on by several packages kept its conflicting transitive dependencies under only one of the dependents, so requiring them through any other dependent resolved the wrong (root-hoisted) version — for example an ESM
-
pnpm update --no-save <pkg>@<version>now keeps the manifest's declared importer specifier inpnpm-lock.yamlwhen the requested version satisfies that range, so a subsequent--frozen-lockfileinstall no longer fails because the lockfile records the requested version as the specifier. -
Reduced registry metadata requests during dependency resolution by reusing cached metadata when lockfile preferences prove that no uncached version can win pnpm/pnpm#13976.
-
Improved install performance: the store-index writer's shutdown now overlaps the install's final lockfile and
.modules.yamlwrites instead of extending the install's tail. -
A setting in the global
config.yamlthat pnpm does not read from that file, or that is written in kebab-case instead of camelCase, is now reported instead of being ignored silently. -
A forced full re-resolution (config changes the fast lockfile update cannot absorb, such as a changed override or
packageExtensions) no longer moves dependencies whose recorded versions still satisfy their ranges. The prior lockfile now pins each still-satisfied edge even when its recorded subtree cannot be reused wholesale, so open ranges like@types/node: "*"keep their locked versions instead of collapsing onto the highest locked version and churning the lockfile. -
Improved fresh resolution performance when package metadata is already cached.
-
Improved fresh installs by reusing the store index and verified-files cache during dependency materialization.
-
A runtime installed through
devEngines.runtimenow matches the host whensupportedArchitectureslists several platforms. Listingos: [darwin, linux]andcpu: [x64, arm64]used to install the runtime built for the first entry of each list, so a machine running Linux on arm64 got a macOS x64 Node.js that could not execute #13898. -
pnpm self-update <tag>no longer downgrades when the dist-tag points at the pnpm version already running and that version is younger thanminimumReleaseAge. The maturity cutoff moved the tag back to the previous mature release, sopnpm self-update next-12on v12.0.0-rc.4 switched to v12.0.0-rc.3. -
Improved install performance: large tarballs are now verified and extracted while they download, so the biggest packages — whose downloads finish last — no longer add their whole extraction to the end of the install.