1"use strict";(self.webpackChunk=self.webpackChunk||[]).push([["33173"],{26456(e,n,s){s.r(n),s.d(n,{assets:()=>c,contentTitle:()=>d,default:()=>p,frontMatter:()=>a,metadata:()=>i,toc:()=>l});var i=s(94824),r=s(91987),t=s(67008);let a={title:"pnpm 11.8",authors:"zkochan",tags:["release"],date:new Date("2026-06-18T00:00:00.000Z")},d,c={authorsImageUrls:[void 0]},l=[{value:"Minor Changes",id:"minor-changes",level:2},{value:"<code>pnpm install --dry-run</code>",id:"pnpm-install---dry-run",level:3},{value:"Node.js package maps",id:"nodejs-package-maps",level:3},{value:"SBOM improvements",id:"sbom-improvements",level:3},{value:"CLI behavior",id:"cli-behavior",level:3},{value:"Patch Changes",id:"patch-changes",level:2}];function o(e){let n={a:"a",code:"code",h2:"h2",h3:"h3",li:"li",p:"p",strong:"strong",ul:"ul",...(0,t.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsxs)(n.p,{children:["pnpm 11.8 adds install dry-run previews, Node.js package map generation, richer SBOM output, ",(0,r.jsx)(n.code,{children:"pnpm view"})," defaulting to the current package, and correct ",(0,r.jsx)(n.code,{children:"pnpm run --no-bail"})," exit codes. It also includes a config-dependency lockfile traversal fix and many install/update determinism fixes."]}),"\n",(0,r.jsx)(n.h2,{id:"minor-changes",children:"Minor Changes"}),"\n",(0,r.jsx)(n.h3,{id:"pnpm-install---dry-run",children:(0,r.jsx)(n.code,{children:"pnpm install --dry-run"})}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.code,{children:"pnpm install"})," now supports ",(0,r.jsx)(n.a,{href:"/cli/install#--dry-run",children:(0,r.jsx)(n.code,{children:"--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 ",(0,r.jsx)(n.code,{children:"node_modules"})," updates."]}),"\n",(0,r.jsxs)(n.p,{children:["A completed dry run exits with code 0, matching the preview behavior of ",(0,r.jsx)(n.code,{children:"npm install --dry-run"})," (",(0,r.jsx)(n.a,{href:"https://github.com/pnpm/pnpm/issues/7340",children:"#7340"}),")."]}),"\n",(0,r.jsx)(n.h3,{id:"nodejs-package-maps",children:"Node.js package maps"}),"\n",(0,r.jsxs)(n.p,{children:["pnpm now generates ",(0,r.jsx)(n.code,{children:"node_modules/.package-map.json"})," during isolated and hoisted installs. The map describes how packages should resolve through the installed ",(0,r.jsx)(n.code,{children:"node_modules"})," layout."]}),"\n",(0,r.jsxs)(n.p,{children:["The new ",(0,r.jsx)(n.a,{href:"/settings/node-modules#nodeexperimentalpackagemap",children:(0,r.jsx)(n.code,{children:"nodeExperimentalPackageMap"})})," setting injects the generated map into pnpm-managed Node.js script environments by adding Node's ",(0,r.jsx)(n.code,{children:"--experimental-package-map"})," option to ",(0,r.jsx)(n.code,{children:"NODE_OPTIONS"}),". The ",(0,r.jsx)(n.a,{href:"/settings/node-modules#nodepackagemaptype",children:(0,r.jsx)(n.code,{children:"nodePackageMapType"})})," setting chooses between ",(0,r.jsx)(n.code,{children:"standard"})," maps, which expose declared dependencies only, and ",(0,r.jsx)(n.code,{children:"loose"})," maps, which also map packages reachable through the installed layout."]}),"\n",(0,r.jsx)(n.h3,{id:"sbom-improvements",children:"SBOM improvements"}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.a,{href:"/cli/sbom",children:(0,r.jsx)(n.code,{children:"pnpm sbom"})})," can now write SBOMs to files with ",(0,r.jsx)(n.a,{href:"/cli/sbom#--out-path",children:(0,r.jsx)(n.code,{children:"--out"})}),", generate one SBOM per selected workspace package with ",(0,r.jsx)(n.a,{href:"/cli/sbom#--split",children:(0,r.jsx)(n.code,{children:"--split"})}),", and use the selected package's metadata as the root component when ",(0,r.jsx)(n.code,{children:"--filter"})," selects a single package."]}),"\n",(0,r.jsxs)(n.p,{children:["CycloneDX output now marks components reachable only through ",(0,r.jsx)(n.code,{children:"devDependencies"})," with ",(0,r.jsx)(n.code,{children:'scope: "excluded"'})," and the ",(0,r.jsx)(n.code,{children:"cdx:npm:package:development"})," property. Runtime components, including installed optional dependencies, keep the default required scope."]}),"\n",(0,r.jsx)(n.h3,{id:"cli-behavior",children:"CLI behavior"}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.code,{children:"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 (",(0,r.jsx)(n.a,{href:"https://github.com/pnpm/pnpm/issues/8013",children:"#8013"}),")."]}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.code,{children:"pnpm view"})," can now be run without a package name. In that case it searches upward for the nearest project manifest and uses its ",(0,r.jsx)(n.code,{children:"name"})," field."]}
1),"\n",(0,r.jsx)(n.h2,{id:"patch-changes",children:"Patch Changes"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"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 ",(0,r.jsx)(n.code,{children:"configDependencies"})," names or versions to write outside ",(0,r.jsx)(n.code,{children:"node_modules/.pnpm-config"})," or the store. Names must be valid npm package names, and versions must be exact semver versions. See ",(0,r.jsx)(n.a,{href:"https://github.com/pnpm/pnpm/security/advisories/GHSA-qrv3-253h-g69c",children:"GHSA-qrv3-253h-g69c"}),"."]}),"\n",(0,r.jsxs)(n.li,{children:["Fixed ",(0,r.jsx)(n.code,{children:"pnpm update"})," overriding the version range policy of a named catalog whose name parses as a version, such as ",(0,r.jsx)(n.code,{children:"catalog:express4-21"})," (",(0,r.jsx)(n.a,{href:"https://github.com/pnpm/pnpm/issues/10321",children:"#10321"}),")."]}),"\n",(0,r.jsxs)(n.li,{children:["Fixed ",(0,r.jsx)(n.code,{children:"link:"})," workspace protocol dependencies switching to ",(0,r.jsx)(n.code,{children:"file:"})," after ",(0,r.jsx)(n.code,{children:"pnpm rm"})," when ",(0,r.jsx)(n.code,{children:"injectWorkspacePackages: true"})," is set and the target workspace dependency has its own dependencies."]}),"\n",(0,r.jsxs)(n.li,{children:["Stopped warning about matching ",(0,r.jsx)(n.code,{children:"packageManager"})," and ",(0,r.jsx)(n.code,{children:"devEngines.packageManager"})," values when both pin the same package manager, version, and integrity hash (",(0,r.jsx)(n.a,{href:"https://github.com/pnpm/pnpm/issues/12028",children:"#12028"}),")."]}),"\n",(0,r.jsxs)(n.li,{children:["Fixed interactive progress output leaving characters behind when external processes write to the terminal, such as SSH passphrase prompts (",(0,r.jsx)(n.a,{href:"https://github.com/pnpm/pnpm/issues/12350",children:"#12350"}),")."]}),"\n",(0,r.jsxs)(n.li,{children:["Fixed ",(0,r.jsx)(n.code,{children:"pnpm approve-builds"})," missing packages whose build approval was revoked and then re-added (",(0,r.jsx)(n.a,{href:"https://github.com/pnpm/pnpm/issues/12221",children:"#12221"}),")."]}),"\n",(0,r.jsxs)(n.li,{children:["Skipped the redundant Windows warning about an existing ",(0,r.jsx)(n.code,{children:"node.exe"})," when it already matches the target (",(0,r.jsx)(n.a,{href:"https://github.com/pnpm/pnpm/issues/12203",children:"#12203"}),")."]}),"\n",(0,r.jsxs)(n.li,{children:["Fixed macOS Gatekeeper blocking native binaries imported from the store by removing the ",(0,r.jsx)(n.code,{children:"com.apple.quarantine"})," extended attribute from native binaries after import (",(0,r.jsx)(n.a,{href:"https://github.com/pnpm/pnpm/issues/11056",children:"#11056"}),")."]}),"\n",(0,r.jsxs)(n.li,{children:["Fixed ",(0,r.jsx)(n.code,{children:"optimisticRepeatInstall"}),' incorrectly reporting "Already up to date" when only ',(0,r.jsx)(n.code,{children:"pnpm-lock.yaml"})," changed, and fixed the same checks for git branch lockfiles (",(0,r.jsx)(n.a,{href:"https://github.com/pnpm/pnpm/issues/12100",children:"#12100"}),")."]}),"\n",(0,r.jsxs)(n.li,{children:["Fixed recursive updates that mix transitive dependency patterns with direct dependency selectors, such as ",(0,r.jsx)(n.code,{children:'pnpm up -r "@babel/core" uuid'})," (",(0,r.jsx)(n.a,{href:"https://github.com/pnpm/pnpm/issues/12103",children:"#12103"}),")."]}),"\n",(0,r.jsxs)(n.li,{children:["Registered ",(0,r.jsx)(n.code,{children:"pnpm update --no-save"})," in CLI help and option parsing."]}),"\n",(0,r.jsxs)(n.li,{children:["Fixed ",(0,r.jsx)(n.code,{children:"pnpm import"})," for Yarn v2 lockfiles when ",(0,r.jsx)(n.code,{children:"js-yaml"})," v4 is installed."]}),"\n",(0,r.jsxs)(n.li,{children:["Fixed repeated reinstall prompts when ",(0,r.jsx)(n.code,{children:"enableGlobalVirtualStore"})," is enabled by keeping the virtual store directory recorded during post-install builds aligned with the install step (",(0,r.jsx)(n.a,{href:"https://github.com/pnpm/pnpm/issues/12307",children:"#12307"}),")."]}),"\n",(0,r.jsxs)(n.li,{children:["Documented the ",(0,r.jsx)(n.code,{children:"--cpu"}),", ",(0,r.jsx)(n.code,{children:"--os"}),", and ",(0,r.jsx)(n.code,{children:"--libc"})," flags in ",(0,r.jsx)(n.code,{children:"pnpm install --help"})," (",(0,r.jsx)(n.a,{href:"https://github.com/pnpm/pnpm/issues/12359",children:"#12359"}),")."]}),"\n",(0,r.jsxs)(n.li,{children:["Avoided reading ",(0,r.jsx)(n.code,{children:"README.md"})," from disk during publish when the publish manifest already provides a ",(0,r.jsx)(n.code,{children:"readme"})," field."]}),"\n",(0,r.jsxs)(n.li,{children:["Fixed ",(0,r.jsx)(n.code,{children:"pnpm peers check"})," rejecting loose peer dependency ranges that the installed peer satisfies (",(0,r.jsx)(n.a,{href:"https://github.com/pnpm/pnpm/issues/12149",children:"#12149"}),")."]}),"\n",(0,r.jsxs)(n.li,{children:["Preserved ",(0,r.jsx)(n.code,{children:"workspace:"})," dependencies that point at local paths during ",(0,r.jsx)(n.code,{children:"pnpm update"})," (",(0,r.jsx)(n.a,{href:"https://github.com/pnpm/pnpm/issues/3902",children:"#3902"}),")."]}),"\n",(0,r.jsx)(n.li,{children:"Fixed a lockfile non-convergence case where incremental installs could keep a duplicate transitive dependency that a fresh install would remove."}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"pnpm install"})," detects changes inside local ",(0,r.jsx)(n.code,{children:"file:"})," dependencies and local tarballs again, bypassing the optimistic fast path for those projects (",(0,r.jsx)(n.a,{href:"https://github.com/pnpm/pnpm/issues/11795",children:"#11795"}),")."]}),"\n",(0,r.jsxs)(n.li,{children:["Preserved the existing Node.js runtime version prefix when resolving ",(0,r.jsx)(n.code,{children:"node@runtime:<range>"})," to a concrete version."]}),"\n",(0,r.jsxs)(n.li,{children:["Shortened CAFS temporary package directories to leave room for lifecycle scripts that create IPC socket paths under ",(0,r.jsx)(n.code,{children:"TMPDIR"}),"."]}),"\n",(0,r.jsxs)(n.li,{children:["Reporter output for ",(0,r.jsx)(n.code,{children:"pnpm store"})," and ",(0,r.jsx)(n.code,{children:"pnpm config"})," subcommands now goes to stderr, so scripts can safely capture stdout."]}),"\n",(0,r.jsx)(n.li,{children:"Avoided relinking unchanged child dependencies and removed stale child links during warm installs."}),"\n",(0,r.jsxs)(n.li,{children:["Fixed lockfile churn where ",(0,r.jsx)(n.code,{children:"transitivePeerDependencies"})," could be dropped or shifted when a package participates in a dependency cycle (",(0,r.jsx)(n.a,{href:"https://github.com/pnpm/pnpm/issues/5108",children:"#5108"}),")."]}),"\n",(0,r.jsxs)(n.li,{children:["Fixed ",(0,r.jsx)(n.code,{children:"pnpm install"}),' reporting "Already up to date" after a catalog entry in ',(0,r.jsx)(n.code,{children:"pnpm-workspace.yaml"})," was reverted to a previous version (",(0,r.jsx)(n.a,{href:"https://github.com/pnpm/pnpm/issues/12418",children:"#12418"}),")."]}),"\n",(0,r.jsxs)(n.li,{children:["Kept lockfile ",(0,r.jsx)(n.code,{children:"overrides"})," that resolve through a catalog in sync when ",(0,r.jsx)(n.code,{children:"pnpm update"})," bumps the catalog entry."]}),"\n",(0,r.jsxs)(n.li,{children:["Fixed ",(0,r.jsx)(n.code,{children:"pnpm version --recursive"})," so it honors workspace selection instead of always bumping every workspace package (",(0,r.jsx)(n.a,{href:"https://github.com/pnpm/pnpm/issues/11348",children:"#11348"}),")."]}),"\n"]})]})}function p(e={}){let{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(o,{...e})}):o(e)}},67008(e,n,s){s.d(n,{R:()=>a,x:()=>d});var i=s(71763);let r={},t=i.createContext(r);function a(e){let n=i.useContext(t);return i.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function d(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:a(e.components),i.createElement(t.Provider,{value:n},e.children)}},94824(e){e.exports=JSON.parse('{"permalink":"/blog/releases/11.8","editUrl":"https://github.com/pnpm/pnpm.io/edit/main/blog/releases/11.8.md","source":"@site/blog/releases/11.8.md","title":"pnpm 11.8","description":"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.","date":"2026-06-18T00:00:00.000Z","tags":[{"inline":true,"label":"release","permalink":"/blog/tags/release"}],"readingTime":4.87,"hasTruncateMarker":true,"authors":[{"name":"Zoltan Kochan","title":"Lead maintainer of pnpm","url":"https://x.com/zkochan","
1socials":{"x":"https://x.com/zkochan","github":"https://github.com/zkochan"},"imageURL":"https://pbs.twimg.com/profile_images/2005604765028245504/SudRBVMH_400x400.jpg","key":"zkochan","page":null}],"frontMatter":{"title":"pnpm 11.8","authors":"zkochan","tags":["release"],"date":"2026-06-18T00:00:00.000Z"},"unlisted":false,"prevItem":{"title":"pnpm 11.9","permalink":"/blog/releases/11.9"},"nextItem":{"title":"pnpm 11.7","permalink":"/blog/releases/11.7"}}')}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.