Skip to content
verdict:

pnpm 12.17.0

notable

pending: summary queued

Detected 2026-10-31 at 02:31 UTC. Summaries usually land within a few hours; the full release notes are already below.

release-notesgithub.com/pnpm/pnpm · 228 lines
  1. pnpm 12.17.0 adds `pnpm peers check --global`, dependency-group selectors and wildcards in `overrides`, and runtime, package manager, Cargo, and Python updates in `pnpm outdated` and `pnpm update`. It also fixes files that other users could not read in Docker images, and repeat installs that missed changes to local dependencies.
  2. ### Minor Changes
  3. - `pnpm peers check` now accepts `--global` (`-g`) to check globally installed packages. After a global install or update, the peer dependency warning now suggests `pnpm peers check -g` [#17504](https://github.com/pnpm/pnpm/issues/17504).
  4. - `pnpm outdated` and `pnpm update` now inspect and update package manager and runtime versions in `devEngines.packageManager`, `packageManager`, `devEngines.runtime`, `engines.runtime`, and `runtime:` dependencies. With `cargo.enabled` or `python.enabled`, `pnpm outdated` and `pnpm update` also inspect and update Cargo dependencies in `Cargo.toml` and `Cargo.lock` and Python dependencies in `pyproject.toml` and `pylock.toml`.
  5. - `overrides` selectors now support `:prod`, `:optional`, and `:peer` pseudo-selectors that target a single dependency group. They also support `*` wildcard patterns on parent and target package names. An override with a non-range value, such as `npm:` or `file:`, no longer adds a peer dependency to `dependencies`. Removing an optional dependency with `"-"` now also removes the duplicate entry from `dependencies` [#5391](https://github.com/pnpm/pnpm/issues/5391).
  6. ### Patch Changes
  7. #### Installing packages
  8. - Files that pnpm creates in a project now get the permissions the umask gives any new file, such as `0644`. Before, several of them were readable only by the user who ran pnpm, which broke Docker images that run as a different user [#17435](https://github.com/orgs/pnpm/discussions/17435).
  9. The fix applies to:
  10. - `node_modules/.pnpm-workspace-state-v1.json`. A different user running `pnpm run` reinstalled the dependencies because it could not read this file.
  11. - `pnpm-workspace.yaml`
  12. - the files written by `pnpm deploy`
  13. - patch files from `pnpm patch-commit`
  14. - the lockfile restored by `pnpm dedupe --check`
  15. - the Cargo and Python files that pnpm writes
  16. When pnpm edits an existing file, the file keeps its permissions.
  17. - `pnpm install` no longer writes broken dependency links when the project's `node_modules` is a symlink to another directory, such as another project's `node_modules`. The install reported success, but the links it wrote did not resolve [#17448](https://github.com/pnpm/pnpm/issues/17448).
  18. - Commands installed with `pnpm add --global` now find the dependencies that pnpm hoists for their package. A package that loads a dependency it does not declare, such as a native addon's platform-specific package, failed with "Cannot find module". Reinstall such a package with `pnpm add --global` to update its existing command [#17503](https://github.com/pnpm/pnpm/issues/17503).
  19. - When pnpm switches to the version a project pins, it now installs that version's native binary for the current host, even if the project's `supportedArchitectures` excludes the host. Before, pnpm could not use the pinned version and kept running the installed one. pnpm also repairs a pinned version that an earlier install left without the host's binary.
  20. - With the global virtual store, a change to a dependency of one package in a dependency cycle now gives every package in that cycle a new directory. Before, the other packages kept their directories and still linked the old copy of the changed package, so the project could load two copies of it [#17466](https://github.com/pnpm/pnpm/issues/17466).
  21. Packages in a dependency cycle, and packages that depend on one, move to new global virtual store directories once after this update. Those that run a build miss the side-effects cache once.
  22. - With `nodeLinker: hoisted`, when two workspace projects need the same package version and it cannot be hoisted to the root, each project now keeps the copy installed in its own `node_modules`. Build scripts run in each of these copies. Before, the second project's copy was replaced with a symlink to the first project's, so every repeat install imported it again [#17498](https://github.com/pnpm/pnpm/issues/17498).
  23. - The Node.js `latest` selector now chooses the newest stable release [#17490](https://github.com/pnpm/pnpm/issues/17490).
  24. - Hardlinking from the store now stats each store file once per install. Since pnpm 12.8.0, a store file was stat'ed again for every package that links it. On an offline install of a large workspace, that added up to 15% system CPU [#17500](https://github.com/pnpm/pnpm/issues/17500).
  25. #### Repeat installs and local dependencies
  26. - `pnpm install --frozen-lockfile` no longer reports an outdated lockfile right after the install that wrote it. This happened when a pnpmfile `readPackage` hook or `packageExtensions` changed the dependencies or peer dependencies of a local directory dependency outside the workspace projects. It affected a `file:` dependency, an injected `workspace:../dir` dependency outside the workspace patterns, and an injected workspace package left out by `--filter` [#17447](https://github.com/pnpm/pnpm/issues/17447) [#17473](https://github.com/pnpm/pnpm/issues/17473).
  27. A repeat `pnpm install` now takes the fast path when `ignoredOptionalDependencies` removes an optional dependency of a `file:` directory dependency. It used to resolve again on every run.
  28. - `pnpm install` now resolves again after an edit to an injected `workspace:../dir` dependency outside the workspace patterns under a shared lockfile. It used to keep the lockfile unchanged. The next `pnpm install --frozen-lockfile` then failed [#17476](https://github.com/pnpm/pnpm/issues/17476).
  29. - `pnpm install` now resolves a local directory again after its `package.json` changes when an override or a package extension adds it to the dependencies of a registry package. `pnpm install --frozen-lockfile` fails on a lockfile that still records the old dependencies of such a directory [#17486](https://github.com/pnpm/pnpm/issues/17486).
  30. - With `injectWorkspacePackages` and per-project lockfiles, `pnpm install` now updates a project's lockfile when a local tarball that an injected workspace project depends on is replaced. The project's lockfile kept the old version and integrity before.
  31. - `pnpm install` now detects a change to a local tarball or directory that only a pnpmfile `readPackage` hook depends on. A repeat install reported "Already up to date" and kept the old contents before [pnpm/tasks#227](https://github.com/pnpm/tasks/issues/227).
  32. - `pnpm install` now relinks a `workspace:` or `link:` dependency whose link was deleted from `node_modules`. It used to report "Already up to date" and leave the link missing [#17485](https://github.com/pnpm/pnpm/issues/17485).
  33. - A repeat `pnpm install` reports "Already up to date" again when a pnpmfile `readPackage` hook changes the dependencies of a workspace project. Since 12.15.0, every such install ran in full [#17468](https://github.com/pnpm/pnpm/issues/17468).
  34. After an edit to a project's `package.json`, `pnpm install` and `verifyDepsBeforeRun` now run the pnpmfile's `readPackage` hook on the edited manifest before comparing it with the lockfile. They could report the project up to date when the hook rewrote the edited dependency to a specifier the lockfile did not record.
  35. #### Adding and updating dependencies
  36. - `pnpm update --latest` with package selectors now updates the dependencies of the selected packages within their declared ranges. Excluded packages and their dependencies keep their locked versions [#17444](https://github.com/pnpm/pnpm/issues/17444).
  37. - `pnpm add <pkg>@<tag>` now uses a catalog entry set to that same dist-tag. The dependency is saved as `catalog:` and the entry stays as written [pnpm/tasks#225](https://github.com/pnpm/tasks/issues/225).
  38. When `pnpm add` adds a new dist-tag such as `vue@legacy` to a catalog, the catalog entry records the exact version the tag points to. The same applies to a dependency added without a version when the `tag` setting names a dist-tag other than `latest`.
  39. - `pnpm add` now saves the resolved version range of an npm alias. For example, `pnpm add v2@npm:vue` and `pnpm add v2@npm:vue@^3.0.0` both save `npm:vue@^3.5.13`.
  40. #### Running scripts and commands
  41. - Programs that a script or `pnpm exec` command leaves running, such as a browser opened by the `open` package, now outlive pnpm exactly when they would outlive the command run without pnpm. Before, on Windows, a failed command, `Ctrl+C`, or a closed terminal ended all of them. pnpm still ends them when it is killed or crashes.
  42. When a recursive or parallel run bails, the scripts still running now receive `SIGTERM` on Unix and are killed only if they are still running two seconds later. On Windows, everything those scripts started is ended, including detached processes whose parent has already exited. Before, a bail killed them at once [#17465](https://github.com/pnpm/pnpm/issues/17465).
  43. - On Windows, the shell shim of a `.cmd` or `.bat` bin now runs it under Git Bash when MSYS argument conversion is off, such as with `MSYS_NO_PATHCONV=1` or `MSYS2_ARG_CONV_EXCL='*'`. The shim used to start an interactive cmd.exe prompt [#17480](https://github.com/pnpm/pnpm/issues/17480).
  44. - `pnx` and `pnpm create` now run the command in the directory selected by `--dir` or `--workspace-root`. Without either flag, the command still runs in the directory pnpm was started in.
  45. - `pnpm --env-file` now reads files the way `node --env-file` does. Values are taken as written, so `$VAR` is no longer expanded and Windows paths such as `C:\Users\me` load unchanged. A file is no longer rejected for its contents.
  46. #### Settings and messages
  47. - `pnpm version` no longer creates a git commit and tag when `gitTagVersion` is set to `false` [#17460](https://github.com/pnpm/pnpm/issues/17460).
  48. - `pnpm publish` now reads the `publishBranch` setting from `pnpm-workspace.yaml` and from `PNPM_CONFIG_PUBLISH_BRANCH`. The `--publish-branch` flag overrides it.
  49. - `pnpm` now accepts `--strict-ssl` and `--local-address <ip>` on the command line.
  50. - `pnpm doctor` uses a 15-second timeout when checking registry connectivity.
  51. - `pnpm-workspace.yaml` parse errors now report `ERR_PNPM_INVALID_SETTING`.
  52. - The error for a dependency with an invalid name in a project's manifest now names the project's directory. It used to say only "The current package", which did not tell which workspace project to fix [#17475](https://github.com/pnpm/pnpm/issues/17475).
  53. <!-- sponsors -->
  54. ## Platinum Sponsors
  55. <table>
  56. <tbody>
  57. <tr>
  58. <td align="center" valign="middle">
  59. <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a>
  60. </td>
  61. <td align="center" valign="middle">
  62. <a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
  63. <picture>
  64. <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
  65. <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
  66. <img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
  67. </picture>
  68. </a>
  69. </td>
  70. <td align="center" valign="middle">
  71. <a href="https://notion.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/notion.svg" width="80" alt="Notion"></a>
  72. </td>
  73. </tr>
  74. <tr>
  75. <td align="center" valign="middle">
  76. <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
  77. <picture>
  78. <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
  79. <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
  80. <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
  81. </picture>
  82. </a>
  83. </td>
  84. <td align="center" valign="middle">
  85. <a href="https://www.cloudflare.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
  86. <picture>
  87. <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/cloudflare.svg" />
  88. <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/cloudflare_light.svg" />
  89. <img src="https://pnpm.io/img/users/cloudflare.svg" width="160" alt="Cloudflare" />
  90. </picture>
  91. </a>
  92. </td>
  93. </tr>
  94. </tbody>
  95. </table>
  96. ## Gold Sponsors
  97. <table>
  98. <tbody>
  99. <tr>
  100. <td align="center" valign="middle">
  101. <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
  102. <picture>
  103. <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
  104. <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
  105. <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
  106. </picture>
  107. </a>
  108. </td>
  109. <td align="center" valign="middle">
  110. <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
  111. <picture>
  112. <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
  113. <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
  114. <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
  115. </picture>
  116. </a>
  117. </td>
  118. <td align="center" valign="middle">
  119. <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a>
  120. </td>
  121. </tr>
  122. <tr>
  123. <td align="center" valign="middle">
  124. <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
  125. <picture>
  126. <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
  127. <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
  128. <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
  129. </picture>
  130. </a>
  131. </td>
  132. <td align="center" valign="middle">
  133. <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
  134. <picture>
  135. <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
  136. <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
  137. <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
  138. </picture>
  139. </a>
  140. </td>
  141. <td align="center" valign="middle">
  142. <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
  143. <picture>
  144. <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
  145. <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
  146. <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
  147. </picture>
  148. </a>
  149. </td>
  150. </tr>
  151. <tr>
  152. <td align="center" valign="middle">
  153. <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
  154. <picture>
  155. <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
  156. <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
  157. <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
  158. </picture>
  159. </a>
  160. </td>
  161. <td align="center" valign="middle">
  162. <a href="https://latitude.so/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer"><img src="https://pnpm.io/img/users/latitude.svg" width="160" alt="Latitude"></a>
  163. </td>
  164. <td align="center" valign="middle">
  165. <a href="https://syntax.fm/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
  166. <picture>
  167. <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/syntaxfm.svg" />
  168. <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/syntaxfm_light.svg" />
  169. <img src="https://pnpm.io/img/users/syntaxfm.svg" width="90" alt="Syntax" />
  170. </picture>
  171. </a>
  172. </td>
  173. </tr>
  174. </tbody>
  175. </table>
  176. <!-- sponsors end -->

view on githubdetected 2026-10-31 via npm