pnpm 12.6.0 ships with automatic dependency deduplication, relocatable node_modules, package.yaml manifest editing, and --save-types support.
Minor Changes
autoDedupe deduplicates compatible dependency versions during installation #7258. Enable it in pnpm-workspace.yaml or use pnpm install --auto-dedupe or pnpm add --auto-dedupe. Frozen installs leave the lockfile unchanged.
pnpm install, pnpm run, and pnpm exec on macOS and Linux now reuse a node_modules directory and bin shims that moved or were copied together with their project #6937. The first command after the move checks the tree and records its new location, so project commands in node_modules/.bin keep working.
pnpm add --save-types saves available @types/* packages in devDependencies alongside registry dependencies #3868. Packages that declare bundled TypeScript types are skipped. Set saveTypes: true in pnpm-workspace.yaml to enable this by default.
package.yaml manifests can now be updated by pnpm add, pnpm update, pnpm remove, pnpm pkg, pnpm link, pnpm set-script, and pnpm version#2008. Existing comments and key order are preserved.
Catalog entries can now use the file: and link: protocols #8642. A relative path or bare path in an entry, such as ./tarballs/foo.tgz, is measured from the directory holding pnpm-workspace.yaml.
pnpm tasks status lists running and waiting tasks in each concurrency group, and waiting tasks now take available slots in arrival order with higher priority tasks going first #15208. If workspaces use different limits for the same group, a later task can take a free slot that earlier tasks cannot use. A package script named tasks takes precedence; use pnpm pm tasks status when that script exists.
pnpm cache prune deletes registry metadata cache directories that this version of pnpm can no longer read #15046. pnpm cache prune --dry-run lists what it would delete without removing anything.
macosBackup.excludeModulesDir and macosBackup.excludeStoreDir on macOS can now exclude newly created modules, virtual-store, and package-store directories from Time Machine #6440. Set either to true in global configuration or using the PNPM_CONFIG_MACOS_BACKUP_EXCLUDE_MODULES_DIR and PNPM_CONFIG_MACOS_BACKUP_EXCLUDE_STORE_DIR environment variables.
pnpm add --tilde is now an alias for --save-prefix=~#12863. The Yarn -T shorthand is not supported.
progress setting and --no-progress option now turn off dependency and download progress lines #14065. Warnings, lifecycle output, and the dependency summary are still printed.
Patch Changes
Security
POSIX bin shims now take cygpath and wslpath from the system default path on Cygwin, MSYS2, and WSL2 so a dependency cannot redirect another package's shim #14866.
pnpm install warnings no longer carry the text of a package's deprecation notice, naming only the deprecated package and version #15099. A deprecation warning names the newest non-deprecated version when one exists, and control characters and line separators are stripped from package identifiers and warnings.
pnpm install and other commands that report configuration warnings now warn when environment variables in project .npmrc credentials are ignored #15051.
Installing packages
pnpm install --frozen-lockfile now succeeds when an optional dependency was unresolvable and skipped by the install that wrote the lockfile #3960.
pnpm install --frozen-lockfile no longer installs dependencies of projects removed from pnpm-workspace.yaml#15248. Missing local tarballs used only by those projects no longer fail the install.
pnpm ci now empties node_modules before installing in a project that declares a clean script #15276.
pnpm install --force now re-imports every package into the virtual store #15030 and removes obsolete dependency links inside virtual-store packages when their dependencies change #15039.
preinstall script for the root project now runs before dependencies are resolved and linked #3760.
pnpm install now runs pnpm:devPreinstall when the root project uses package.yaml#15168.
pnpm install now enforces the root project's engines.node range when engineStrict is enabled #3016.
pnpm install now uses the running Node.js when devEngines.runtime declares a range without onFail: download#15230.
pnpm install no longer hangs when a git dependency is fetched over SSH and ssh prompts for a passphrase or host key confirmation, running ssh in batch mode instead #2227.
pnpm install now installs git-hosted dependencies without preparing them when their builds are explicitly denied by allowBuilds#10522.
pnpm install now reuses an in-flight tarball download when another resolution of the same archive still needs its package.json#15037.
pnpm install --prod no longer downloads registry packages that only a devDependency reaches #881.
pnpm install --no-runtime --frozen-lockfile with nodeLinker: hoisted no longer fails on repeated runs with a broken lockfile #15212.
Resolving and linking dependencies
pnpm install and pnpm update now resolve a dependency range to the newest matching version that is not deprecated #15128.
pnpm add <pkg> without a version now uses the catalog entry when the workspace already catalogs that package #14865.
pnpm install now links workspace dependencies declared with plain version ranges when excludeLinksFromLockfile and linkWorkspacePackages are enabled #15133.
pnpm install now resolves local tarball dependencies whose absolute file: paths contain .. consistently and skips reinstallation on repeat installs #15190.
pnpm install now installs dependencies when a custom resolver returns a local or git-hosted tarball without a manifest #15016.
pnpm.overrides entries written as a bare path, such as ./local-dep, are now measured from the directory holding pnpm-workspace.yaml#11131.
pnpm update --no-save no longer bypasses version-scoped overrides when a dependency selector specifies a version #14923.
pnpm peers check and strict peer dependency checks no longer reject compatible versions from named registries #15225.
pnpm outdated and pnpm update --interactive --latest now include named-registry dependencies such as work:2.1.0 and preserve their registry prefix #15226.
Workspace projects selected by hoistPattern or publicHoistPattern are now hoisted on every install #3642.
Workspace packages with SemVer build metadata are no longer skipped when they match the requested range and have the same version precedence as the registry package #2812.
Sped up pnpm dedupe and pnpm install in projects with many convergence overrides by checking overrides concurrently #15175.
minimumReleaseAge is no longer skipped for packages served by registries returning matching ETags for abbreviated and full package metadata #14925.
Running scripts and tasks
pnpm run signal handling no longer delivers a redundant second SIGINT to child scripts on Ctrl+C in a terminal, and properly forwards termination signals when running non-interactively without a terminal #7374.
pnpm run and pnpm exec in workspaces with sharedWorkspaceLockfile: false now verify dependencies in the selected projects rather than expecting a root workspace state #15272.
pnpm test now forwards --filter arguments to the test script when the option follows the shortcut #15217.
Recursive runs now start scripts matched by a /pattern/ selector in parallel within workspaceConcurrency#14933.
pnpm deploy, pnpm rebuild, pnpm rb, and pnpm setup now prefer a package.json script of the same name #14976.
modulesDir custom directory names now support executable lookup and CommonJS plugin resolution across pnpm run, pnpm exec, pnpm version hooks, and lifecycle scripts #3604.
pnpm install-test now accepts --no-bail directly and in recursive runs #3777.
Workspace and project configuration
pnpm commands run in a project not included in the workspace now act on that project alone #3561.
pnpm-workspace.yaml edits now preserve scalar YAML anchors and aliases #8245.
pnpm-workspace.yaml now expands environment variable placeholders with fallback syntax in enum-valued settings such as nodeLinker#14914.
pnpmfile configuration now loads a .js file as CommonJS or an ES module, following the nearest package.json#15141.
updateConfig hook settings are now honored by pnpm peers check, why, list, ll, licenses, audit, sbom, fetch, patch, patch-commit, patch-remove, approve-builds, and runtime#15047, #15049.
readPackage hook changes or removal now take added dependencies out of pnpm-lock.yaml and update dependencies when an existing lockfile is present #3735, #15136.
package.yaml projects now record their pinned pnpm under packageManagerDependencies in pnpm-lock.yaml#15167.
packageManagerDependencies pinning @pnpm/exe beside pnpm is no longer rewritten in pnpm-lock.yaml#14926.
pnpm now preserves CRLF line endings when modifying project manifests #3529.
loglevel setting is now honored when configured in pnpm-workspace.yaml, global configuration, or PNPM_CONFIG_LOGLEVEL#3122.
storeDir values loaded from global configuration or PNPM_CONFIG_STORE_DIR now expand a leading ~/ to the user's home directory #6560.
--shared-workspace-lockfile now produces a warning when passed on the command line outside a workspace #1617.
Windows
pnpm install on Windows now runs dependency build scripts from long global virtual store paths and normalizes scoped package paths in lifecycle script PATH entries #15111.
pnpm install across projects sharing a global virtual store on Windows no longer fails with Access is denied, file-exists errors, or transient sharing violations #15114, #15176, #15171.
pn, pnpx, pnx, and pnpm now run when Git Bash, MSYS2, or Cygwin launches them through a Windows path #14884.
pnpm dlx now reuses cached packages when Windows creates directory junctions for its cache links #15171.
pnpm pipeline --watch now resolves Windows short paths so multiple path representations share the build cache #15105.
CLI commands and output
pnpm remove now runs the project's own preuninstall, uninstall, and postuninstall scripts #3276.
pnpm remove -r now fails before modifying manifests if any requested dependency is absent from all selected projects #2319.
pnpm update --peer now updates ranges in peerDependencies#8081.
pnpm update now moves devEngines.runtime and engines.runtime version ranges to the resolved Node.js version #14988.
pnpm update -g no longer reinstalls unchanged packages #12002.
pnpm add -g, pnpm update -g, and pnpm remove -g now recover a global package group whose node_modules directory was deleted #15093.
pnpm add -g now installs local tarballs when PNPM_HOME contains .. path segments #15118.
pnpm version now reads tagVersionPrefix from pnpm-workspace.yaml, global config, or PNPM_CONFIG_TAG_VERSION_PREFIX when creating and reading Git tags #15044.
pnpm publish now allows a detached Git HEAD in CI environments #5894.
pnpm store prune now removes unreferenced files and packages from the content-addressable store #3635, as well as expired or superseded pnpm dlx cache data #15171.
pnpm cache list-registries now prints decoded registry URLs #15046.
pnpm deploy no longer triggers an install when running scripts in a read-only deployed filesystem #11617.
pnpm -r list --json now outputs a single JSON array when sharedWorkspaceLockfile is false, and --long and --parseable read each project's own modules directory #15011.
pnpm sbom now validates SPDX identifiers and expressions before emitting them as CycloneDX license IDs or expressions, falling back to a license name for non-SPDX values such as UNLICENSED#14786.
pnpm change check now validates pending change intents in .changeset/#15183.
pnpm --filter and pnpm -F shell completion now suggests workspace package names #15216. Completion candidates containing control or invisible formatting characters are omitted so package and script names cannot inject terminal escape sequences.
pnpm run and pnpm run-script shell completion now suggests package scripts #15034.
pnpm --version no longer creates a temporary file in the project directory during store detection #15264.
pnpm setup now describes displayed configuration changes as "The following configuration changes were made" #15100.
minimumReleaseAge approval prompts in pnpm install and pnpm update -g now count and display each package version once #15083, #15091.
.npmrc authentication warnings now report when an empty environment variable removes an auth token and name the affected key #4806.
The install summary now names the version each dependency resolved to when node-linker is hoisted#15161.
pnpm install now re-links a package's global virtual store slot after allowBuilds changes #15117.
pnpm now reports an unknown task setting in pnpm-workspace.yaml and carries on. It used to refuse to start, so a project could not use a task setting that only the pnpm version its packageManager pins reads. The setting is still an error when the running pnpm is that pinned version.
Python interpreter installation now retries historical release metadata requests. It caches the release list for up to 24 hours and refreshes it once after a lookup miss. When a release omits the current platform, the search samples at most eight other releases before reporting that the lookup is inconclusive.
Python registries entries now route packages by exact names or trailing-prefix patterns in packages. Registry declaration order no longer affects resolution. A matched package resolves exclusively from its assigned registry, including transitive and build dependencies. Use packages: ["*"] to declare the default index.
pnpm install no longer fails with "Too many levels of symbolic links" when a Cargo configuration file above the workspace is a symlink, such as a ~/.cargo/config.toml linked from a dotfiles repository.
pnpm install now returns "Already up to date" in a workspace where dedupeDirectDeps left a project without a node_modules directory of its own. Such a project forced a full install on every run.
pnpm install no longer refuses the repeat-install fast path just because a changed pnpm-lock.yaml is 16 MiB or larger. Such a lockfile forced a full install on the run after every change.
pnpm 12.5.0 makes Python a first-class ecosystem, accepts Package URLs in pnpm add, names whole platforms in supportedArchitectures, and gives tasks machine-wide concurrency limits. It also fixes an install that could reuse one package's downloaded tarball for another.
Minor Changes
Installing packages
pnpm add accepts a Package URL in place of a package name. pnpm add pkg:npm/express@4.18.2 saves express to package.json. pnpm add pkg:cargo/serde@1.0.188 saves serde to Cargo.toml. pnpm add pkg:pypi/requests@2.31.0 saves requests to pyproject.toml. pkg is now a reserved specifier prefix, whatever case it is written in, so a named registry can no longer be called pkg.
A registries entry can now name the ecosystem it serves.
ecosystem accepts npm, cargo and pypi. An entry that does not name one serves npm, as every entry did before.
An ecosystem with several indexes searches them in the order they are declared. The first index that has a package supplies it, so the one declared last answers what none before it had.
A registries entry may not carry credentials. pnpm reads them from .npmrc, matched by origin, for a PyPI index as for every other package source.
Configuring pnpm
supportedArchitectures now accepts a list of platforms, in place of the os, cpu and libc axes.
An install prepares for the platforms the list names, and for those only. A platform reads as <os>-<cpu>, with a C library on Linux, as in linux-x64-musl or linux-x64-manylinux_2_28. The Rust target triple of the same machine is accepted too, so x86_64-unknown-linux-gnu names the platform linux-x64 names. A Linux platform that names no C library is the glibc platform. current is the platform the install runs on.
The os, cpu and libc mapping keeps working and keeps its meaning.
Added concurrency groups for tasks. A task in pnpm-workspace.yaml can name a concurrencyGroup. The new concurrencyGroups setting gives each group a limit. At most that many tasks of the group run at once on the machine, counted across every pnpm process, pnpm pipeline included. A task past the limit waits for a running one to finish. A script that calls pnpm run for a task of the same group runs under the slot its parent holds.
node, bun and python can be named. Any other tool is refused.
mirror is the base a tool's own layout hangs off.
channels sends one release channel elsewhere. A channel neither it nor node-mirror:<channel> names is left to mirror. Only node publishes channels, so naming them for another tool is refused.
Set it in the global config.yaml or in PNPM_CONFIG_TOOLS. A pnpm-workspace.yaml that names a tool mirror is ignored.
pnpm pack-app downloads the Node.js it embeds through tools.node. node-mirror:<channel> keeps working and names the same thing as an entry under channels.
Python interpreters and environments
pnpm install now chooses a Python interpreter for each project instead of installing every project with one interpreter #14945. A project is installed with the first interpreter on the machine that its requires-python accepts, so a workspace can hold projects that support different Python versions. pnpm reads .python-version too, and prefers the version it asks for. Set python.executable in pnpm-workspace.yaml to name one interpreter for every project.
pnpm install now installs a Python interpreter when no interpreter on the machine fits the project #14945. The builds are python-build-standalone's, which uv and rye install too. One interpreter is shared by every project on the machine, and a later install uses it without downloading anything. runtimeOnFail decides what an install with no interpreter that fits does, the way it does for a Node.js runtime. error reports the project instead of installing one. warn and ignore install with an interpreter the machine has that the project's requires-python rejects. tools.python.mirror names a mirror.
Python environments now live in the store. Each project keeps only its .venv link, which points at the project's current environment generation under python-envs in the store. A repository with many Python projects no longer holds a .pnpm/python-envs directory in each of them. The next install relinks a .venv that an earlier release published. The old .pnpm/python-envs directory is left in place, since a running program may still use it, and can be deleted once none does. With frozenStore set, pnpm writes nothing to the store, so environments stay in the project's .pnpm/python-envs#15014.
Python environments now use packageImportMethod to import wheel files from the store. Use clone-or-copy for copy-on-write clones with a copy fallback, or copy for independent files. Hardlinked files share writes with the store and other environments.
Isolated Python build environments keep backend writes private with copy-on-write clones or copies.
Python projects and workspaces
pnpm install now installs a Python project's own package, so the project can be imported and the commands in [project.scripts] run right after an install #14945. The installed package points at the source tree, so an edit to a module takes effect without another install. pnpm installs the package of a project that declares a [build-system]. tool.uv.package overrides that either way.
pnpm install now installs a Python project in the workspace from its own source. Declare it under [tool.uv.sources], as shared = { workspace = true } or shared = { path = "../shared", editable = true }. pnpm builds the project with the backend it declares. It installs the build editable, so an edit to the project takes effect without another install.
Approve the build backend under allowBuilds in pnpm-workspace.yaml as a Package URL, as pkg:pypi/hatchling: true. An install that has not approved a backend does not build the projects that need it. The message names the key to add.
pnpm install now refuses a requirement that names a project in the workspace when nothing declares where it comes from. It used to take that name from the index.
The members of a uv workspace can now share one Python environment. Set shared-environment = true under [tool.pnpm.python] in the pyproject.toml that declares [tool.uv.workspace]. pnpm install then resolves every member as one graph into one pylock.toml and one .venv at the workspace root. Two members that require versions of a distribution no release satisfies at once are refused with an error naming both. Each project still gets an environment of its own by default #15015.
Python projects can now select extras and dependency groups through [tool.pnpm.python] in pyproject.toml#14945. Workspace python.extras and python.groups defaults now skip names a project does not define.
pnpm install now reads dynamic Python project metadata from the build backend #14945. Projects with only a requirements.txt file now get a Python environment and lockfile.
Python dependencies and lockfiles
pnpm can now resolve pylock.toml for several platforms and Python versions at once. supportedArchitectures names the platforms to lock for and python.versions the versions. Every platform is paired with every version. One committed lockfile then serves Linux CI and macOS or Windows contributors #14945.
The lockfile pins the wheel each environment takes for a distribution. It marks a package only some environments install. pnpm install takes the packages and wheels of the environment its interpreter matches, and refuses an interpreter none of them stand for. pnpm resolves a project that declares environments itself, not through the server pnprServer names. Naming neither setting locks for the interpreter running the install.
python.overrides and python.constraints pin the versions a Python resolution may pick #14945. pnpm reads uv's own overrides and constraints from pyproject.toml too.
pnpm install now supports Python dependencies from Git repositories #14945. Direct wheel URLs are also supported. Sources can be declared in [tool.uv.sources]. Git dependencies require allowBuilds approval.
pnpm install can install a Python release that publishes no wheel this interpreter accepts, by building the source distribution the index serves beside it #14945. The archive is pinned in pylock.toml by name and SHA-256. A later install replays it from the store, offline included. Building a source distribution runs the release's own build backend. Approve it with pkg:pypi/<distribution>: true under allowBuilds.
A resolution that finds no version of a distribution now says why. It tells apart a distribution no index publishes, one whose releases publish nothing this interpreter can install, and one whose versions the project's requirements exclude.
Patch Changes
Installing packages
pnpm no longer reuses one package's downloaded tarball for another package whose resolution pins a different integrity hash to the same URL #15021.
pnpm install and pnpm add now report an error when package.json, pnpm-lock.yaml, pyproject.toml or another file they snapshot before installing is a named pipe or a device. The command used to wait forever for something to write to it.
pnpm install --prod and pnpm install --dev now record every dependency group in pnpm-lock.yaml. node_modules still holds only the groups the filter selects. They used to write the filter into the lockfile, so a later pnpm install --frozen-lockfile rejected it. pnpm prune --prod, pnpm prune --dev, and pnpm prune --no-optional behave the same way #14912.
POSIX bin shims now convert a Windows-form path such as C:\node_modules\.bin\tsc correctly. The shim mangled the backslashes in such a path and could not reach the package it runs. Installing again replaces the shims already in node_modules#14867.
Two pnpm processes installing one workspace at the same time no longer fail on Windows with "Access is denied" while writing node_modules/.pnpm-workspace-state-v1.json. The write now retries the transient lock the other process holds, as pnpm's other file writes do.
pnpm now reads the manifest from the tarball when a pnpmfile resolvers hook returns a resolution without one. Such a package installed alone, with none of its own dependencies and no warning #15000.
pnpm install now merges Git conflict markers in pnpm-lock.yaml. It parses both sides of the conflict and keeps the versions they locked. A conflict in the config dependencies recorded at the top of the lockfile is merged too #14880.
Cargo projects
pnpm install can now generate Cargo.lock for workspaces with path or Git [patch] and [replace] overrides. Adding, removing, and updating crates also preserve these overrides #14950.
Cargo lockfile resolution blocks unsupported Git transport helpers declared by transitive dependencies.
pnpm install now vendors recursive Git submodules for Cargo dependencies at their pinned commits. Cargo builds can use these sources offline. Set Git's protocol.file.allow to always to fetch local file submodules. pnpm fetches cached Git crates again on the first online install #14951.
pnpm install now generates Cargo.lock for workspaces with Git dependencies, including a dependency that omits a package version. It also downloads the Rust standard library's dependencies when Cargo configuration enables build-std#14944.
pnpm install now handles weak Cargo features, written crate?/feature. Resolution failed when one dependency turned on an optional crate and another asked for a weak feature of it #14960. The generated Cargo.lock now also includes the dependencies weak features reference, which Cargo rejected with --locked for crates such as uuid#14978.
pnpm install now generates Cargo.lock when a crate version it considers depends on a release the registry carries only as yanked. pnpm rules that version out and resolves the rest of the graph. Resolution failed with an error such as no non-yanked version of napi-build satisfies ^3.0.0-beta#14952.
pnpm install now falls back to an older semver-incompatible version of a crate when the newest one a dependency range allows cannot be resolved. Ranges such as >=1, <3 span several of them #14962.
Python projects
pnpm install now honors uv workspace members when discovering Python projects. When no uv workspace declares a project, pnpm skips projects under conventional example, demo, documentation, template, test, tests, and test fixture directories #15058.
pnpm install --filter <selector> now installs only the Python projects the selection asks for. A Python project that shares a directory with an npm workspace project is selected with that project. A Python project in a directory of its own is selected by the distribution it declares, by its path, or through the [tool.uv.sources] entries that reach it. Under --fail-if-no-match, a selector that names only a Python project is a match. pnpm add --filter <selector> pypi:<package> writes the requirement to every selected project #14945.
pnpm install now installs wheels whose RECORD hashes disagree with their contents. The wheel archive's locked SHA-256 hash remains verified. pnpm writes correct hashes to the installed RECORD#15061.
pnpm install now installs a Python wheel whose WHEEL file lists tags that differ from the ones in its filename. A wheel whose filename tags were changed after the build, such as mysql-connector-python, was rejected #14945.
A Python release whose wheel metadata declares a requirement pnpm cannot read no longer fails the install. pnpm now resolves the project against the other releases of that package, and reports the unreadable requirement when none of them works.
pnpm install no longer fails when a Python index lists a file pnpm cannot use, such as a release with no SHA-256 digest or an unreadable wheel filename. That file is left out and the project resolves against the remaining releases.
pnpm add pypi:<package> in a directory that has no pyproject.toml now names the missing file and says where to run the command. It used to fail with a bare No such file or directory (os error 2)#14945.
Performance
pnpm audit no longer hangs on dependency graphs with many shared dependencies #15005.
Sped up pnpm install in Python workspaces with many projects. Projects now prepare concurrently. Projects with identical registry requirements also share fresh dependency resolutions #14945.
Repeat installs through the Node-API bindings now return "Already up to date" when the project manifests still match pnpm-lock.yaml. Before, every such install reinstalled the whole tree. An install also no longer reinstalls when pnpm-lock.yaml differs from the installed dependencies only by packages no project depends on or by top-level keys pnpm does not define.
Other commands
pnpm deploy now links commands exposed by workspace dependencies into the deployed project's node_modules/.bin directory #14899.
pnpm dlx and pnx now prompt to approve dependency build scripts in interactive terminals #14943. Cached packages with pending builds also prompt for approval. Without an interactive terminal, use --allow-build to allow the required builds.
pnpm add -g and pnpm update -g now ignore incomplete unrelated global package groups when every command from the replaced group is retained. Operations that could remove a global command still require complete ownership information.
pnpm pack now writes tarball entries grouped by file extension and file name, the order npm uses. Packages that ship many same-named files, such as template collections, pack much smaller #14766.
pnpm outdated --long fills the Details column with the package homepage again #14886.
pnpm 12.4.2 includes security fixes for executable shims and GitHub Actions links, more reliable installs, faster peer dependency checks in workspaces, and Python lockfiles that work across compatible targets.
Patch Changes
Security
Dependency executables can no longer take over another package's POSIX bin shim through its shell helpers. Reinstall dependencies to replace existing shims #14837.
On Cygwin, MSYS2, and WSL, shims still use PATH for Windows path conversion, so dependency executables can still redirect them there.
GitHub Actions homepage links no longer expose server credentials. GitHub server URLs now require HTTPS, with HTTP allowed only for loopback hosts.
Installing packages
pnpm no longer crashes at startup on FreeBSD and other Unix-like platforms. Platforms other than Windows and macOS use ~/.local/share/pnpm/store by default #14859.
pnpm install on Windows no longer fails with ERR_PNPM_PACKAGE_MANAGER_REMOVE_MODULES_DIR when clearing node_modules containing linked dependencies, such as when changing nodeLinker#14790.
pnpm install <pkg> now accepts --prod and --dev, including --prod=false#14868.
pnpm install and pnpm update now honor --ignore-workspace in nested projects excluded from the surrounding workspace. The flag also skips that workspace's settings during the packageManager check #14809.
pnpm install on macOS no longer reuses stale files for file: tarball or git-hosted tarball dependencies.
pnpm install in a single-project directory now detects package.json edits made while the previous install was finishing #14890.
pnpm install --frozen-lockfile now removes packages no longer reachable from any project in pnpm-lock.yaml. This also prevents repeated lifecycle script execution and unnecessary installs before pnpm run and pnpm exec with verifyDepsBeforeRun#14891.
Resolving and updating dependencies
Node.js runtime resolution now reports network failures from unofficial-builds.nodejs.org. These failures previously omitted musl builds from pnpm-lock.yaml, making its contents depend on network access #14813.
pnpm install now rejects invalid peerDependencies specifiers with ERR_PNPM_INVALID_PEER_DEPENDENCY_SPECIFICATION. A value such as "foo": "foo@1.0.0" previously created a broken directory link #14791.
pnpm deploy now writes plain registry versions in the deployed package.json, without peer dependency suffixes. The lockfile retains peer bindings, and npm aliases retain their target package names #14873.
pnpm add <git repository> now names repositories without a package.json as @owner/repo, allowing dependencies on equally named repositories from different owners #14870.
Peer dependency resolution now deduplicates packages whose child dependency resolves an optional peer in only some workspace projects, such as next with styled-jsx's optional babel-plugin-macros peer #14800.
pnpm update now settles the lockfile in one run when an upgrade removes the package providing an optional peer dependency #14895.
pnpm update --no-save now preserves override-applied specifiers for dependencies it is not updating, preventing subsequent frozen installs from failing with ERR_PNPM_OUTDATED_LOCKFILE#14836.
pnpm update --no-save now succeeds under minimumReleaseAgeStrict when every resolved version is old enough #14835.
Performance
Workspace installs and pnpm peers check are faster when projects depend on each other, fixing a slowdown introduced in 12.3.0. Unmet peer dependencies of workspace packages are now reported only under projects that link them directly #14906.
Hoisted installs use less memory when packages are cached. Frozen-lockfile hoisted installs on macOS are also faster when reusable package directories are cached.
Python projects
pnpm install --frozen-lockfile now reuses pylock.toml across compatible Python targets, including after kernel updates. Reuse requires unchanged requirements, index, and requires-python, compatible wheels, and a locked dependency graph matching the target's markers #14843.
The lockfile's environments marker now includes only the interpreter version and marker variables used by the dependency graph. Without --frozen-lockfile, pnpm warns and resolves again when the locked graph no longer matches the target.
Python resolution no longer fails on malformed Requires-Python values, such as the trailing comma in openpyxl 3.0.x. pnpm treats these releases as declaring no interpreter range #14910.
pnpm add pypi:... now rejects unsupported --save-prefix values before editing the manifest or resolving dependencies.
Workspaces and scripts
Scripts listed in syncInjectedDepsAfterScripts no longer fail with ERR_PNPM_INJECTED_DEPS_SYNC_READ_DIR when the lockfile contains an injected package copy that no project depends on.
shellEmulator now expands ${VAR}, ${VAR:-default}, and ${VAR:+alternative} in scripts #14814.
Cargo and Python project discovery now honors ! exclusions in pnpm-workspace.yamlpackages, skipping both parsing and generated source configuration for excluded projects #14844.
pnpm --filter "./packages/{app,lib}" now selects either alternative. Brace alternatives can nest, span path separators, and combine with other wildcards.
GitHub Actions updates now stop if an action reference changes during version resolution, and preserve unrelated workflow edits.
CLI and output
pn, pnpx, and pnx now run the pnpm installed alongside them, even when that directory is absent from PATH or another pnpm comes first #14803.
pnpm --version now reports failures to install or record a project's pinned pnpm, then prints the running CLI's version. It also honors --store-dir and --store#14831.
pnpm self-update no longer reinstalls the active version when it was installed by the standalone installation script #14823.
pnpm t and pnpm tst work again as aliases for pnpm test.
pnpm sbom now emits valid repository URLs in CycloneDX externalReferences[].url and SPDX homepage. Shorthands such as vercel/ms become git+https URLs, embedded credentials are removed, and invalid repository values are omitted #14773.
pnpm 12.4.1 fixes installs that failed on filesystems refusing hard links or clones, on Android, and under nodeLinker: hoisted. Repeat installs are faster.
Patch Changes
Installing packages
pnpm install no longer fails with Operation not permitted when the filesystem refuses a hard link or a copy-on-write clone #14722. Under packageImportMethod: auto and clone-or-copy, pnpm copies the file instead. EdenFS checkouts, which have no hard links, and rootless containers, which refuse the clone syscall, both hit this. An explicit packageImportMethod: hardlink or clone still reports the error.
pnpm also copies a package file whose store entry has reached the filesystem's limit on names for one file, 1024 on NTFS and 65000 on ext4. Such a file failed the install under packageImportMethod: hardlink, and under auto it stopped pnpm hard linking for the rest of the install.
pnpm install no longer writes a package file through a symlink left at the path it is importing to. Copying such a file overwrote whatever the link pointed at, and created that file when the link pointed nowhere. An executable package file also made the link's target executable.
Fixed pnpm install and pnpm dlx on Android. Registry requests crashed because pnpm found no system CA certificates, so pnpm uses bundled ones there #14777. Imports also failed with "Permission denied" on filesystems that deny hard links and reflinks, and now fall back to copying #14780.
pnpm install no longer fails with "Invalid cross-device link" while preserving a package's nested node_modules directory during a Docker build #14758.
pnpm install no longer fails on a package tarball that carries a file at the archive root, such as the ._* entries macOS tar adds #14701. The file is installed at the root of the package.
A file: tarball packed without the usual package/ directory is now recorded under the name and version from its own package.json. It was recorded under the alias the dependency was given, at version 0.0.0.
Under nodeLinker: hoisted, pnpm install no longer re-imports packages that are already in place. A repeat install replaced the whole node_modules tree and reported Packages: +N. A package is still imported when its directory is missing, when its package.json no longer carries the installed version, when it is a file: dependency, and when it is patched. Lifecycle scripts no longer run again for a package left in place, and pnpm rebuild and a change to allowBuilds still reach it.
pnpm install now runs a dependency's build scripts again when its side-effects cache entry has no files to restore #14717. Such builds were skipped and nothing was put in their place, so a script whose whole effect lands outside its own package directory, such as a git hook installer, never took effect. pnpm no longer publishes empty artifacts to the shared side-effects cache either.
Resolving and linking dependencies
pnpm install, pnpm add, and pnpm dedupe now apply ignoredOptionalDependencies#14729. Matching optional dependencies are left out of the lockfile and are not installed. pnpm 12 installed them whenever it resolved dependencies from scratch.
pnpm install no longer links a transitive dependency to a workspace package when linkWorkspacePackages is true and the dependency is declared with a plain version range #14781. Enabling preferWorkspacePackages does not change this. Set linkWorkspacePackages: deep to link them.
pnpm install no longer leaves dangling dependency links in workspace packages located above the workspace root #14726.
pnpm install and pnpm add no longer leave a dangling symlink in node_modules when a project starts depending directly on a package that the lockfile holds only as a transitive dependency with resolved peer dependencies #14714.
pnpm dedupe now keeps a compatible auto-installed peer when another workspace project depends on a newer major #14697. Repeated runs alternated between compatible and incompatible peer versions.
pnpm peers check no longer reports a peer dependency declared as workspace:^, workspace:~, or a bare workspace: as unmet #14770. pnpm reported these as unmet whatever version the linked workspace project supplied.
Performance
Sped up repeat installs #14540. pnpm checks the store's files only for the packages it links into node_modules, instead of every package in the lockfile. Creating the command shims in node_modules/.bin makes about 1,500 fewer filesystem calls in a 76 project workspace. Installs that use the global virtual store read their slot paths from the cache directory instead of deriving them every time. Verifying a large lockfile also allocates less memory.
Sped up pnpm install in Cargo workspaces with many member crates. Repeated installs reuse verified Cargo checksum metadata.
Installing several packages from the same Git repository and commit now downloads the source once per install #14725. Each package still runs its prepare scripts in its own copy of the checkout.
Running scripts and tasks
pnpm now passes Ctrl+C on to the script or command it started and waits for it to shut down #14723. pnpm exited first, so a script that was still writing landed on the shell prompt.
pnpm run "/pattern/" --no-bail now lets every matched script finish after one of them fails #14718. The command exits with ERR_PNPM_RUN_FAILED, and its message lists the scripts that failed in the order they were selected.
pnpm pipeline no longer fails on a project that tracks a symlink, such as a CLAUDE.md pointing at AGENTS.md#14692. Changing a symlinked input's target invalidates that task's cache, and pnpm pipeline --no-cache no longer hashes task inputs.
Commands
pnpm add -g, pnpm update -g, and pnpm remove -g no longer change global bins or install directories after reading only part of an installed package group #13796. If any declared package manifest is missing, malformed, or unreadable, pnpm now fails before it activates or removes anything and leaves the existing global installation intact.
pnpm dedupe now processes every workspace project by default, including workspaces that keep a separate lockfile per project #14732. Workspace filters select which projects it processes, and --fail-if-no-match exits with an error when no project matches.
pnpm update <name>@<version> now keeps the range operator the manifest declares #14745. Running pnpm update react@19.3.0 on "react": "^19.2.8" writes "react": "^19.3.0". A jsr: entry keeps its jsr: prefix, and a plain pnpm update now moves a jsr: range the way it moves an npm range.
pnpm --filter directory selectors now support ? wildcards and character classes such as [ab]. A * or ? wildcard no longer selects a directory whose name starts with a dot, as on pnpm 11.
pnpm deploy --legacy now prefers the dependency versions pinned in the source workspace lockfile when they still satisfy the deployed project's range #13857.
pnpm sbom now leaves out a package's author field when the manifest author name is empty or contains only whitespace #14685. In a filtered or split workspace run, only a project with no author field inherits the workspace root's author.
pnpm sbom --sbom-format spdx now writes creationInfo.created with whole seconds, such as 2026-09-08T10:38:21Z#14684. The fractional seconds it carried were rejected by strict SPDX consumers.
Configuration
The updateConfig pnpmfile hook now receives the resolved configuration, including settings that came from .npmrc, the command line, or a default #14676. Scoped registries are reported under registriesByScope, and a hook may rewrite that map to change where packages are fetched from. Registry credentials are reported under configByUri, as pnpm 11 reports them. An unset setting is left out rather than reported as null.
pnpm audit --fix and the minimumReleaseAgeStrict approval prompt now keep the comments in minimumReleaseAgeExclude when they append an entry to it in pnpm-workspace.yaml. The rest of the list is left as written, and the trustPolicyExcludePrune and minimumReleaseAgeExcludePrune cleanups keep the comments of the entries they retain.
pnpm install and pnpm dedupe now run those cleanups too #14759. Only pnpm add, pnpm update, and pnpm remove pruned the entries that the freshly written lockfile no longer resolves.
pnpm config set --global node-download-mirrors no longer rejects the key #13611. The global config file already accepted nodeDownloadMirrors, but the command refused to write it.
NO_PROXY entries that start with a dot, such as .npmjs.org, now bypass the proxy for the domain and its subdomains #14686.
pnpm no longer creates a project pnpm-lock.yaml when devEngines.packageManager.onFail is download and lockfile writing is off through lockfile: false or --no-lockfile#14728. pnpm still switches to the pinned version.
pnpm now writes node_modules/.package-map.json only when nodeExperimentalPackageMap is enabled. Nothing reads the file without that setting, and an install that stops writing the map removes the one a previous install left.
Windows
pnpm pipeline no longer fails with intermittent access denied errors when concurrent tasks save their cache entries on Windows.
Windows filesystem operations now retry permission errors for up to one second #14682. A permanent permission error delayed the failure by a minute. Sharing and lock violations keep their one minute retry budget.
Messages and output
pnpm now warns when the root package.json declares a non-empty workspaces array and the project has no pnpm-workspace.yaml#2255. Such an install linked no project and said nothing about why.
ERR_PNPM_PACKAGE_MANAGER_REMOVE_MODULES_DIR now names the file or directory in node_modules that pnpm could not clean up. It reported only the underlying OS error, such as "Access is denied (os error 5)".
pnpm --help no longer describes pnpm as experimental.
pnpm can now manage npm, Python, and Cargo dependencies in the same workspace. Enable python.enabled or cargo.enabled in pnpm-workspace.yaml, then use pnpm install to install them together.
Add Python packages with pnpm add pypi:<package>. pnpm uses pyproject.toml, pylock.toml, and a managed .venv. Frozen and offline installs are supported, and pnpm run and pnpm exec make the environment's executables available #14566.
Add Rust crates with pnpm add crate:<package>. pnpm supports crates.io and custom sparse registries configured with cargo.indexUrl. Registry authentication supports pnpm credentials and, for crates.io, CARGO_REGISTRY_TOKEN or $CARGO_HOME/credentials.toml.
Both ecosystems support faster dependency resolution through pnprServer, with local resolution as a fallback when the server does not support it.
Added pnpm pipeline [name] to install frozen dependencies and run workspace tasks declared in pipelines. It selects affected projects, runs their task graph, and continues running tasks after a task fails.
Tasks support inputs, outputs, env, and cache settings. Cached results restore task outputs and replay logs. Cargo tasks can reuse local build state between worktrees with tasks.<name>.cargoTargetDir. Set includeWorkspaceRoot: true to include root tasks.
Use pnpm pipeline --dry-run to preview the task graph without installing configuration dependencies or running workspace hooks.
Added support for Android on arm64 and x64, FreeBSD on x64, and Linux on ppc64le, s390x, and RISC-V (riscv64 with glibc) #14431, #14597, #7582.
Added trustPolicyExcludePrune to automatically remove unused versions and packages from trustPolicyExclude when running pnpm add, pnpm update, or pnpm remove. It is disabled by default. Package name patterns such as @scope/* are kept, and cleanup is skipped when sharedWorkspaceLockfile is false.
Added pnpm change check for CI validation of package versions against the versioning.epics bands and versioning.fixed groups in pnpm-workspace.yaml. It reports all violations, including packages that are not part of the current release.
Patch Changes
Registry metadata is now kept separate for registries with different URL paths or schemes. This prevents installs from using another registry's package versions or tarball URLs, and keeps metadata fetched over HTTP from being reused for HTTPS #13558.
The first install after upgrading refetches registry metadata. The package store is unchanged. pnpm cache view now shows full registry URLs. Scripts that parse the directory names from pnpm cache list-registries or pnpm cache list need updating.
Patches that add build scripts or a binding.gyp now trigger a build, subject to build approval. Unapproved builds appear under "Ignored build scripts" #14648.
Build scripts can now be rejected before installing a package with pnpm add --allow-build=!<pkg>, including global installs. pnpm approve-builds <pkg> and pnpm approve-builds !<pkg> also save decisions when no packages are awaiting approval. They warn if the named package is not awaiting approval #14067.
A registry configured in .npmrc now takes precedence over registry settings saved by pnpm login in the global config.yaml. This fixes installs using the wrong registry after login #14614.
Large downloads over slow connections no longer time out while data is still arriving. fetch-timeout now limits how long a request can go without making progress #14604.
Sped up installs in workspaces with many projects when reusing a warm global virtual store #14540.
pnpm deploy is faster in large workspaces and no longer fails with ERR_PNPM_LOCKFILE_CONFIG_MISMATCH when the project includes a .pnpmfile.mjs#14539, #14671.
pnpm add --workspace <pkg> works again. It saves the dependency with the workspace: protocol and links it from the workspace. The command fails if no workspace project provides the package #14602.
pnpm add and pnpm install now accept protocol-prefixed selectors such as jsr:@scope/pkg, npm:pkg@^1.0.0, and workspace:pkg@*#14590. Installs with JSR dependencies in the lockfile also no longer fail with ERR_PNPM_META_FETCH_FAIL#14649.
Boolean flags now accept explicit inline values. For example, pnpm install --prod=false installs devDependencies, while --prod=true skips them #14553.
pnpm install <pkg> now accepts --offline and --prefer-offline, as pnpm add <pkg> already did #14194.
Fixed pnpm install --frozen-lockfile rejecting a freshly generated lockfile when overrides use relative file: or link: paths in a workspace #14555.
Fixed installs with config dependencies failing on symlinked lockfiles, such as those used by Bazel and Nix, when the config dependencies have not changed. Updates that would write through a symlink remain disallowed. Updating config dependencies also preserves lockfiles that start with a byte order mark #14372.
Fixed package manager version pins being written to the wrong lockfile when lockfileDir is set. The pins also remain consistent across commands when version switching is disabled, avoiding unnecessary lockfile changes #14633, #14575.
pnpm import now respects lockfileDir and branch lockfiles without modifying other lockfiles. Failed imports restore the destination lockfile #14563.
pnpm patch-commit now produces valid patches when files are added or deleted. pnpm install also accepts patches that delete files without listing their contents, and patch files with CRLF line endings #14559, #14557.
Fixed version ranges with partial upper bounds. For example, <=16 now includes all 16.x versions, and >=0.11 <=3 correctly accepts 3.0.1 #14419.
Workspace package patterns now support . and .. segments and repeated slashes. Patterns such as ./packages/* and exclusions such as !./packages/foo now match correctly #14571.
packageConfigs settings now apply to the specified projects when sharedWorkspaceLockfile is false, including overrides, hoist, modulesDir, saveExact, and savePrefix. Workspaces with a shared lockfile report which entries were ignored #14556.
pnpm run and pnpm exec no longer report a changed workspace structure after a successful install when sharedWorkspaceLockfile is false and verifyDepsBeforeRun is enabled #14588.
Commands run from a project's subdirectory now find the nearest ancestor with a manifest. This fixes commands such as pnpm bin returning paths under the wrong directory. pnpm init still creates its manifest in the current directory, and pnpm exec still runs there #14622.
Relative scriptShell paths in pnpm-workspace.yaml now resolve from the workspace root, including when scripts run in nested packages. Bare command names such as bash still use PATH#14422.
Fixed installing the pnpm version pinned in packageManager when nodeLinker is hoisted. Managed Node.js, Deno, and Bun installations also work when the global config uses nodeLinker: hoisted#14595.
The JavaScript pnpm can again switch to a project's pinned pnpm version on platforms without a native binary for that version, such as Alpine Linux with pnpm 10 or Intel Macs with pnpm 11. If a native pnpm version does not support the platform, the error now names the missing target #13622.
Provisioning Yarn 6 now uses GH_TOKEN or GITHUB_TOKEN when available to avoid GitHub's anonymous API rate limit in CI. Tokens are only sent when strict-ssl is enabled.
Fixed concurrent installs sharing a global virtual store on macOS failing with "failed to import ... No such file or directory" #14560.
Fixed pnpm setup failing with ERR_PNPM_DIRECTORY_FETCHER_PATH_ESCAPE on Windows. Local file: dependencies whose directories are symlinks or junctions are now packed correctly #14618.
On Windows, installs now retry replacing command shims temporarily locked by another process #14549.
Fixed argument forwarding on Windows with shellEmulator enabled. Paths ending in a backslash, line breaks, and literal shell expressions are preserved #14548.
Windows store paths now consistently use backslashes in pnpm store path output and in the storeDir and virtualStoreDir fields of node_modules/.modules.yaml.
Invalid certificates in ca or cafile no longer cause an Invalid CA certificate error. Valid certificates still apply, and blank cert or key values are treated as unset #14646.
Installs now respect the archive extraction concurrency limit even after a download is abandoned #14585.
pnpm audit summaries now exclude advisories ignored through auditConfig.ignoreGhsas and report them separately. When all advisories are ignored, the summary says so #14535.
pnpm pack --json now reports errors as JSON. Lifecycle script output appears before the final JSON output.
pnpm outdated -r now wraps the Dependents column, keeping the table readable when many workspace projects use the same dependency #14591.
Shell completions now support the pn alias in bash, fish, pwsh, and zsh #11955.
pnpm version now accepts -m as a short alias for --message#14567.
This PR contains the following updates:
| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [@girs/gjs](https://github.com/gjsify/types/tree/main/gjs#readme) ([source](https://github.com/gjsify/types)) | [`4.7.0` → `4.9.0`](https://renovatebot.com/diffs/npm/@girs%2fgjs/4.7.0/4.9.0) |  |  |
| [@girs/mtk-17](https://github.com/gjsify/types/tree/main/mtk-17#readme) ([source](https://github.com/gjsify/types)) | [`17.0.0-4.0.4` → `17.0.0-4.0.0-rc.17`](https://renovatebot.com/diffs/npm/@girs%2fmtk-17/17.0.0-4.0.4/17.0.0-4.0.0-rc.17) |  |  |
| [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc) | [`64.3.6` → `64.5.4`](https://renovatebot.com/diffs/npm/eslint-plugin-jsdoc/64.3.6/64.5.4) |  |  |
| [pnpm](https://github.com/pnpm/pnpm/tree/main/pnpm) ([source](https://github.com/pnpm/pnpm/tree/HEAD/pnpm/npm/pnpm)) | [`12.3.4` → `12.6.0`](https://renovatebot.com/diffs/npm/pnpm/12.3.4/12.6.0) |  |  |
| [pnpm](https://github.com/pnpm/pnpm/tree/main/pnpm) ([source](https://github.com/pnpm/pnpm/tree/HEAD/pnpm/npm/pnpm)) | `12.3.4` → `12.6.0` |  |  |
| [ts-jest](https://kulshekhar.github.io/ts-jest) ([source](https://github.com/kulshekhar/ts-jest)) | [`29.4.12` → `29.4.14`](https://renovatebot.com/diffs/npm/ts-jest/29.4.12/29.4.14) |  |  |
---
### Release Notes
<details>
<summary>gjsify/types (@​girs/gjs)</summary>
### [`v4.9.0`](https://github.com/gjsify/types/compare/8ecce25095f944f325cf48ddf453a636c7159955...9ab5e64375bc9e0677afb6383d97cd6b6488b3ef)
[Compare Source](https://github.com/gjsify/types/compare/8ecce25095f944f325cf48ddf453a636c7159955...9ab5e64375bc9e0677afb6383d97cd6b6488b3ef)
### [`v4.8.0`](https://github.com/gjsify/types/compare/a891b9e8a0e455c1501c244457fe48f798446607...8ecce25095f944f325cf48ddf453a636c7159955)
[Compare Source](https://github.com/gjsify/types/compare/a891b9e8a0e455c1501c244457fe48f798446607...8ecce25095f944f325cf48ddf453a636c7159955)
</details>
<details>
<summary>gajus/eslint-plugin-jsdoc (eslint-plugin-jsdoc)</summary>
### [`v64.5.4`](https://github.com/gajus/eslint-plugin-jsdoc/releases/tag/v64.5.4)
[Compare Source](https://github.com/gajus/eslint-plugin-jsdoc/compare/v64.5.3...v64.5.4)
##### Bug Fixes
- **`no-unnecessary-type-assertion`:** only try typescript if the rule is enabled; fixes [#​1773](https://github.com/gajus/eslint-plugin-jsdoc/issues/1773) ([3c1c209](https://github.com/gajus/eslint-plugin-jsdoc/commit/3c1c209bed53470ec5fcac8e2fa7693ff254bc91))
### [`v64.5.3`](https://github.com/gajus/eslint-plugin-jsdoc/releases/tag/v64.5.3)
[Compare Source](https://github.com/gajus/eslint-plugin-jsdoc/compare/v64.5.2...v64.5.3)
##### Bug Fixes
- for [@​link](https://github.com/link) targets, check properties within export class declarations; fixes [#​1771](https://github.com/gajus/eslint-plugin-jsdoc/issues/1771) ([2d85fed](https://github.com/gajus/eslint-plugin-jsdoc/commit/2d85fedb689f5045f284b37ca4d01ed688c2a920))
### [`v64.5.2`](https://github.com/gajus/eslint-plugin-jsdoc/releases/tag/v64.5.2)
[Compare Source](https://github.com/gajus/eslint-plugin-jsdoc/compare/v64.5.1...v64.5.2)
##### Bug Fixes
- set optional typescript peer dep. to any to undo breaking change ([f469cdf](https://github.com/gajus/eslint-plugin-jsdoc/commit/f469cdf5ac484c2d3c8ce37355dc1a861b71128e))
### [`v64.5.1`](https://github.com/gajus/eslint-plugin-jsdoc/releases/tag/v64.5.1)
[Compare Source](https://github.com/gajus/eslint-plugin-jsdoc/compare/v64.5.0...v64.5.1)
##### Bug Fixes
- specify typescript as an optional peer dependency; fixes [#​1768](https://github.com/gajus/eslint-plugin-jsdoc/issues/1768) ([d147779](https://github.com/gajus/eslint-plugin-jsdoc/commit/d147779967f1876c1742a4d3d4c2ee37c9504855))
### [`v64.5.0`](https://github.com/gajus/eslint-plugin-jsdoc/releases/tag/v64.5.0)
[Compare Source](https://github.com/gajus/eslint-plugin-jsdoc/compare/v64.4.0...v64.5.0)
##### Features
- **`ts-ban-ts-comments`:** add rule mirrorring typescript-eslint's `ban-ts-comments` rule ([afff498](https://github.com/gajus/eslint-plugin-jsdoc/commit/afff498687acb78c36fabc74a0662d0b00d567b8))
### [`v64.4.0`](https://github.com/gajus/eslint-plugin-jsdoc/releases/tag/v64.4.0)
[Compare Source](https://github.com/gajus/eslint-plugin-jsdoc/compare/v64.3.10...v64.4.0)
##### Features
- **`convert-to-jsdoc-comments`, `no-bad-blocks`:** add LibreJS tags ([99d2abf](https://github.com/gajus/eslint-plugin-jsdoc/commit/99d2abf96bd6ebe3beb54faf3f3db2dd9c5b51a7))
### [`v64.3.10`](https://github.com/gajus/eslint-plugin-jsdoc/releases/tag/v64.3.10)
[Compare Source](https://github.com/gajus/eslint-plugin-jsdoc/compare/v64.3.9...v64.3.10)
##### Bug Fixes
- **`convert-to-jsdoc-comments`:** merge stacked line comments into a single JSDoc block ([64a8dca](https://github.com/gajus/eslint-plugin-jsdoc/commit/64a8dca813d060bcb065277cc183f25d5f44ea98)), closes [#​1764](https://github.com/gajus/eslint-plugin-jsdoc/issues/1764)
### [`v64.3.9`](https://github.com/gajus/eslint-plugin-jsdoc/releases/tag/v64.3.9)
[Compare Source](https://github.com/gajus/eslint-plugin-jsdoc/compare/v64.3.8...v64.3.9)
##### Bug Fixes
- **`no-undefined-types`:** recognize class members from the class's own JSDoc ([eade377](https://github.com/gajus/eslint-plugin-jsdoc/commit/eade3775ed771e6622e9828cfe6a998d35de43bd)), closes [#​1762](https://github.com/gajus/eslint-plugin-jsdoc/issues/1762)
### [`v64.3.8`](https://github.com/gajus/eslint-plugin-jsdoc/releases/tag/v64.3.8)
[Compare Source](https://github.com/gajus/eslint-plugin-jsdoc/compare/v64.3.7...v64.3.8)
##### Bug Fixes
- **`getJsdocProcessorPlugin`:** key positions by file; drop suggestions ([1b3c339](https://github.com/gajus/eslint-plugin-jsdoc/commit/1b3c3395cc38f23898695848b8d1136e0b909856))
### [`v64.3.7`](https://github.com/gajus/eslint-plugin-jsdoc/releases/tag/v64.3.7)
[Compare Source](https://github.com/gajus/eslint-plugin-jsdoc/compare/v64.3.6...v64.3.7)
##### Bug Fixes
- **`no-undefined-types`:** recognize sibling class members from a property's JSDoc ([5cac70a](https://github.com/gajus/eslint-plugin-jsdoc/commit/5cac70a2dbb8c8470551d74b862036e133f8b5cc)), closes [#​1760](https://github.com/gajus/eslint-plugin-jsdoc/issues/1760)
</details>
<details>
<summary>pnpm/pnpm (pnpm)</summary>
### [`v12.6.0`](https://github.com/pnpm/pnpm/releases/tag/v12.6.0): pnpm 12.6
[Compare Source](https://github.com/pnpm/pnpm/compare/pnpm@12.5.1...v12.6.0)
pnpm 12.6.0 ships with automatic dependency deduplication, relocatable node\_modules, package.yaml manifest editing, and --save-types support.
##### Minor Changes
- `autoDedupe` deduplicates compatible dependency versions during installation [#​7258](https://github.com/pnpm/pnpm/issues/7258). Enable it in `pnpm-workspace.yaml` or use `pnpm install --auto-dedupe` or `pnpm add --auto-dedupe`. Frozen installs leave the lockfile unchanged.
- `pnpm install`, `pnpm run`, and `pnpm exec` on macOS and Linux now reuse a `node_modules` directory and bin shims that moved or were copied together with their project [#​6937](https://github.com/pnpm/pnpm/issues/6937). The first command after the move checks the tree and records its new location, so project commands in `node_modules/.bin` keep working.
- `pnpm add --save-types` saves available `@types/*` packages in `devDependencies` alongside registry dependencies [#​3868](https://github.com/pnpm/pnpm/issues/3868). Packages that declare bundled TypeScript types are skipped. Set `saveTypes: true` in `pnpm-workspace.yaml` to enable this by default.
- `package.yaml` manifests can now be updated by `pnpm add`, `pnpm update`, `pnpm remove`, `pnpm pkg`, `pnpm link`, `pnpm set-script`, and `pnpm version` [#​2008](https://github.com/pnpm/pnpm/issues/2008). Existing comments and key order are preserved.
- Catalog entries can now use the `file:` and `link:` protocols [#​8642](https://github.com/pnpm/pnpm/issues/8642). A relative path or bare path in an entry, such as `./tarballs/foo.tgz`, is measured from the directory holding `pnpm-workspace.yaml`.
- `pnpm tasks status` lists running and waiting tasks in each concurrency group, and waiting tasks now take available slots in arrival order with higher `priority` tasks going first [#​15208](https://github.com/pnpm/pnpm/issues/15208). If workspaces use different limits for the same group, a later task can take a free slot that earlier tasks cannot use. A package script named `tasks` takes precedence; use `pnpm pm tasks status` when that script exists.
- `pnpm cache prune` deletes registry metadata cache directories that this version of pnpm can no longer read [#​15046](https://github.com/pnpm/pnpm/issues/15046). `pnpm cache prune --dry-run` lists what it would delete without removing anything.
- `macosBackup.excludeModulesDir` and `macosBackup.excludeStoreDir` on macOS can now exclude newly created modules, virtual-store, and package-store directories from Time Machine [#​6440](https://github.com/pnpm/pnpm/issues/6440). Set either to `true` in global configuration or using the `PNPM_CONFIG_MACOS_BACKUP_EXCLUDE_MODULES_DIR` and `PNPM_CONFIG_MACOS_BACKUP_EXCLUDE_STORE_DIR` environment variables.
- `pnpm add --tilde` is now an alias for `--save-prefix=~` [#​12863](https://github.com/pnpm/pnpm/issues/12863). The Yarn `-T` shorthand is not supported.
- `progress` setting and `--no-progress` option now turn off dependency and download progress lines [#​14065](https://github.com/pnpm/pnpm/issues/14065). Warnings, lifecycle output, and the dependency summary are still printed.
##### Patch Changes
##### Security
- POSIX bin shims now take `cygpath` and `wslpath` from the system default path on Cygwin, MSYS2, and WSL2 so a dependency cannot redirect another package's shim [#​14866](https://github.com/pnpm/pnpm/issues/14866).
- `pnpm install` warnings no longer carry the text of a package's deprecation notice, naming only the deprecated package and version [#​15099](https://github.com/pnpm/pnpm/issues/15099). A deprecation warning names the newest non-deprecated version when one exists, and control characters and line separators are stripped from package identifiers and warnings.
- `pnpm install` and other commands that report configuration warnings now warn when environment variables in project `.npmrc` credentials are ignored [#​15051](https://github.com/pnpm/pnpm/issues/15051).
##### Installing packages
- `pnpm install --frozen-lockfile` now succeeds when an optional dependency was unresolvable and skipped by the install that wrote the lockfile [#​3960](https://github.com/pnpm/pnpm/issues/3960).
- `pnpm install --frozen-lockfile` no longer installs dependencies of projects removed from `pnpm-workspace.yaml` [#​15248](https://github.com/pnpm/pnpm/issues/15248). Missing local tarballs used only by those projects no longer fail the install.
- `pnpm ci` now empties `node_modules` before installing in a project that declares a `clean` script [#​15276](https://github.com/pnpm/pnpm/issues/15276).
- `pnpm install --force` now re-imports every package into the virtual store [#​15030](https://github.com/pnpm/pnpm/issues/15030) and removes obsolete dependency links inside virtual-store packages when their dependencies change [#​15039](https://github.com/pnpm/pnpm/issues/15039).
- `preinstall` script for the root project now runs before dependencies are resolved and linked [#​3760](https://github.com/pnpm/pnpm/issues/3760).
- `pnpm install` now runs `pnpm:devPreinstall` when the root project uses `package.yaml` [#​15168](https://github.com/pnpm/pnpm/issues/15168).
- `pnpm install` now enforces the root project's `engines.node` range when `engineStrict` is enabled [#​3016](https://github.com/pnpm/pnpm/issues/3016).
- `pnpm install` now uses the running Node.js when `devEngines.runtime` declares a range without `onFail: download` [#​15230](https://github.com/pnpm/pnpm/issues/15230).
- `pnpm install` no longer hangs when a git dependency is fetched over SSH and ssh prompts for a passphrase or host key confirmation, running ssh in batch mode instead [#​2227](https://github.com/pnpm/pnpm/issues/2227).
- `pnpm install` now installs git-hosted dependencies without preparing them when their builds are explicitly denied by `allowBuilds` [#​10522](https://github.com/pnpm/pnpm/issues/10522).
- `pnpm install` now reuses an in-flight tarball download when another resolution of the same archive still needs its `package.json` [#​15037](https://github.com/pnpm/pnpm/issues/15037).
- `pnpm install --prod` no longer downloads registry packages that only a devDependency reaches [#​881](https://github.com/pnpm/pnpm/issues/881).
- `pnpm install --no-runtime --frozen-lockfile` with `nodeLinker: hoisted` no longer fails on repeated runs with a broken lockfile [#​15212](https://github.com/pnpm/pnpm/issues/15212).
##### Resolving and linking dependencies
- `pnpm install` and `pnpm update` now resolve a dependency range to the newest matching version that is not deprecated [#​15128](https://github.com/pnpm/pnpm/issues/15128).
- `pnpm add <pkg>` without a version now uses the catalog entry when the workspace already catalogs that package [#​14865](https://github.com/pnpm/pnpm/issues/14865).
- `pnpm install` now links workspace dependencies declared with plain version ranges when `excludeLinksFromLockfile` and `linkWorkspacePackages` are enabled [#​15133](https://github.com/pnpm/pnpm/issues/15133).
- `pnpm install` now resolves local tarball dependencies whose absolute `file:` paths contain `..` consistently and skips reinstallation on repeat installs [#​15190](https://github.com/pnpm/pnpm/issues/15190).
- `pnpm install` now installs dependencies when a custom resolver returns a local or git-hosted tarball without a manifest [#​15016](https://github.com/pnpm/pnpm/issues/15016).
- `pnpm.overrides` entries written as a bare path, such as `./local-dep`, are now measured from the directory holding `pnpm-workspace.yaml` [#​11131](https://github.com/pnpm/pnpm/issues/11131).
- `pnpm update --no-save` no longer bypasses version-scoped overrides when a dependency selector specifies a version [#​14923](https://github.com/pnpm/pnpm/issues/14923).
- `pnpm peers check` and strict peer dependency checks no longer reject compatible versions from named registries [#​15225](https://github.com/pnpm/pnpm/issues/15225).
- `pnpm outdated` and `pnpm update --interactive --latest` now include named-registry dependencies such as `work:2.1.0` and preserve their registry prefix [#​15226](https://github.com/pnpm/pnpm/issues/15226).
- Workspace projects selected by `hoistPattern` or `publicHoistPattern` are now hoisted on every install [#​3642](https://github.com/pnpm/pnpm/issues/3642).
- Workspace packages with SemVer build metadata are no longer skipped when they match the requested range and have the same version precedence as the registry package [#​2812](https://github.com/pnpm/pnpm/issues/2812).
- Sped up `pnpm dedupe` and `pnpm install` in projects with many convergence overrides by checking overrides concurrently [#​15175](https://github.com/pnpm/pnpm/issues/15175).
- `minimumReleaseAge` is no longer skipped for packages served by registries returning matching ETags for abbreviated and full package metadata [#​14925](https://github.com/pnpm/pnpm/issues/14925).
##### Running scripts and tasks
- `pnpm run` signal handling no longer delivers a redundant second `SIGINT` to child scripts on `Ctrl+C` in a terminal, and properly forwards termination signals when running non-interactively without a terminal [#​7374](https://github.com/pnpm/pnpm/issues/7374).
- `pnpm run` and `pnpm exec` in workspaces with `sharedWorkspaceLockfile: false` now verify dependencies in the selected projects rather than expecting a root workspace state [#​15272](https://github.com/pnpm/pnpm/issues/15272).
- `pnpm test` now forwards `--filter` arguments to the test script when the option follows the shortcut [#​15217](https://github.com/pnpm/pnpm/issues/15217).
- Recursive runs now start scripts matched by a `/pattern/` selector in parallel within `workspaceConcurrency` [#​14933](https://github.com/pnpm/pnpm/issues/14933).
- `pnpm deploy`, `pnpm rebuild`, `pnpm rb`, and `pnpm setup` now prefer a `package.json` script of the same name [#​14976](https://github.com/pnpm/pnpm/issues/14976).
- `modulesDir` custom directory names now support executable lookup and CommonJS plugin resolution across `pnpm run`, `pnpm exec`, `pnpm version` hooks, and lifecycle scripts [#​3604](https://github.com/pnpm/pnpm/issues/3604).
- `pnpm install-test` now accepts `--no-bail` directly and in recursive runs [#​3777](https://github.com/pnpm/pnpm/issues/3777).
##### Workspace and project configuration
- `pnpm` commands run in a project not included in the workspace now act on that project alone [#​3561](https://github.com/pnpm/pnpm/issues/3561).
- `pnpm-workspace.yaml` edits now preserve scalar YAML anchors and aliases [#​8245](https://github.com/pnpm/pnpm/issues/8245).
- `pnpm-workspace.yaml` now expands environment variable placeholders with fallback syntax in enum-valued settings such as `nodeLinker` [#​14914](https://github.com/pnpm/pnpm/issues/14914).
- `pnpmfile` configuration now loads a `.js` file as CommonJS or an ES module, following the nearest `package.json` [#​15141](https://github.com/pnpm/pnpm/issues/15141).
- `updateConfig` hook settings are now honored by `pnpm peers check`, `why`, `list`, `ll`, `licenses`, `audit`, `sbom`, `fetch`, `patch`, `patch-commit`, `patch-remove`, `approve-builds`, and `runtime` [#​15047](https://github.com/pnpm/pnpm/issues/15047), [#​15049](https://github.com/pnpm/pnpm/issues/15049).
- `readPackage` hook changes or removal now take added dependencies out of `pnpm-lock.yaml` and update dependencies when an existing lockfile is present [#​3735](https://github.com/pnpm/pnpm/issues/3735), [#​15136](https://github.com/pnpm/pnpm/issues/15136).
- `package.yaml` projects now record their pinned pnpm under `packageManagerDependencies` in `pnpm-lock.yaml` [#​15167](https://github.com/pnpm/pnpm/issues/15167).
- `packageManagerDependencies` pinning `@pnpm/exe` beside `pnpm` is no longer rewritten in `pnpm-lock.yaml` [#​14926](https://github.com/pnpm/pnpm/issues/14926).
- `pnpm` now preserves CRLF line endings when modifying project manifests [#​3529](https://github.com/pnpm/pnpm/issues/3529).
- `loglevel` setting is now honored when configured in `pnpm-workspace.yaml`, global configuration, or `PNPM_CONFIG_LOGLEVEL` [#​3122](https://github.com/pnpm/pnpm/issues/3122).
- `storeDir` values loaded from global configuration or `PNPM_CONFIG_STORE_DIR` now expand a leading `~/` to the user's home directory [#​6560](https://github.com/pnpm/pnpm/issues/6560).
- `--shared-workspace-lockfile` now produces a warning when passed on the command line outside a workspace [#​1617](https://github.com/pnpm/pnpm/issues/1617).
##### Windows
- `pnpm install` on Windows now runs dependency build scripts from long global virtual store paths and normalizes scoped package paths in lifecycle script `PATH` entries [#​15111](https://github.com/pnpm/pnpm/issues/15111).
- `pnpm install` across projects sharing a global virtual store on Windows no longer fails with `Access is denied`, file-exists errors, or transient sharing violations [#​15114](https://github.com/pnpm/pnpm/issues/15114), [#​15176](https://github.com/pnpm/pnpm/issues/15176), [#​15171](https://github.com/pnpm/pnpm/issues/15171).
- `pn`, `pnpx`, `pnx`, and `pnpm` now run when Git Bash, MSYS2, or Cygwin launches them through a Windows path [#​14884](https://github.com/pnpm/pnpm/issues/14884).
- `pnpm dlx` now reuses cached packages when Windows creates directory junctions for its cache links [#​15171](https://github.com/pnpm/pnpm/issues/15171).
- `pnpm pipeline --watch` now resolves Windows short paths so multiple path representations share the build cache [#​15105](https://github.com/pnpm/pnpm/issues/15105).
##### CLI commands and output
- `pnpm remove` now runs the project's own `preuninstall`, `uninstall`, and `postuninstall` scripts [#​3276](https://github.com/pnpm/pnpm/issues/3276).
- `pnpm remove -r` now fails before modifying manifests if any requested dependency is absent from all selected projects [#​2319](https://github.com/pnpm/pnpm/issues/2319).
- `pnpm update --peer` now updates ranges in `peerDependencies` [#​8081](https://github.com/pnpm/pnpm/issues/8081).
- `pnpm update` now moves `devEngines.runtime` and `engines.runtime` version ranges to the resolved Node.js version [#​14988](https://github.com/pnpm/pnpm/issues/14988).
- `pnpm update -g` no longer reinstalls unchanged packages [#​12002](https://github.com/pnpm/pnpm/issues/12002).
- `pnpm add -g`, `pnpm update -g`, and `pnpm remove -g` now recover a global package group whose `node_modules` directory was deleted [#​15093](https://github.com/pnpm/pnpm/issues/15093).
- `pnpm add -g` now installs local tarballs when `PNPM_HOME` contains `..` path segments [#​15118](https://github.com/pnpm/pnpm/issues/15118).
- `pnpm version` now reads `tagVersionPrefix` from `pnpm-workspace.yaml`, global config, or `PNPM_CONFIG_TAG_VERSION_PREFIX` when creating and reading Git tags [#​15044](https://github.com/pnpm/pnpm/issues/15044).
- `pnpm publish` now allows a detached Git HEAD in CI environments [#​5894](https://github.com/pnpm/pnpm/issues/5894).
- `pnpm store prune` now removes unreferenced files and packages from the content-addressable store [#​3635](https://github.com/pnpm/pnpm/issues/3635), as well as expired or superseded `pnpm dlx` cache data [#​15171](https://github.com/pnpm/pnpm/issues/15171).
- `pnpm cache list-registries` now prints decoded registry URLs [#​15046](https://github.com/pnpm/pnpm/issues/15046).
- `pnpm deploy` no longer triggers an install when running scripts in a read-only deployed filesystem [#​11617](https://github.com/pnpm/pnpm/issues/11617).
- `pnpm -r list --json` now outputs a single JSON array when `sharedWorkspaceLockfile` is `false`, and `--long` and `--parseable` read each project's own modules directory [#​15011](https://github.com/pnpm/pnpm/issues/15011).
- `pnpm sbom` now validates SPDX identifiers and expressions before emitting them as CycloneDX license IDs or expressions, falling back to a license name for non-SPDX values such as `UNLICENSED` [#​14786](https://github.com/pnpm/pnpm/issues/14786).
- `pnpm change check` now validates pending change intents in `.changeset/` [#​15183](https://github.com/pnpm/pnpm/issues/15183).
- `pnpm --filter` and `pnpm -F` shell completion now suggests workspace package names [#​15216](https://github.com/pnpm/pnpm/issues/15216). Completion candidates containing control or invisible formatting characters are omitted so package and script names cannot inject terminal escape sequences.
- `pnpm run` and `pnpm run-script` shell completion now suggests package scripts [#​15034](https://github.com/pnpm/pnpm/issues/15034).
- `pnpm --version` no longer creates a temporary file in the project directory during store detection [#​15264](https://github.com/pnpm/pnpm/issues/15264).
- `pnpm setup` now describes displayed configuration changes as "The following configuration changes were made" [#​15100](https://github.com/pnpm/pnpm/issues/15100).
- `minimumReleaseAge` approval prompts in `pnpm install` and `pnpm update -g` now count and display each package version once [#​15083](https://github.com/pnpm/pnpm/issues/15083), [#​15091](https://github.com/pnpm/pnpm/issues/15091).
- `.npmrc` authentication warnings now report when an empty environment variable removes an auth token and name the affected key [#​4806](https://github.com/pnpm/pnpm/issues/4806).
- The install summary now names the version each dependency resolved to when `node-linker` is `hoisted` [#​15161](https://github.com/pnpm/pnpm/issues/15161).
- `pnpm install` now re-links a package's global virtual store slot after `allowBuilds` changes [#​15117](https://github.com/pnpm/pnpm/issues/15117).
<!-- sponsors -->
##### Platinum Sponsors
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<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>
</td>
<td align="center" valign="middle">
<a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
<img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<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>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
<img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
</picture>
</a>
</td>
</tr>
</tbody>
</table>
##### Gold Sponsors
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
<img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
<img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<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>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
<img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
<img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
<img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
</picture>
</a>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
<img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<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>
</td>
</tr>
</tbody>
</table>
<!-- sponsors end -->
### [`v12.5.1`](https://github.com/pnpm/pnpm/releases/tag/v12.5.1): pnpm 12.5.1
[Compare Source](https://github.com/pnpm/pnpm/compare/v12.5.0...pnpm@12.5.1)
##### Patch Changes
- pnpm now reports an unknown task setting in `pnpm-workspace.yaml` and carries on. It used to refuse to start, so a project could not use a task setting that only the pnpm version its `packageManager` pins reads. The setting is still an error when the running pnpm is that pinned version.
- Python interpreter installation now retries historical release metadata requests. It caches the release list for up to 24 hours and refreshes it once after a lookup miss. When a release omits the current platform, the search samples at most eight other releases before reporting that the lookup is inconclusive.
- Python `registries` entries now route packages by exact names or trailing-prefix patterns in `packages`. Registry declaration order no longer affects resolution. A matched package resolves exclusively from its assigned registry, including transitive and build dependencies. Use `packages: ["*"]` to declare the default index.
- `pnpm install` no longer fails with "Too many levels of symbolic links" when a Cargo configuration file above the workspace is a symlink, such as a `~/.cargo/config.toml` linked from a dotfiles repository.
- `pnpm install` now returns "Already up to date" in a workspace where `dedupeDirectDeps` left a project without a `node_modules` directory of its own. Such a project forced a full install on every run.
- `pnpm install` no longer refuses the repeat-install fast path just because a changed `pnpm-lock.yaml` is 16 MiB or larger. Such a lockfile forced a full install on the run after every change.
<!-- sponsors -->
##### Platinum Sponsors
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<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>
</td>
<td align="center" valign="middle">
<a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
<img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<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>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
<img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
</picture>
</a>
</td>
</tr>
</tbody>
</table>
##### Gold Sponsors
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
<img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
<img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<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>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
<img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
<img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
<img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
</picture>
</a>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
<img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<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>
</td>
</tr>
</tbody>
</table>
<!-- sponsors end -->
### [`v12.5.0`](https://github.com/pnpm/pnpm/releases/tag/v12.5.0): pnpm 12.5
[Compare Source](https://github.com/pnpm/pnpm/compare/v12.4.2...v12.5.0)
pnpm 12.5.0 makes Python a first-class ecosystem, accepts Package URLs in `pnpm add`, names whole platforms in `supportedArchitectures`, and gives tasks machine-wide concurrency limits. It also fixes an install that could reuse one package's downloaded tarball for another.
##### Minor Changes
##### Installing packages
- `pnpm add` accepts a [Package URL](https://github.com/package-url/purl-spec) in place of a package name. `pnpm add pkg:npm/express@4.18.2` saves `express` to `package.json`. `pnpm add pkg:cargo/serde@1.0.188` saves `serde` to `Cargo.toml`. `pnpm add pkg:pypi/requests@2.31.0` saves `requests` to `pyproject.toml`. `pkg` is now a reserved specifier prefix, whatever case it is written in, so a named registry can no longer be called `pkg`.
- A `registries` entry can now name the ecosystem it serves.
```yaml
registries:
https://internal.example/simple/:
ecosystem: pypi
https://pypi.org/simple/:
ecosystem: pypi
https://index.crates.io/:
ecosystem: cargo
```
`ecosystem` accepts `npm`, `cargo` and `pypi`. An entry that does not name one serves npm, as every entry did before.
An ecosystem with several indexes searches them in the order they are declared. The first index that has a package supplies it, so the one declared last answers what none before it had.
A `registries` entry may not carry credentials. pnpm reads them from `.npmrc`, matched by origin, for a PyPI index as for every other package source.
##### Configuring pnpm
- `supportedArchitectures` now accepts a list of platforms, in place of the `os`, `cpu` and `libc` axes.
```yaml
supportedArchitectures:
- linux-x64
- darwin-arm64
- win32-x64
```
An install prepares for the platforms the list names, and for those only. A platform reads as `<os>-<cpu>`, with a C library on Linux, as in `linux-x64-musl` or `linux-x64-manylinux_2_28`. The Rust target triple of the same machine is accepted too, so `x86_64-unknown-linux-gnu` names the platform `linux-x64` names. A Linux platform that names no C library is the glibc platform. `current` is the platform the install runs on.
The `os`, `cpu` and `libc` mapping keeps working and keeps its meaning.
- Added concurrency groups for tasks. A task in `pnpm-workspace.yaml` can name a `concurrencyGroup`. The new `concurrencyGroups` setting gives each group a limit. At most that many tasks of the group run at once on the machine, counted across every pnpm process, `pnpm pipeline` included. A task past the limit waits for a running one to finish. A script that calls `pnpm run` for a task of the same group runs under the slot its parent holds.
```yaml
tasks:
test:rust:
concurrencyGroup: cargo
concurrencyGroups:
cargo: 2
```
- `tools` names the programs pnpm downloads, and `mirror` says where each one comes from.
```yaml
tools:
node:
mirror: https://mirror.example.com/node/download
channels:
nightly: https://nightly.example.com/
bun:
mirror: https://mirror.example.com/bun
python:
mirror: https://mirror.example.com/python-build-standalone/releases
```
`node`, `bun` and `python` can be named. Any other tool is refused.
`mirror` is the base a tool's own layout hangs off.
`channels` sends one release channel elsewhere. A channel neither it nor `node-mirror:<channel>` names is left to `mirror`. Only `node` publishes channels, so naming them for another tool is refused.
Set it in the global `config.yaml` or in `PNPM_CONFIG_TOOLS`. A `pnpm-workspace.yaml` that names a tool mirror is ignored.
`pnpm pack-app` downloads the Node.js it embeds through `tools.node`. `node-mirror:<channel>` keeps working and names the same thing as an entry under `channels`.
##### Python interpreters and environments
- `pnpm install` now chooses a Python interpreter for each project instead of installing every project with one interpreter [#​14945](https://github.com/pnpm/pnpm/issues/14945). A project is installed with the first interpreter on the machine that its `requires-python` accepts, so a workspace can hold projects that support different Python versions. pnpm reads `.python-version` too, and prefers the version it asks for. Set `python.executable` in `pnpm-workspace.yaml` to name one interpreter for every project.
- `pnpm install` now installs a Python interpreter when no interpreter on the machine fits the project [#​14945](https://github.com/pnpm/pnpm/issues/14945). The builds are [python-build-standalone](https://github.com/astral-sh/python-build-standalone)'s, which uv and rye install too. One interpreter is shared by every project on the machine, and a later install uses it without downloading anything. `runtimeOnFail` decides what an install with no interpreter that fits does, the way it does for a Node.js runtime. `error` reports the project instead of installing one. `warn` and `ignore` install with an interpreter the machine has that the project's `requires-python` rejects. `tools.python.mirror` names a mirror.
- Python environments now live in the store. Each project keeps only its `.venv` link, which points at the project's current environment generation under `python-envs` in the store. A repository with many Python projects no longer holds a `.pnpm/python-envs` directory in each of them. The next install relinks a `.venv` that an earlier release published. The old `.pnpm/python-envs` directory is left in place, since a running program may still use it, and can be deleted once none does. With `frozenStore` set, pnpm writes nothing to the store, so environments stay in the project's `.pnpm/python-envs` [#​15014](https://github.com/pnpm/pnpm/issues/15014).
- Python environments now use `packageImportMethod` to import wheel files from the store. Use `clone-or-copy` for copy-on-write clones with a copy fallback, or `copy` for independent files. Hardlinked files share writes with the store and other environments.
Isolated Python build environments keep backend writes private with copy-on-write clones or copies.
##### Python projects and workspaces
- `pnpm install` now installs a Python project's own package, so the project can be imported and the commands in `[project.scripts]` run right after an install [#​14945](https://github.com/pnpm/pnpm/issues/14945). The installed package points at the source tree, so an edit to a module takes effect without another install. pnpm installs the package of a project that declares a `[build-system]`. `tool.uv.package` overrides that either way.
- `pnpm install` now installs a Python project in the workspace from its own source. Declare it under `[tool.uv.sources]`, as `shared = { workspace = true }` or `shared = { path = "../shared", editable = true }`. pnpm builds the project with the backend it declares. It installs the build editable, so an edit to the project takes effect without another install.
Approve the build backend under `allowBuilds` in `pnpm-workspace.yaml` as a Package URL, as `pkg:pypi/hatchling: true`. An install that has not approved a backend does not build the projects that need it. The message names the key to add.
`pnpm install` now refuses a requirement that names a project in the workspace when nothing declares where it comes from. It used to take that name from the index.
- The members of a uv workspace can now share one Python environment. Set `shared-environment = true` under `[tool.pnpm.python]` in the `pyproject.toml` that declares `[tool.uv.workspace]`. `pnpm install` then resolves every member as one graph into one `pylock.toml` and one `.venv` at the workspace root. Two members that require versions of a distribution no release satisfies at once are refused with an error naming both. Each project still gets an environment of its own by default [#​15015](https://github.com/pnpm/pnpm/issues/15015).
- Python projects can now select extras and dependency groups through `[tool.pnpm.python]` in `pyproject.toml` [#​14945](https://github.com/pnpm/pnpm/issues/14945). Workspace `python.extras` and `python.groups` defaults now skip names a project does not define.
- `pnpm install` now reads dynamic Python project metadata from the build backend [#​14945](https://github.com/pnpm/pnpm/issues/14945). Projects with only a `requirements.txt` file now get a Python environment and lockfile.
##### Python dependencies and lockfiles
- pnpm can now resolve `pylock.toml` for several platforms and Python versions at once. `supportedArchitectures` names the platforms to lock for and `python.versions` the versions. Every platform is paired with every version. One committed lockfile then serves Linux CI and macOS or Windows contributors [#​14945](https://github.com/pnpm/pnpm/issues/14945).
```yaml
supportedArchitectures:
- linux-x64-manylinux_2_28
- darwin-arm64
- win32-x64
python:
enabled: true
versions: ['3.12', '3.13']
```
The lockfile pins the wheel each environment takes for a distribution. It marks a package only some environments install. `pnpm install` takes the packages and wheels of the environment its interpreter matches, and refuses an interpreter none of them stand for. pnpm resolves a project that declares environments itself, not through the server `pnprServer` names. Naming neither setting locks for the interpreter running the install.
- `python.overrides` and `python.constraints` pin the versions a Python resolution may pick [#​14945](https://github.com/pnpm/pnpm/issues/14945). pnpm reads uv's own overrides and constraints from `pyproject.toml` too.
- `pnpm install` now supports Python dependencies from Git repositories [#​14945](https://github.com/pnpm/pnpm/issues/14945). Direct wheel URLs are also supported. Sources can be declared in `[tool.uv.sources]`. Git dependencies require `allowBuilds` approval.
- `pnpm install` can install a Python release that publishes no wheel this interpreter accepts, by building the source distribution the index serves beside it [#​14945](https://github.com/pnpm/pnpm/issues/14945). The archive is pinned in `pylock.toml` by name and SHA-256. A later install replays it from the store, offline included. Building a source distribution runs the release's own build backend. Approve it with `pkg:pypi/<distribution>: true` under `allowBuilds`.
A resolution that finds no version of a distribution now says why. It tells apart a distribution no index publishes, one whose releases publish nothing this interpreter can install, and one whose versions the project's requirements exclude.
##### Patch Changes
##### Installing packages
- pnpm no longer reuses one package's downloaded tarball for another package whose resolution pins a different integrity hash to the same URL [#​15021](https://github.com/pnpm/pnpm/issues/15021).
- `pnpm install` and `pnpm add` now report an error when `package.json`, `pnpm-lock.yaml`, `pyproject.toml` or another file they snapshot before installing is a named pipe or a device. The command used to wait forever for something to write to it.
- `pnpm install --prod` and `pnpm install --dev` now record every dependency group in `pnpm-lock.yaml`. `node_modules` still holds only the groups the filter selects. They used to write the filter into the lockfile, so a later `pnpm install --frozen-lockfile` rejected it. `pnpm prune --prod`, `pnpm prune --dev`, and `pnpm prune --no-optional` behave the same way [#​14912](https://github.com/pnpm/pnpm/issues/14912).
- POSIX bin shims now convert a Windows-form path such as `C:\node_modules\.bin\tsc` correctly. The shim mangled the backslashes in such a path and could not reach the package it runs. Installing again replaces the shims already in `node_modules` [#​14867](https://github.com/pnpm/pnpm/issues/14867).
- Two pnpm processes installing one workspace at the same time no longer fail on Windows with "Access is denied" while writing `node_modules/.pnpm-workspace-state-v1.json`. The write now retries the transient lock the other process holds, as pnpm's other file writes do.
- pnpm now reads the manifest from the tarball when a pnpmfile `resolvers` hook returns a resolution without one. Such a package installed alone, with none of its own dependencies and no warning [#​15000](https://github.com/pnpm/pnpm/issues/15000).
- `pnpm install` now merges Git conflict markers in `pnpm-lock.yaml`. It parses both sides of the conflict and keeps the versions they locked. A conflict in the config dependencies recorded at the top of the lockfile is merged too [#​14880](https://github.com/pnpm/pnpm/issues/14880).
##### Cargo projects
- `pnpm install` can now generate `Cargo.lock` for workspaces with path or Git `[patch]` and `[replace]` overrides. Adding, removing, and updating crates also preserve these overrides [#​14950](https://github.com/pnpm/pnpm/issues/14950).
Cargo lockfile resolution blocks unsupported Git transport helpers declared by transitive dependencies.
- `pnpm install` now vendors recursive Git submodules for Cargo dependencies at their pinned commits. Cargo builds can use these sources offline. Set Git's `protocol.file.allow` to `always` to fetch local file submodules. pnpm fetches cached Git crates again on the first online install [#​14951](https://github.com/pnpm/pnpm/issues/14951).
- `pnpm install` now generates `Cargo.lock` for workspaces with Git dependencies, including a dependency that omits a package version. It also downloads the Rust standard library's dependencies when Cargo configuration enables `build-std` [#​14944](https://github.com/pnpm/pnpm/issues/14944).
- `pnpm install` now handles weak Cargo features, written `crate?/feature`. Resolution failed when one dependency turned on an optional crate and another asked for a weak feature of it [#​14960](https://github.com/pnpm/pnpm/issues/14960). The generated `Cargo.lock` now also includes the dependencies weak features reference, which Cargo rejected with `--locked` for crates such as `uuid` [#​14978](https://github.com/pnpm/pnpm/issues/14978).
- `pnpm install` now generates `Cargo.lock` when a crate version it considers depends on a release the registry carries only as yanked. pnpm rules that version out and resolves the rest of the graph. Resolution failed with an error such as `no non-yanked version of napi-build satisfies ^3.0.0-beta` [#​14952](https://github.com/pnpm/pnpm/issues/14952).
- `pnpm install` now falls back to an older semver-incompatible version of a crate when the newest one a dependency range allows cannot be resolved. Ranges such as `>=1, <3` span several of them [#​14962](https://github.com/pnpm/pnpm/issues/14962).
##### Python projects
- `pnpm install` now honors uv workspace members when discovering Python projects. When no uv workspace declares a project, pnpm skips projects under conventional example, demo, documentation, template, `test`, `tests`, and test fixture directories [#​15058](https://github.com/pnpm/pnpm/issues/15058).
- `pnpm install --filter <selector>` now installs only the Python projects the selection asks for. A Python project that shares a directory with an npm workspace project is selected with that project. A Python project in a directory of its own is selected by the distribution it declares, by its path, or through the `[tool.uv.sources]` entries that reach it. Under `--fail-if-no-match`, a selector that names only a Python project is a match. `pnpm add --filter <selector> pypi:<package>` writes the requirement to every selected project [#​14945](https://github.com/pnpm/pnpm/issues/14945).
- `pnpm install` now installs wheels whose `RECORD` hashes disagree with their contents. The wheel archive's locked SHA-256 hash remains verified. pnpm writes correct hashes to the installed `RECORD` [#​15061](https://github.com/pnpm/pnpm/issues/15061).
- `pnpm install` now installs a Python wheel whose `WHEEL` file lists tags that differ from the ones in its filename. A wheel whose filename tags were changed after the build, such as `mysql-connector-python`, was rejected [#​14945](https://github.com/pnpm/pnpm/issues/14945).
- A Python release whose wheel metadata declares a requirement pnpm cannot read no longer fails the install. pnpm now resolves the project against the other releases of that package, and reports the unreadable requirement when none of them works.
- `pnpm install` no longer fails when a Python index lists a file pnpm cannot use, such as a release with no SHA-256 digest or an unreadable wheel filename. That file is left out and the project resolves against the remaining releases.
- `pnpm add pypi:<package>` in a directory that has no `pyproject.toml` now names the missing file and says where to run the command. It used to fail with a bare `No such file or directory (os error 2)` [#​14945](https://github.com/pnpm/pnpm/issues/14945).
##### Performance
- `pnpm audit` no longer hangs on dependency graphs with many shared dependencies [#​15005](https://github.com/pnpm/pnpm/issues/15005).
- Sped up `pnpm install` in Python workspaces with many projects. Projects now prepare concurrently. Projects with identical registry requirements also share fresh dependency resolutions [#​14945](https://github.com/pnpm/pnpm/issues/14945).
- Repeat installs through the Node-API bindings now return "Already up to date" when the project manifests still match `pnpm-lock.yaml`. Before, every such install reinstalled the whole tree. An install also no longer reinstalls when `pnpm-lock.yaml` differs from the installed dependencies only by packages no project depends on or by top-level keys pnpm does not define.
##### Other commands
- `pnpm deploy` now links commands exposed by workspace dependencies into the deployed project's `node_modules/.bin` directory [#​14899](https://github.com/pnpm/pnpm/issues/14899).
- `pnpm dlx` and `pnx` now prompt to approve dependency build scripts in interactive terminals [#​14943](https://github.com/pnpm/pnpm/issues/14943). Cached packages with pending builds also prompt for approval. Without an interactive terminal, use `--allow-build` to allow the required builds.
- `pnpm add -g` and `pnpm update -g` now ignore incomplete unrelated global package groups when every command from the replaced group is retained. Operations that could remove a global command still require complete ownership information.
- `pnpm pack` now writes tarball entries grouped by file extension and file name, the order npm uses. Packages that ship many same-named files, such as template collections, pack much smaller [#​14766](https://github.com/pnpm/pnpm/issues/14766).
- `pnpm outdated --long` fills the Details column with the package homepage again [#​14886](https://github.com/pnpm/pnpm/issues/14886).
<!-- sponsors -->
##### Platinum Sponsors
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<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>
</td>
<td align="center" valign="middle">
<a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
<img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<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>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
<img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
</picture>
</a>
</td>
</tr>
</tbody>
</table>
##### Gold Sponsors
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
<img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
<img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<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>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
<img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
<img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
<img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
</picture>
</a>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
<img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<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>
</td>
</tr>
</tbody>
</table>
<!-- sponsors end -->
### [`v12.4.2`](https://github.com/pnpm/pnpm/releases/tag/v12.4.2): pnpm 12.4.2
[Compare Source](https://github.com/pnpm/pnpm/compare/v12.4.1...v12.4.2)
pnpm 12.4.2 includes security fixes for executable shims and GitHub Actions links, more reliable installs, faster peer dependency checks in workspaces, and Python lockfiles that work across compatible targets.
##### Patch Changes
##### Security
- Dependency executables can no longer take over another package's POSIX bin shim through its shell helpers. Reinstall dependencies to replace existing shims [#​14837](https://github.com/pnpm/pnpm/issues/14837).
On Cygwin, MSYS2, and WSL, shims still use `PATH` for Windows path conversion, so dependency executables can still redirect them there.
- GitHub Actions homepage links no longer expose server credentials. GitHub server URLs now require HTTPS, with HTTP allowed only for loopback hosts.
##### Installing packages
- pnpm no longer crashes at startup on FreeBSD and other Unix-like platforms. Platforms other than Windows and macOS use `~/.local/share/pnpm/store` by default [#​14859](https://github.com/pnpm/pnpm/issues/14859).
- `pnpm install` on Windows no longer fails with `ERR_PNPM_PACKAGE_MANAGER_REMOVE_MODULES_DIR` when clearing `node_modules` containing linked dependencies, such as when changing `nodeLinker` [#​14790](https://github.com/pnpm/pnpm/issues/14790).
- `pnpm install <pkg>` now accepts `--prod` and `--dev`, including `--prod=false` [#​14868](https://github.com/pnpm/pnpm/issues/14868).
- `pnpm install` and `pnpm update` now honor `--ignore-workspace` in nested projects excluded from the surrounding workspace. The flag also skips that workspace's settings during the `packageManager` check [#​14809](https://github.com/pnpm/pnpm/issues/14809).
- `pnpm install` on macOS no longer reuses stale files for `file:` tarball or git-hosted tarball dependencies.
- `pnpm install` in a single-project directory now detects `package.json` edits made while the previous install was finishing [#​14890](https://github.com/pnpm/pnpm/issues/14890).
- `pnpm install --frozen-lockfile` now removes packages no longer reachable from any project in `pnpm-lock.yaml`. This also prevents repeated lifecycle script execution and unnecessary installs before `pnpm run` and `pnpm exec` with `verifyDepsBeforeRun` [#​14891](https://github.com/pnpm/pnpm/issues/14891).
##### Resolving and updating dependencies
- Node.js runtime resolution now reports network failures from unofficial-builds.nodejs.org. These failures previously omitted musl builds from `pnpm-lock.yaml`, making its contents depend on network access [#​14813](https://github.com/pnpm/pnpm/issues/14813).
- `pnpm install` now rejects invalid `peerDependencies` specifiers with `ERR_PNPM_INVALID_PEER_DEPENDENCY_SPECIFICATION`. A value such as `"foo": "foo@1.0.0"` previously created a broken directory link [#​14791](https://github.com/pnpm/pnpm/issues/14791).
- `pnpm deploy` now writes plain registry versions in the deployed `package.json`, without peer dependency suffixes. The lockfile retains peer bindings, and npm aliases retain their target package names [#​14873](https://github.com/pnpm/pnpm/issues/14873).
- `pnpm add <git repository>` now names repositories without a `package.json` as `@owner/repo`, allowing dependencies on equally named repositories from different owners [#​14870](https://github.com/pnpm/pnpm/issues/14870).
- Peer dependency resolution now deduplicates packages whose child dependency resolves an optional peer in only some workspace projects, such as `next` with `styled-jsx`'s optional `babel-plugin-macros` peer [#​14800](https://github.com/pnpm/pnpm/issues/14800).
- `pnpm update` now settles the lockfile in one run when an upgrade removes the package providing an optional peer dependency [#​14895](https://github.com/pnpm/pnpm/issues/14895).
- `pnpm update --no-save` now preserves override-applied specifiers for dependencies it is not updating, preventing subsequent frozen installs from failing with `ERR_PNPM_OUTDATED_LOCKFILE` [#​14836](https://github.com/pnpm/pnpm/issues/14836).
- `pnpm update --no-save` now succeeds under `minimumReleaseAgeStrict` when every resolved version is old enough [#​14835](https://github.com/pnpm/pnpm/issues/14835).
##### Performance
- Workspace installs and `pnpm peers check` are faster when projects depend on each other, fixing a slowdown introduced in 12.3.0. Unmet peer dependencies of workspace packages are now reported only under projects that link them directly [#​14906](https://github.com/pnpm/pnpm/issues/14906).
- Hoisted installs use less memory when packages are cached. Frozen-lockfile hoisted installs on macOS are also faster when reusable package directories are cached.
##### Python projects
- `pnpm install --frozen-lockfile` now reuses `pylock.toml` across compatible Python targets, including after kernel updates. Reuse requires unchanged requirements, index, and `requires-python`, compatible wheels, and a locked dependency graph matching the target's markers [#​14843](https://github.com/pnpm/pnpm/issues/14843).
The lockfile's `environments` marker now includes only the interpreter version and marker variables used by the dependency graph. Without `--frozen-lockfile`, pnpm warns and resolves again when the locked graph no longer matches the target.
- Python resolution no longer fails on malformed `Requires-Python` values, such as the trailing comma in `openpyxl` 3.0.x. pnpm treats these releases as declaring no interpreter range [#​14910](https://github.com/pnpm/pnpm/issues/14910).
- `pnpm add pypi:...` now rejects unsupported `--save-prefix` values before editing the manifest or resolving dependencies.
##### Workspaces and scripts
- Scripts listed in `syncInjectedDepsAfterScripts` no longer fail with `ERR_PNPM_INJECTED_DEPS_SYNC_READ_DIR` when the lockfile contains an injected package copy that no project depends on.
- `shellEmulator` now expands `${VAR}`, `${VAR:-default}`, and `${VAR:+alternative}` in scripts [#​14814](https://github.com/pnpm/pnpm/issues/14814).
- Cargo and Python project discovery now honors `!` exclusions in `pnpm-workspace.yaml` `packages`, skipping both parsing and generated source configuration for excluded projects [#​14844](https://github.com/pnpm/pnpm/issues/14844).
- `pnpm --filter "./packages/{app,lib}"` now selects either alternative. Brace alternatives can nest, span path separators, and combine with other wildcards.
- GitHub Actions updates now stop if an action reference changes during version resolution, and preserve unrelated workflow edits.
##### CLI and output
- `pn`, `pnpx`, and `pnx` now run the pnpm installed alongside them, even when that directory is absent from `PATH` or another pnpm comes first [#​14803](https://github.com/pnpm/pnpm/issues/14803).
- `pnpm --version` now reports failures to install or record a project's pinned pnpm, then prints the running CLI's version. It also honors `--store-dir` and `--store` [#​14831](https://github.com/pnpm/pnpm/issues/14831).
- `pnpm self-update` no longer reinstalls the active version when it was installed by the standalone installation script [#​14823](https://github.com/pnpm/pnpm/issues/14823).
- `pnpm t` and `pnpm tst` work again as aliases for `pnpm test`.
- `pnpm sbom` now emits valid repository URLs in CycloneDX `externalReferences[].url` and SPDX `homepage`. Shorthands such as `vercel/ms` become `git+https` URLs, embedded credentials are removed, and invalid repository values are omitted [#​14773](https://github.com/pnpm/pnpm/issues/14773).
<!-- sponsors -->
##### Platinum Sponsors
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<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>
</td>
<td align="center" valign="middle">
<a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
<img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<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>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
<img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
</picture>
</a>
</td>
</tr>
</tbody>
</table>
##### Gold Sponsors
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
<img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
<img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<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>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
<img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
<img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
<img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
</picture>
</a>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
<img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<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>
</td>
</tr>
</tbody>
</table>
<!-- sponsors end -->
### [`v12.4.1`](https://github.com/pnpm/pnpm/releases/tag/v12.4.1): pnpm 12.4.1
[Compare Source](https://github.com/pnpm/pnpm/compare/v12.4.0...v12.4.1)
pnpm 12.4.1 fixes installs that failed on filesystems refusing hard links or clones, on Android, and under `nodeLinker: hoisted`. Repeat installs are faster.
##### Patch Changes
##### Installing packages
- `pnpm install` no longer fails with `Operation not permitted` when the filesystem refuses a hard link or a copy-on-write clone [#​14722](https://github.com/pnpm/pnpm/issues/14722). Under `packageImportMethod: auto` and `clone-or-copy`, pnpm copies the file instead. EdenFS checkouts, which have no hard links, and rootless containers, which refuse the clone syscall, both hit this. An explicit `packageImportMethod: hardlink` or `clone` still reports the error.
pnpm also copies a package file whose store entry has reached the filesystem's limit on names for one file, 1024 on NTFS and 65000 on ext4. Such a file failed the install under `packageImportMethod: hardlink`, and under `auto` it stopped pnpm hard linking for the rest of the install.
- `pnpm install` no longer writes a package file through a symlink left at the path it is importing to. Copying such a file overwrote whatever the link pointed at, and created that file when the link pointed nowhere. An executable package file also made the link's target executable.
- Fixed `pnpm install` and `pnpm dlx` on Android. Registry requests crashed because pnpm found no system CA certificates, so pnpm uses bundled ones there [#​14777](https://github.com/pnpm/pnpm/issues/14777). Imports also failed with "Permission denied" on filesystems that deny hard links and reflinks, and now fall back to copying [#​14780](https://github.com/pnpm/pnpm/issues/14780).
- `pnpm install` no longer fails with "Invalid cross-device link" while preserving a package's nested `node_modules` directory during a Docker build [#​14758](https://github.com/pnpm/pnpm/issues/14758).
- `pnpm install` no longer fails on a package tarball that carries a file at the archive root, such as the `._*` entries macOS `tar` adds [#​14701](https://github.com/pnpm/pnpm/issues/14701). The file is installed at the root of the package.
A `file:` tarball packed without the usual `package/` directory is now recorded under the name and version from its own `package.json`. It was recorded under the alias the dependency was given, at version 0.0.0.
- Under `nodeLinker: hoisted`, `pnpm install` no longer re-imports packages that are already in place. A repeat install replaced the whole `node_modules` tree and reported `Packages: +N`. A package is still imported when its directory is missing, when its `package.json` no longer carries the installed version, when it is a `file:` dependency, and when it is patched. Lifecycle scripts no longer run again for a package left in place, and `pnpm rebuild` and a change to `allowBuilds` still reach it.
- `pnpm install` now runs a dependency's build scripts again when its side-effects cache entry has no files to restore [#​14717](https://github.com/pnpm/pnpm/issues/14717). Such builds were skipped and nothing was put in their place, so a script whose whole effect lands outside its own package directory, such as a git hook installer, never took effect. pnpm no longer publishes empty artifacts to the shared side-effects cache either.
##### Resolving and linking dependencies
- `pnpm install`, `pnpm add`, and `pnpm dedupe` now apply `ignoredOptionalDependencies` [#​14729](https://github.com/pnpm/pnpm/issues/14729). Matching optional dependencies are left out of the lockfile and are not installed. pnpm 12 installed them whenever it resolved dependencies from scratch.
- `pnpm install` no longer links a transitive dependency to a workspace package when `linkWorkspacePackages` is `true` and the dependency is declared with a plain version range [#​14781](https://github.com/pnpm/pnpm/issues/14781). Enabling `preferWorkspacePackages` does not change this. Set `linkWorkspacePackages: deep` to link them.
- `pnpm install` no longer leaves dangling dependency links in workspace packages located above the workspace root [#​14726](https://github.com/pnpm/pnpm/issues/14726).
- `pnpm install` and `pnpm add` no longer leave a dangling symlink in `node_modules` when a project starts depending directly on a package that the lockfile holds only as a transitive dependency with resolved peer dependencies [#​14714](https://github.com/pnpm/pnpm/issues/14714).
- `pnpm dedupe` now keeps a compatible auto-installed peer when another workspace project depends on a newer major [#​14697](https://github.com/pnpm/pnpm/issues/14697). Repeated runs alternated between compatible and incompatible peer versions.
- `pnpm peers check` no longer reports a peer dependency declared as `workspace:^`, `workspace:~`, or a bare `workspace:` as unmet [#​14770](https://github.com/pnpm/pnpm/issues/14770). pnpm reported these as unmet whatever version the linked workspace project supplied.
##### Performance
- Sped up repeat installs [#​14540](https://github.com/pnpm/pnpm/issues/14540). pnpm checks the store's files only for the packages it links into `node_modules`, instead of every package in the lockfile. Creating the command shims in `node_modules/.bin` makes about 1,500 fewer filesystem calls in a 76 project workspace. Installs that use the global virtual store read their slot paths from the cache directory instead of deriving them every time. Verifying a large lockfile also allocates less memory.
- Sped up `pnpm install` in Cargo workspaces with many member crates. Repeated installs reuse verified Cargo checksum metadata.
- Installing several packages from the same Git repository and commit now downloads the source once per install [#​14725](https://github.com/pnpm/pnpm/issues/14725). Each package still runs its prepare scripts in its own copy of the checkout.
##### Running scripts and tasks
- pnpm now passes Ctrl+C on to the script or command it started and waits for it to shut down [#​14723](https://github.com/pnpm/pnpm/issues/14723). pnpm exited first, so a script that was still writing landed on the shell prompt.
- `pnpm run "/pattern/" --no-bail` now lets every matched script finish after one of them fails [#​14718](https://github.com/pnpm/pnpm/issues/14718). The command exits with `ERR_PNPM_RUN_FAILED`, and its message lists the scripts that failed in the order they were selected.
- `pnpm pipeline` no longer fails on a project that tracks a symlink, such as a `CLAUDE.md` pointing at `AGENTS.md` [#​14692](https://github.com/pnpm/pnpm/issues/14692). Changing a symlinked input's target invalidates that task's cache, and `pnpm pipeline --no-cache` no longer hashes task inputs.
##### Commands
- `pnpm add -g`, `pnpm update -g`, and `pnpm remove -g` no longer change global bins or install directories after reading only part of an installed package group [#​13796](https://github.com/pnpm/pnpm/issues/13796). If any declared package manifest is missing, malformed, or unreadable, pnpm now fails before it activates or removes anything and leaves the existing global installation intact.
- `pnpm dedupe` now processes every workspace project by default, including workspaces that keep a separate lockfile per project [#​14732](https://github.com/pnpm/pnpm/issues/14732). Workspace filters select which projects it processes, and `--fail-if-no-match` exits with an error when no project matches.
- `pnpm update <name>@<version>` now keeps the range operator the manifest declares [#​14745](https://github.com/pnpm/pnpm/issues/14745). Running `pnpm update react@19.3.0` on `"react": "^19.2.8"` writes `"react": "^19.3.0"`. A `jsr:` entry keeps its `jsr:` prefix, and a plain `pnpm update` now moves a `jsr:` range the way it moves an npm range.
- `pnpm --filter` directory selectors now support `?` wildcards and character classes such as `[ab]`. A `*` or `?` wildcard no longer selects a directory whose name starts with a dot, as on pnpm 11.
- `pnpm deploy --legacy` now prefers the dependency versions pinned in the source workspace lockfile when they still satisfy the deployed project's range [#​13857](https://github.com/pnpm/pnpm/issues/13857).
- `pnpm sbom` now leaves out a package's author field when the manifest author name is empty or contains only whitespace [#​14685](https://github.com/pnpm/pnpm/issues/14685). In a filtered or split workspace run, only a project with no `author` field inherits the workspace root's author.
`pnpm sbom --sbom-format spdx` now writes `creationInfo.created` with whole seconds, such as `2026-09-08T10:38:21Z` [#​14684](https://github.com/pnpm/pnpm/issues/14684). The fractional seconds it carried were rejected by strict SPDX consumers.
##### Configuration
- The `updateConfig` pnpmfile hook now receives the resolved configuration, including settings that came from `.npmrc`, the command line, or a default [#​14676](https://github.com/pnpm/pnpm/issues/14676). Scoped registries are reported under `registriesByScope`, and a hook may rewrite that map to change where packages are fetched from. Registry credentials are reported under `configByUri`, as pnpm 11 reports them. An unset setting is left out rather than reported as `null`.
- `pnpm audit --fix` and the `minimumReleaseAgeStrict` approval prompt now keep the comments in `minimumReleaseAgeExclude` when they append an entry to it in `pnpm-workspace.yaml`. The rest of the list is left as written, and the `trustPolicyExcludePrune` and `minimumReleaseAgeExcludePrune` cleanups keep the comments of the entries they retain.
`pnpm install` and `pnpm dedupe` now run those cleanups too [#​14759](https://github.com/pnpm/pnpm/issues/14759). Only `pnpm add`, `pnpm update`, and `pnpm remove` pruned the entries that the freshly written lockfile no longer resolves.
- `pnpm config set --global node-download-mirrors` no longer rejects the key [#​13611](https://github.com/pnpm/pnpm/issues/13611). The global config file already accepted `nodeDownloadMirrors`, but the command refused to write it.
- `NO_PROXY` entries that start with a dot, such as `.npmjs.org`, now bypass the proxy for the domain and its subdomains [#​14686](https://github.com/pnpm/pnpm/issues/14686).
- pnpm no longer creates a project `pnpm-lock.yaml` when `devEngines.packageManager.onFail` is `download` and lockfile writing is off through `lockfile: false` or `--no-lockfile` [#​14728](https://github.com/pnpm/pnpm/issues/14728). pnpm still switches to the pinned version.
- pnpm now writes `node_modules/.package-map.json` only when `nodeExperimentalPackageMap` is enabled. Nothing reads the file without that setting, and an install that stops writing the map removes the one a previous install left.
##### Windows
- `pnpm pipeline` no longer fails with intermittent access denied errors when concurrent tasks save their cache entries on Windows.
- Windows filesystem operations now retry permission errors for up to one second [#​14682](https://github.com/pnpm/pnpm/issues/14682). A permanent permission error delayed the failure by a minute. Sharing and lock violations keep their one minute retry budget.
##### Messages and output
- pnpm now warns when the root `package.json` declares a non-empty `workspaces` array and the project has no `pnpm-workspace.yaml` [#​2255](https://github.com/pnpm/pnpm/issues/2255). Such an install linked no project and said nothing about why.
- `ERR_PNPM_PACKAGE_MANAGER_REMOVE_MODULES_DIR` now names the file or directory in `node_modules` that pnpm could not clean up. It reported only the underlying OS error, such as "Access is denied (os error 5)".
- `pnpm --help` no longer describes pnpm as experimental.
<!-- sponsors -->
##### Platinum Sponsors
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<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>
</td>
<td align="center" valign="middle">
<a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
<img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<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>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
<img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
</picture>
</a>
</td>
</tr>
</tbody>
</table>
##### Gold Sponsors
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
<img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
<img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<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>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
<img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
<img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
<img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
</picture>
</a>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
<img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<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>
</td>
</tr>
</tbody>
</table>
<!-- sponsors end -->
### [`v12.4.0`](https://github.com/pnpm/pnpm/releases/tag/v12.4.0): pnpm 12.4
[Compare Source](https://github.com/pnpm/pnpm/compare/v12.3.4...v12.4.0)
##### Minor Changes
- pnpm can now manage npm, Python, and Cargo dependencies in the same workspace. Enable `python.enabled` or `cargo.enabled` in `pnpm-workspace.yaml`, then use `pnpm install` to install them together.
- Add Python packages with `pnpm add pypi:<package>`. pnpm uses `pyproject.toml`, `pylock.toml`, and a managed `.venv`. Frozen and offline installs are supported, and `pnpm run` and `pnpm exec` make the environment's executables available [#​14566](https://github.com/pnpm/pnpm/issues/14566).
- Add Rust crates with `pnpm add crate:<package>`. pnpm supports crates.io and custom sparse registries configured with `cargo.indexUrl`. Registry authentication supports pnpm credentials and, for crates.io, `CARGO_REGISTRY_TOKEN` or `$CARGO_HOME/credentials.toml`.
Both ecosystems support faster dependency resolution through `pnprServer`, with local resolution as a fallback when the server does not support it.
- Added `pnpm pipeline [name]` to install frozen dependencies and run workspace tasks declared in `pipelines`. It selects affected projects, runs their task graph, and continues running tasks after a task fails.
Tasks support `inputs`, `outputs`, `env`, and `cache` settings. Cached results restore task outputs and replay logs. Cargo tasks can reuse local build state between worktrees with `tasks.<name>.cargoTargetDir`. Set `includeWorkspaceRoot: true` to include root tasks.
Use `pnpm pipeline --dry-run` to preview the task graph without installing configuration dependencies or running workspace hooks.
- Added support for Android on arm64 and x64, FreeBSD on x64, and Linux on ppc64le, s390x, and RISC-V (riscv64 with glibc) [#​14431](https://github.com/pnpm/pnpm/issues/14431), [#​14597](https://github.com/pnpm/pnpm/issues/14597), [#​7582](https://github.com/pnpm/pnpm/issues/7582).
- Added `trustPolicyExcludePrune` to automatically remove unused versions and packages from `trustPolicyExclude` when running `pnpm add`, `pnpm update`, or `pnpm remove`. It is disabled by default. Package name patterns such as `@scope/*` are kept, and cleanup is skipped when `sharedWorkspaceLockfile` is `false`.
- Added `pnpm change check` for CI validation of package versions against the `versioning.epics` bands and `versioning.fixed` groups in `pnpm-workspace.yaml`. It reports all violations, including packages that are not part of the current release.
##### Patch Changes
- Registry metadata is now kept separate for registries with different URL paths or schemes. This prevents installs from using another registry's package versions or tarball URLs, and keeps metadata fetched over HTTP from being reused for HTTPS [#​13558](https://github.com/pnpm/pnpm/issues/13558).
The first install after upgrading refetches registry metadata. The package store is unchanged. `pnpm cache view` now shows full registry URLs. Scripts that parse the directory names from `pnpm cache list-registries` or `pnpm cache list` need updating.
- Patches that add build scripts or a `binding.gyp` now trigger a build, subject to build approval. Unapproved builds appear under "Ignored build scripts" [#​14648](https://github.com/pnpm/pnpm/issues/14648).
- Build scripts can now be rejected before installing a package with `pnpm add --allow-build=!<pkg>`, including global installs. `pnpm approve-builds <pkg>` and `pnpm approve-builds !<pkg>` also save decisions when no packages are awaiting approval. They warn if the named package is not awaiting approval [#​14067](https://github.com/pnpm/pnpm/issues/14067).
- A registry configured in `.npmrc` now takes precedence over registry settings saved by `pnpm login` in the global `config.yaml`. This fixes installs using the wrong registry after login [#​14614](https://github.com/pnpm/pnpm/issues/14614).
- Large downloads over slow connections no longer time out while data is still arriving. `fetch-timeout` now limits how long a request can go without making progress [#​14604](https://github.com/pnpm/pnpm/issues/14604).
- Sped up installs in workspaces with many projects when reusing a warm global virtual store [#​14540](https://github.com/pnpm/pnpm/issues/14540).
- `pnpm deploy` is faster in large workspaces and no longer fails with `ERR_PNPM_LOCKFILE_CONFIG_MISMATCH` when the project includes a `.pnpmfile.mjs` [#​14539](https://github.com/pnpm/pnpm/issues/14539), [#​14671](https://github.com/pnpm/pnpm/issues/14671).
- `pnpm add --workspace <pkg>` works again. It saves the dependency with the `workspace:` protocol and links it from the workspace. The command fails if no workspace project provides the package [#​14602](https://github.com/pnpm/pnpm/issues/14602).
- `pnpm add` and `pnpm install` now accept protocol-prefixed selectors such as `jsr:@scope/pkg`, `npm:pkg@^1.0.0`, and `workspace:pkg@*` [#​14590](https://github.com/pnpm/pnpm/issues/14590). Installs with JSR dependencies in the lockfile also no longer fail with `ERR_PNPM_META_FETCH_FAIL` [#​14649](https://github.com/pnpm/pnpm/issues/14649).
- Boolean flags now accept explicit inline values. For example, `pnpm install --prod=false` installs devDependencies, while `--prod=true` skips them [#​14553](https://github.com/pnpm/pnpm/issues/14553).
- `pnpm install <pkg>` now accepts `--offline` and `--prefer-offline`, as `pnpm add <pkg>` already did [#​14194](https://github.com/pnpm/pnpm/pull/14194).
- Fixed `pnpm install --frozen-lockfile` rejecting a freshly generated lockfile when overrides use relative `file:` or `link:` paths in a workspace [#​14555](https://github.com/pnpm/pnpm/issues/14555).
- Fixed installs with config dependencies failing on symlinked lockfiles, such as those used by Bazel and Nix, when the config dependencies have not changed. Updates that would write through a symlink remain disallowed. Updating config dependencies also preserves lockfiles that start with a byte order mark [#​14372](https://github.com/pnpm/pnpm/issues/14372).
- Fixed package manager version pins being written to the wrong lockfile when `lockfileDir` is set. The pins also remain consistent across commands when version switching is disabled, avoiding unnecessary lockfile changes [#​14633](https://github.com/pnpm/pnpm/issues/14633), [#​14575](https://github.com/pnpm/pnpm/issues/14575).
- `pnpm import` now respects `lockfileDir` and branch lockfiles without modifying other lockfiles. Failed imports restore the destination lockfile [#​14563](https://github.com/pnpm/pnpm/issues/14563).
- `pnpm patch-commit` now produces valid patches when files are added or deleted. `pnpm install` also accepts patches that delete files without listing their contents, and patch files with CRLF line endings [#​14559](https://github.com/pnpm/pnpm/issues/14559), [#​14557](https://github.com/pnpm/pnpm/issues/14557).
- Fixed version ranges with partial upper bounds. For example, `<=16` now includes all 16.x versions, and `>=0.11 <=3` correctly accepts 3.0.1 [#​14419](https://github.com/pnpm/pnpm/issues/14419).
- Workspace package patterns now support `.` and `..` segments and repeated slashes. Patterns such as `./packages/*` and exclusions such as `!./packages/foo` now match correctly [#​14571](https://github.com/pnpm/pnpm/issues/14571).
- `packageConfigs` settings now apply to the specified projects when `sharedWorkspaceLockfile` is `false`, including `overrides`, `hoist`, `modulesDir`, `saveExact`, and `savePrefix`. Workspaces with a shared lockfile report which entries were ignored [#​14556](https://github.com/pnpm/pnpm/issues/14556).
- `pnpm run` and `pnpm exec` no longer report a changed workspace structure after a successful install when `sharedWorkspaceLockfile` is `false` and `verifyDepsBeforeRun` is enabled [#​14588](https://github.com/pnpm/pnpm/issues/14588).
- Commands run from a project's subdirectory now find the nearest ancestor with a manifest. This fixes commands such as `pnpm bin` returning paths under the wrong directory. `pnpm init` still creates its manifest in the current directory, and `pnpm exec` still runs there [#​14622](https://github.com/pnpm/pnpm/issues/14622).
- Relative `scriptShell` paths in `pnpm-workspace.yaml` now resolve from the workspace root, including when scripts run in nested packages. Bare command names such as `bash` still use `PATH` [#​14422](https://github.com/pnpm/pnpm/issues/14422).
- Fixed installing the pnpm version pinned in `packageManager` when `nodeLinker` is `hoisted`. Managed Node.js, Deno, and Bun installations also work when the global config uses `nodeLinker: hoisted` [#​14595](https://github.com/pnpm/pnpm/issues/14595).
- The JavaScript pnpm can again switch to a project's pinned pnpm version on platforms without a native binary for that version, such as Alpine Linux with pnpm 10 or Intel Macs with pnpm 11. If a native pnpm version does not support the platform, the error now names the missing target [#​13622](https://github.com/pnpm/pnpm/issues/13622).
- Provisioning Yarn 6 now uses `GH_TOKEN` or `GITHUB_TOKEN` when available to avoid GitHub's anonymous API rate limit in CI. Tokens are only sent when `strict-ssl` is enabled.
- Fixed concurrent installs sharing a global virtual store on macOS failing with "failed to import ... No such file or directory" [#​14560](https://github.com/pnpm/pnpm/issues/14560).
- Fixed `pnpm setup` failing with `ERR_PNPM_DIRECTORY_FETCHER_PATH_ESCAPE` on Windows. Local `file:` dependencies whose directories are symlinks or junctions are now packed correctly [#​14618](https://github.com/pnpm/pnpm/issues/14618).
- On Windows, installs now retry replacing command shims temporarily locked by another process [#​14549](https://github.com/pnpm/pnpm/issues/14549).
- Fixed argument forwarding on Windows with `shellEmulator` enabled. Paths ending in a backslash, line breaks, and literal shell expressions are preserved [#​14548](https://github.com/pnpm/pnpm/issues/14548).
- Windows store paths now consistently use backslashes in `pnpm store path` output and in the `storeDir` and `virtualStoreDir` fields of `node_modules/.modules.yaml`.
- Invalid certificates in `ca` or `cafile` no longer cause an `Invalid CA certificate` error. Valid certificates still apply, and blank `cert` or `key` values are treated as unset [#​14646](https://github.com/pnpm/pnpm/issues/14646).
- Installs now respect the archive extraction concurrency limit even after a download is abandoned [#​14585](https://github.com/pnpm/pnpm/issues/14585).
- `pnpm audit` summaries now exclude advisories ignored through `auditConfig.ignoreGhsas` and report them separately. When all advisories are ignored, the summary says so [#​14535](https://github.com/pnpm/pnpm/issues/14535).
- `pnpm pack --json` now reports errors as JSON. Lifecycle script output appears before the final JSON output.
- `pnpm outdated -r` now wraps the `Dependents` column, keeping the table readable when many workspace projects use the same dependency [#​14591](https://github.com/pnpm/pnpm/issues/14591).
- Shell completions now support the `pn` alias in bash, fish, pwsh, and zsh [#​11955](https://github.com/pnpm/pnpm/issues/11955).
- `pnpm version` now accepts `-m` as a short alias for `--message` [#​14567](https://github.com/pnpm/pnpm/issues/14567).
<!-- sponsors -->
##### Platinum Sponsors
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<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>
</td>
<td align="center" valign="middle">
<a href="https://openai.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/openai_dark.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/openai_light.svg" />
<img src="https://pnpm.io/img/users/openai_dark.svg" width="160" alt="OpenAI" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<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>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" />
<img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" />
</picture>
</a>
</td>
</tr>
</tbody>
</table>
##### Gold Sponsors
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" />
<img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" />
<img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<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>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" />
<img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" />
<img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" />
<img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" />
</picture>
</a>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank" rel="noopener noreferrer">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" />
<source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" />
<img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" />
</picture>
</a>
</td>
<td align="center" valign="middle">
<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>
</td>
</tr>
</tbody>
</table>
<!-- sponsors end -->
</details>
<details>
<summary>kulshekhar/ts-jest (ts-jest)</summary>
### [`v29.4.14`](https://github.com/kulshekhar/ts-jest/blob/HEAD/CHANGELOG.md#29414-2026-09-25)
[Compare Source](https://github.com/kulshekhar/ts-jest/compare/v29.4.13...v29.4.14)
##### Reverts
- bring back prior changes of compiler util ([#​5471](https://github.com/kulshekhar/ts-jest/issues/5471)) ([01e5a2e](https://github.com/kulshekhar/ts-jest/commit/01e5a2e17ac9c940880865d6a81751832dcc4184)), closes [#​5469](https://github.com/kulshekhar/ts-jest/issues/5469)
### [`v29.4.13`](https://github.com/kulshekhar/ts-jest/blob/HEAD/CHANGELOG.md#29413-2026-09-22)
[Compare Source](https://github.com/kulshekhar/ts-jest/compare/v29.4.12...v29.4.13)
##### Features
- support Babel 8 ([#​5466](https://github.com/kulshekhar/ts-jest/issues/5466)) ([c3a0b57](https://github.com/kulshekhar/ts-jest/commit/c3a0b57c00d8b6acfb72b020e2411752d39073e2)), closes [#​5412](https://github.com/kulshekhar/ts-jest/issues/5412)
##### Performance Improvements
- **compiler:** cache module resolution modes ([#​5419](https://github.com/kulshekhar/ts-jest/issues/5419)) ([697d337](https://github.com/kulshekhar/ts-jest/commit/697d337c1b79a0804972a3d30c0875b02e73bf62)), closes [#​5402](https://github.com/kulshekhar/ts-jest/issues/5402)
</details>
---
### Configuration
📅 **Schedule**: (UTC)
- Branch creation
- At any time (no schedule defined)
- Automerge
- At any time (no schedule defined)
🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.
♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired.
---
- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box
---
This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNDcuMCIsInVwZGF0ZWRJblZlciI6IjQ0LjExNS4xMyIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
This PR contains the following updates:
4.7.0→4.9.017.0.0-4.0.4→17.0.0-4.0.0-rc.1764.3.6→64.5.412.3.4→12.6.012.3.4→12.6.029.4.12→29.4.14Release Notes
gjsify/types (@girs/gjs)
v4.9.0Compare Source
v4.8.0Compare Source
gajus/eslint-plugin-jsdoc (eslint-plugin-jsdoc)
v64.5.4Compare Source
Bug Fixes
no-unnecessary-type-assertion: only try typescript if the rule is enabled; fixes #1773 (3c1c209)v64.5.3Compare Source
Bug Fixes
v64.5.2Compare Source
Bug Fixes
v64.5.1Compare Source
Bug Fixes
v64.5.0Compare Source
Features
ts-ban-ts-comments: add rule mirrorring typescript-eslint'sban-ts-commentsrule (afff498)v64.4.0Compare Source
Features
convert-to-jsdoc-comments,no-bad-blocks: add LibreJS tags (99d2abf)v64.3.10Compare Source
Bug Fixes
convert-to-jsdoc-comments: merge stacked line comments into a single JSDoc block (64a8dca), closes #1764v64.3.9Compare Source
Bug Fixes
no-undefined-types: recognize class members from the class's own JSDoc (eade377), closes #1762v64.3.8Compare Source
Bug Fixes
getJsdocProcessorPlugin: key positions by file; drop suggestions (1b3c339)v64.3.7Compare Source
Bug Fixes
no-undefined-types: recognize sibling class members from a property's JSDoc (5cac70a), closes #1760pnpm/pnpm (pnpm)
v12.6.0: pnpm 12.6Compare Source
pnpm 12.6.0 ships with automatic dependency deduplication, relocatable node_modules, package.yaml manifest editing, and --save-types support.
Minor Changes
autoDedupededuplicates compatible dependency versions during installation #7258. Enable it inpnpm-workspace.yamlor usepnpm install --auto-dedupeorpnpm add --auto-dedupe. Frozen installs leave the lockfile unchanged.pnpm install,pnpm run, andpnpm execon macOS and Linux now reuse anode_modulesdirectory and bin shims that moved or were copied together with their project #6937. The first command after the move checks the tree and records its new location, so project commands innode_modules/.binkeep working.pnpm add --save-typessaves available@types/*packages indevDependenciesalongside registry dependencies #3868. Packages that declare bundled TypeScript types are skipped. SetsaveTypes: trueinpnpm-workspace.yamlto enable this by default.package.yamlmanifests can now be updated bypnpm add,pnpm update,pnpm remove,pnpm pkg,pnpm link,pnpm set-script, andpnpm version#2008. Existing comments and key order are preserved.Catalog entries can now use the
file:andlink:protocols #8642. A relative path or bare path in an entry, such as./tarballs/foo.tgz, is measured from the directory holdingpnpm-workspace.yaml.pnpm tasks statuslists running and waiting tasks in each concurrency group, and waiting tasks now take available slots in arrival order with higherprioritytasks going first #15208. If workspaces use different limits for the same group, a later task can take a free slot that earlier tasks cannot use. A package script namedtaskstakes precedence; usepnpm pm tasks statuswhen that script exists.pnpm cache prunedeletes registry metadata cache directories that this version of pnpm can no longer read #15046.pnpm cache prune --dry-runlists what it would delete without removing anything.macosBackup.excludeModulesDirandmacosBackup.excludeStoreDiron macOS can now exclude newly created modules, virtual-store, and package-store directories from Time Machine #6440. Set either totruein global configuration or using thePNPM_CONFIG_MACOS_BACKUP_EXCLUDE_MODULES_DIRandPNPM_CONFIG_MACOS_BACKUP_EXCLUDE_STORE_DIRenvironment variables.pnpm add --tildeis now an alias for--save-prefix=~#12863. The Yarn-Tshorthand is not supported.progresssetting and--no-progressoption now turn off dependency and download progress lines #14065. Warnings, lifecycle output, and the dependency summary are still printed.Patch Changes
Security
POSIX bin shims now take
cygpathandwslpathfrom the system default path on Cygwin, MSYS2, and WSL2 so a dependency cannot redirect another package's shim #14866.pnpm installwarnings no longer carry the text of a package's deprecation notice, naming only the deprecated package and version #15099. A deprecation warning names the newest non-deprecated version when one exists, and control characters and line separators are stripped from package identifiers and warnings.pnpm installand other commands that report configuration warnings now warn when environment variables in project.npmrccredentials are ignored #15051.Installing packages
pnpm install --frozen-lockfilenow succeeds when an optional dependency was unresolvable and skipped by the install that wrote the lockfile #3960.pnpm install --frozen-lockfileno longer installs dependencies of projects removed frompnpm-workspace.yaml#15248. Missing local tarballs used only by those projects no longer fail the install.pnpm cinow emptiesnode_modulesbefore installing in a project that declares acleanscript #15276.pnpm install --forcenow re-imports every package into the virtual store #15030 and removes obsolete dependency links inside virtual-store packages when their dependencies change #15039.preinstallscript for the root project now runs before dependencies are resolved and linked #3760.pnpm installnow runspnpm:devPreinstallwhen the root project usespackage.yaml#15168.pnpm installnow enforces the root project'sengines.noderange whenengineStrictis enabled #3016.pnpm installnow uses the running Node.js whendevEngines.runtimedeclares a range withoutonFail: download#15230.pnpm installno longer hangs when a git dependency is fetched over SSH and ssh prompts for a passphrase or host key confirmation, running ssh in batch mode instead #2227.pnpm installnow installs git-hosted dependencies without preparing them when their builds are explicitly denied byallowBuilds#10522.pnpm installnow reuses an in-flight tarball download when another resolution of the same archive still needs itspackage.json#15037.pnpm install --prodno longer downloads registry packages that only a devDependency reaches #881.pnpm install --no-runtime --frozen-lockfilewithnodeLinker: hoistedno longer fails on repeated runs with a broken lockfile #15212.Resolving and linking dependencies
pnpm installandpnpm updatenow resolve a dependency range to the newest matching version that is not deprecated #15128.pnpm add <pkg>without a version now uses the catalog entry when the workspace already catalogs that package #14865.pnpm installnow links workspace dependencies declared with plain version ranges whenexcludeLinksFromLockfileandlinkWorkspacePackagesare enabled #15133.pnpm installnow resolves local tarball dependencies whose absolutefile:paths contain..consistently and skips reinstallation on repeat installs #15190.pnpm installnow installs dependencies when a custom resolver returns a local or git-hosted tarball without a manifest #15016.pnpm.overridesentries written as a bare path, such as./local-dep, are now measured from the directory holdingpnpm-workspace.yaml#11131.pnpm update --no-saveno longer bypasses version-scoped overrides when a dependency selector specifies a version #14923.pnpm peers checkand strict peer dependency checks no longer reject compatible versions from named registries #15225.pnpm outdatedandpnpm update --interactive --latestnow include named-registry dependencies such aswork:2.1.0and preserve their registry prefix #15226.Workspace projects selected by
hoistPatternorpublicHoistPatternare now hoisted on every install #3642.Workspace packages with SemVer build metadata are no longer skipped when they match the requested range and have the same version precedence as the registry package #2812.
Sped up
pnpm dedupeandpnpm installin projects with many convergence overrides by checking overrides concurrently #15175.minimumReleaseAgeis no longer skipped for packages served by registries returning matching ETags for abbreviated and full package metadata #14925.Running scripts and tasks
pnpm runsignal handling no longer delivers a redundant secondSIGINTto child scripts onCtrl+Cin a terminal, and properly forwards termination signals when running non-interactively without a terminal #7374.pnpm runandpnpm execin workspaces withsharedWorkspaceLockfile: falsenow verify dependencies in the selected projects rather than expecting a root workspace state #15272.pnpm testnow forwards--filterarguments to the test script when the option follows the shortcut #15217.Recursive runs now start scripts matched by a
/pattern/selector in parallel withinworkspaceConcurrency#14933.pnpm deploy,pnpm rebuild,pnpm rb, andpnpm setupnow prefer apackage.jsonscript of the same name #14976.modulesDircustom directory names now support executable lookup and CommonJS plugin resolution acrosspnpm run,pnpm exec,pnpm versionhooks, and lifecycle scripts #3604.pnpm install-testnow accepts--no-baildirectly and in recursive runs #3777.Workspace and project configuration
pnpmcommands run in a project not included in the workspace now act on that project alone #3561.pnpm-workspace.yamledits now preserve scalar YAML anchors and aliases #8245.pnpm-workspace.yamlnow expands environment variable placeholders with fallback syntax in enum-valued settings such asnodeLinker#14914.pnpmfileconfiguration now loads a.jsfile as CommonJS or an ES module, following the nearestpackage.json#15141.updateConfighook settings are now honored bypnpm peers check,why,list,ll,licenses,audit,sbom,fetch,patch,patch-commit,patch-remove,approve-builds, andruntime#15047, #15049.readPackagehook changes or removal now take added dependencies out ofpnpm-lock.yamland update dependencies when an existing lockfile is present #3735, #15136.package.yamlprojects now record their pinned pnpm underpackageManagerDependenciesinpnpm-lock.yaml#15167.packageManagerDependenciespinning@pnpm/exebesidepnpmis no longer rewritten inpnpm-lock.yaml#14926.pnpmnow preserves CRLF line endings when modifying project manifests #3529.loglevelsetting is now honored when configured inpnpm-workspace.yaml, global configuration, orPNPM_CONFIG_LOGLEVEL#3122.storeDirvalues loaded from global configuration orPNPM_CONFIG_STORE_DIRnow expand a leading~/to the user's home directory #6560.--shared-workspace-lockfilenow produces a warning when passed on the command line outside a workspace #1617.Windows
pnpm installon Windows now runs dependency build scripts from long global virtual store paths and normalizes scoped package paths in lifecycle scriptPATHentries #15111.pnpm installacross projects sharing a global virtual store on Windows no longer fails withAccess is denied, file-exists errors, or transient sharing violations #15114, #15176, #15171.pn,pnpx,pnx, andpnpmnow run when Git Bash, MSYS2, or Cygwin launches them through a Windows path #14884.pnpm dlxnow reuses cached packages when Windows creates directory junctions for its cache links #15171.pnpm pipeline --watchnow resolves Windows short paths so multiple path representations share the build cache #15105.CLI commands and output
pnpm removenow runs the project's ownpreuninstall,uninstall, andpostuninstallscripts #3276.pnpm remove -rnow fails before modifying manifests if any requested dependency is absent from all selected projects #2319.pnpm update --peernow updates ranges inpeerDependencies#8081.pnpm updatenow movesdevEngines.runtimeandengines.runtimeversion ranges to the resolved Node.js version #14988.pnpm update -gno longer reinstalls unchanged packages #12002.pnpm add -g,pnpm update -g, andpnpm remove -gnow recover a global package group whosenode_modulesdirectory was deleted #15093.pnpm add -gnow installs local tarballs whenPNPM_HOMEcontains..path segments #15118.pnpm versionnow readstagVersionPrefixfrompnpm-workspace.yaml, global config, orPNPM_CONFIG_TAG_VERSION_PREFIXwhen creating and reading Git tags #15044.pnpm publishnow allows a detached Git HEAD in CI environments #5894.pnpm store prunenow removes unreferenced files and packages from the content-addressable store #3635, as well as expired or supersededpnpm dlxcache data #15171.pnpm cache list-registriesnow prints decoded registry URLs #15046.pnpm deployno longer triggers an install when running scripts in a read-only deployed filesystem #11617.pnpm -r list --jsonnow outputs a single JSON array whensharedWorkspaceLockfileisfalse, and--longand--parseableread each project's own modules directory #15011.pnpm sbomnow validates SPDX identifiers and expressions before emitting them as CycloneDX license IDs or expressions, falling back to a license name for non-SPDX values such asUNLICENSED#14786.pnpm change checknow validates pending change intents in.changeset/#15183.pnpm --filterandpnpm -Fshell completion now suggests workspace package names #15216. Completion candidates containing control or invisible formatting characters are omitted so package and script names cannot inject terminal escape sequences.pnpm runandpnpm run-scriptshell completion now suggests package scripts #15034.pnpm --versionno longer creates a temporary file in the project directory during store detection #15264.pnpm setupnow describes displayed configuration changes as "The following configuration changes were made" #15100.minimumReleaseAgeapproval prompts inpnpm installandpnpm update -gnow count and display each package version once #15083, #15091..npmrcauthentication warnings now report when an empty environment variable removes an auth token and name the affected key #4806.The install summary now names the version each dependency resolved to when
node-linkerishoisted#15161.pnpm installnow re-links a package's global virtual store slot afterallowBuildschanges #15117.Platinum Sponsors
Gold Sponsors
v12.5.1: pnpm 12.5.1Compare Source
Patch Changes
pnpm now reports an unknown task setting in
pnpm-workspace.yamland carries on. It used to refuse to start, so a project could not use a task setting that only the pnpm version itspackageManagerpins reads. The setting is still an error when the running pnpm is that pinned version.Python interpreter installation now retries historical release metadata requests. It caches the release list for up to 24 hours and refreshes it once after a lookup miss. When a release omits the current platform, the search samples at most eight other releases before reporting that the lookup is inconclusive.
Python
registriesentries now route packages by exact names or trailing-prefix patterns inpackages. Registry declaration order no longer affects resolution. A matched package resolves exclusively from its assigned registry, including transitive and build dependencies. Usepackages: ["*"]to declare the default index.pnpm installno longer fails with "Too many levels of symbolic links" when a Cargo configuration file above the workspace is a symlink, such as a~/.cargo/config.tomllinked from a dotfiles repository.pnpm installnow returns "Already up to date" in a workspace wherededupeDirectDepsleft a project without anode_modulesdirectory of its own. Such a project forced a full install on every run.pnpm installno longer refuses the repeat-install fast path just because a changedpnpm-lock.yamlis 16 MiB or larger. Such a lockfile forced a full install on the run after every change.Platinum Sponsors
Gold Sponsors
v12.5.0: pnpm 12.5Compare Source
pnpm 12.5.0 makes Python a first-class ecosystem, accepts Package URLs in
pnpm add, names whole platforms insupportedArchitectures, and gives tasks machine-wide concurrency limits. It also fixes an install that could reuse one package's downloaded tarball for another.Minor Changes
Installing packages
pnpm addaccepts a Package URL in place of a package name.pnpm add pkg:npm/express@4.18.2savesexpresstopackage.json.pnpm add pkg:cargo/serde@1.0.188savesserdetoCargo.toml.pnpm add pkg:pypi/requests@2.31.0savesrequeststopyproject.toml.pkgis now a reserved specifier prefix, whatever case it is written in, so a named registry can no longer be calledpkg.A
registriesentry can now name the ecosystem it serves.ecosystemacceptsnpm,cargoandpypi. An entry that does not name one serves npm, as every entry did before.An ecosystem with several indexes searches them in the order they are declared. The first index that has a package supplies it, so the one declared last answers what none before it had.
A
registriesentry may not carry credentials. pnpm reads them from.npmrc, matched by origin, for a PyPI index as for every other package source.Configuring pnpm
supportedArchitecturesnow accepts a list of platforms, in place of theos,cpuandlibcaxes.An install prepares for the platforms the list names, and for those only. A platform reads as
<os>-<cpu>, with a C library on Linux, as inlinux-x64-muslorlinux-x64-manylinux_2_28. The Rust target triple of the same machine is accepted too, sox86_64-unknown-linux-gnunames the platformlinux-x64names. A Linux platform that names no C library is the glibc platform.currentis the platform the install runs on.The
os,cpuandlibcmapping keeps working and keeps its meaning.Added concurrency groups for tasks. A task in
pnpm-workspace.yamlcan name aconcurrencyGroup. The newconcurrencyGroupssetting gives each group a limit. At most that many tasks of the group run at once on the machine, counted across every pnpm process,pnpm pipelineincluded. A task past the limit waits for a running one to finish. A script that callspnpm runfor a task of the same group runs under the slot its parent holds.toolsnames the programs pnpm downloads, andmirrorsays where each one comes from.node,bunandpythoncan be named. Any other tool is refused.mirroris the base a tool's own layout hangs off.channelssends one release channel elsewhere. A channel neither it nornode-mirror:<channel>names is left tomirror. Onlynodepublishes channels, so naming them for another tool is refused.Set it in the global
config.yamlor inPNPM_CONFIG_TOOLS. Apnpm-workspace.yamlthat names a tool mirror is ignored.pnpm pack-appdownloads the Node.js it embeds throughtools.node.node-mirror:<channel>keeps working and names the same thing as an entry underchannels.Python interpreters and environments
pnpm installnow chooses a Python interpreter for each project instead of installing every project with one interpreter #14945. A project is installed with the first interpreter on the machine that itsrequires-pythonaccepts, so a workspace can hold projects that support different Python versions. pnpm reads.python-versiontoo, and prefers the version it asks for. Setpython.executableinpnpm-workspace.yamlto name one interpreter for every project.pnpm installnow installs a Python interpreter when no interpreter on the machine fits the project #14945. The builds are python-build-standalone's, which uv and rye install too. One interpreter is shared by every project on the machine, and a later install uses it without downloading anything.runtimeOnFaildecides what an install with no interpreter that fits does, the way it does for a Node.js runtime.errorreports the project instead of installing one.warnandignoreinstall with an interpreter the machine has that the project'srequires-pythonrejects.tools.python.mirrornames a mirror.Python environments now live in the store. Each project keeps only its
.venvlink, which points at the project's current environment generation underpython-envsin the store. A repository with many Python projects no longer holds a.pnpm/python-envsdirectory in each of them. The next install relinks a.venvthat an earlier release published. The old.pnpm/python-envsdirectory is left in place, since a running program may still use it, and can be deleted once none does. WithfrozenStoreset, pnpm writes nothing to the store, so environments stay in the project's.pnpm/python-envs#15014.Python environments now use
packageImportMethodto import wheel files from the store. Useclone-or-copyfor copy-on-write clones with a copy fallback, orcopyfor independent files. Hardlinked files share writes with the store and other environments.Isolated Python build environments keep backend writes private with copy-on-write clones or copies.
Python projects and workspaces
pnpm installnow installs a Python project's own package, so the project can be imported and the commands in[project.scripts]run right after an install #14945. The installed package points at the source tree, so an edit to a module takes effect without another install. pnpm installs the package of a project that declares a[build-system].tool.uv.packageoverrides that either way.pnpm installnow installs a Python project in the workspace from its own source. Declare it under[tool.uv.sources], asshared = { workspace = true }orshared = { path = "../shared", editable = true }. pnpm builds the project with the backend it declares. It installs the build editable, so an edit to the project takes effect without another install.Approve the build backend under
allowBuildsinpnpm-workspace.yamlas a Package URL, aspkg:pypi/hatchling: true. An install that has not approved a backend does not build the projects that need it. The message names the key to add.pnpm installnow refuses a requirement that names a project in the workspace when nothing declares where it comes from. It used to take that name from the index.The members of a uv workspace can now share one Python environment. Set
shared-environment = trueunder[tool.pnpm.python]in thepyproject.tomlthat declares[tool.uv.workspace].pnpm installthen resolves every member as one graph into onepylock.tomland one.venvat the workspace root. Two members that require versions of a distribution no release satisfies at once are refused with an error naming both. Each project still gets an environment of its own by default #15015.Python projects can now select extras and dependency groups through
[tool.pnpm.python]inpyproject.toml#14945. Workspacepython.extrasandpython.groupsdefaults now skip names a project does not define.pnpm installnow reads dynamic Python project metadata from the build backend #14945. Projects with only arequirements.txtfile now get a Python environment and lockfile.Python dependencies and lockfiles
pnpm can now resolve
pylock.tomlfor several platforms and Python versions at once.supportedArchitecturesnames the platforms to lock for andpython.versionsthe versions. Every platform is paired with every version. One committed lockfile then serves Linux CI and macOS or Windows contributors #14945.The lockfile pins the wheel each environment takes for a distribution. It marks a package only some environments install.
pnpm installtakes the packages and wheels of the environment its interpreter matches, and refuses an interpreter none of them stand for. pnpm resolves a project that declares environments itself, not through the serverpnprServernames. Naming neither setting locks for the interpreter running the install.python.overridesandpython.constraintspin the versions a Python resolution may pick #14945. pnpm reads uv's own overrides and constraints frompyproject.tomltoo.pnpm installnow supports Python dependencies from Git repositories #14945. Direct wheel URLs are also supported. Sources can be declared in[tool.uv.sources]. Git dependencies requireallowBuildsapproval.pnpm installcan install a Python release that publishes no wheel this interpreter accepts, by building the source distribution the index serves beside it #14945. The archive is pinned inpylock.tomlby name and SHA-256. A later install replays it from the store, offline included. Building a source distribution runs the release's own build backend. Approve it withpkg:pypi/<distribution>: trueunderallowBuilds.A resolution that finds no version of a distribution now says why. It tells apart a distribution no index publishes, one whose releases publish nothing this interpreter can install, and one whose versions the project's requirements exclude.
Patch Changes
Installing packages
pnpm no longer reuses one package's downloaded tarball for another package whose resolution pins a different integrity hash to the same URL #15021.
pnpm installandpnpm addnow report an error whenpackage.json,pnpm-lock.yaml,pyproject.tomlor another file they snapshot before installing is a named pipe or a device. The command used to wait forever for something to write to it.pnpm install --prodandpnpm install --devnow record every dependency group inpnpm-lock.yaml.node_modulesstill holds only the groups the filter selects. They used to write the filter into the lockfile, so a laterpnpm install --frozen-lockfilerejected it.pnpm prune --prod,pnpm prune --dev, andpnpm prune --no-optionalbehave the same way #14912.POSIX bin shims now convert a Windows-form path such as
C:\node_modules\.bin\tsccorrectly. The shim mangled the backslashes in such a path and could not reach the package it runs. Installing again replaces the shims already innode_modules#14867.Two pnpm processes installing one workspace at the same time no longer fail on Windows with "Access is denied" while writing
node_modules/.pnpm-workspace-state-v1.json. The write now retries the transient lock the other process holds, as pnpm's other file writes do.pnpm now reads the manifest from the tarball when a pnpmfile
resolvershook returns a resolution without one. Such a package installed alone, with none of its own dependencies and no warning #15000.pnpm installnow merges Git conflict markers inpnpm-lock.yaml. It parses both sides of the conflict and keeps the versions they locked. A conflict in the config dependencies recorded at the top of the lockfile is merged too #14880.Cargo projects
pnpm installcan now generateCargo.lockfor workspaces with path or Git[patch]and[replace]overrides. Adding, removing, and updating crates also preserve these overrides #14950.Cargo lockfile resolution blocks unsupported Git transport helpers declared by transitive dependencies.
pnpm installnow vendors recursive Git submodules for Cargo dependencies at their pinned commits. Cargo builds can use these sources offline. Set Git'sprotocol.file.allowtoalwaysto fetch local file submodules. pnpm fetches cached Git crates again on the first online install #14951.pnpm installnow generatesCargo.lockfor workspaces with Git dependencies, including a dependency that omits a package version. It also downloads the Rust standard library's dependencies when Cargo configuration enablesbuild-std#14944.pnpm installnow handles weak Cargo features, writtencrate?/feature. Resolution failed when one dependency turned on an optional crate and another asked for a weak feature of it #14960. The generatedCargo.locknow also includes the dependencies weak features reference, which Cargo rejected with--lockedfor crates such asuuid#14978.pnpm installnow generatesCargo.lockwhen a crate version it considers depends on a release the registry carries only as yanked. pnpm rules that version out and resolves the rest of the graph. Resolution failed with an error such asno non-yanked version of napi-build satisfies ^3.0.0-beta#14952.pnpm installnow falls back to an older semver-incompatible version of a crate when the newest one a dependency range allows cannot be resolved. Ranges such as>=1, <3span several of them #14962.Python projects
pnpm installnow honors uv workspace members when discovering Python projects. When no uv workspace declares a project, pnpm skips projects under conventional example, demo, documentation, template,test,tests, and test fixture directories #15058.pnpm install --filter <selector>now installs only the Python projects the selection asks for. A Python project that shares a directory with an npm workspace project is selected with that project. A Python project in a directory of its own is selected by the distribution it declares, by its path, or through the[tool.uv.sources]entries that reach it. Under--fail-if-no-match, a selector that names only a Python project is a match.pnpm add --filter <selector> pypi:<package>writes the requirement to every selected project #14945.pnpm installnow installs wheels whoseRECORDhashes disagree with their contents. The wheel archive's locked SHA-256 hash remains verified. pnpm writes correct hashes to the installedRECORD#15061.pnpm installnow installs a Python wheel whoseWHEELfile lists tags that differ from the ones in its filename. A wheel whose filename tags were changed after the build, such asmysql-connector-python, was rejected #14945.A Python release whose wheel metadata declares a requirement pnpm cannot read no longer fails the install. pnpm now resolves the project against the other releases of that package, and reports the unreadable requirement when none of them works.
pnpm installno longer fails when a Python index lists a file pnpm cannot use, such as a release with no SHA-256 digest or an unreadable wheel filename. That file is left out and the project resolves against the remaining releases.pnpm add pypi:<package>in a directory that has nopyproject.tomlnow names the missing file and says where to run the command. It used to fail with a bareNo such file or directory (os error 2)#14945.Performance
pnpm auditno longer hangs on dependency graphs with many shared dependencies #15005.Sped up
pnpm installin Python workspaces with many projects. Projects now prepare concurrently. Projects with identical registry requirements also share fresh dependency resolutions #14945.Repeat installs through the Node-API bindings now return "Already up to date" when the project manifests still match
pnpm-lock.yaml. Before, every such install reinstalled the whole tree. An install also no longer reinstalls whenpnpm-lock.yamldiffers from the installed dependencies only by packages no project depends on or by top-level keys pnpm does not define.Other commands
pnpm deploynow links commands exposed by workspace dependencies into the deployed project'snode_modules/.bindirectory #14899.pnpm dlxandpnxnow prompt to approve dependency build scripts in interactive terminals #14943. Cached packages with pending builds also prompt for approval. Without an interactive terminal, use--allow-buildto allow the required builds.pnpm add -gandpnpm update -gnow ignore incomplete unrelated global package groups when every command from the replaced group is retained. Operations that could remove a global command still require complete ownership information.pnpm packnow writes tarball entries grouped by file extension and file name, the order npm uses. Packages that ship many same-named files, such as template collections, pack much smaller #14766.pnpm outdated --longfills the Details column with the package homepage again #14886.Platinum Sponsors
Gold Sponsors
v12.4.2: pnpm 12.4.2Compare Source
pnpm 12.4.2 includes security fixes for executable shims and GitHub Actions links, more reliable installs, faster peer dependency checks in workspaces, and Python lockfiles that work across compatible targets.
Patch Changes
Security
Dependency executables can no longer take over another package's POSIX bin shim through its shell helpers. Reinstall dependencies to replace existing shims #14837.
On Cygwin, MSYS2, and WSL, shims still use
PATHfor Windows path conversion, so dependency executables can still redirect them there.GitHub Actions homepage links no longer expose server credentials. GitHub server URLs now require HTTPS, with HTTP allowed only for loopback hosts.
Installing packages
pnpm no longer crashes at startup on FreeBSD and other Unix-like platforms. Platforms other than Windows and macOS use
~/.local/share/pnpm/storeby default #14859.pnpm installon Windows no longer fails withERR_PNPM_PACKAGE_MANAGER_REMOVE_MODULES_DIRwhen clearingnode_modulescontaining linked dependencies, such as when changingnodeLinker#14790.pnpm install <pkg>now accepts--prodand--dev, including--prod=false#14868.pnpm installandpnpm updatenow honor--ignore-workspacein nested projects excluded from the surrounding workspace. The flag also skips that workspace's settings during thepackageManagercheck #14809.pnpm installon macOS no longer reuses stale files forfile:tarball or git-hosted tarball dependencies.pnpm installin a single-project directory now detectspackage.jsonedits made while the previous install was finishing #14890.pnpm install --frozen-lockfilenow removes packages no longer reachable from any project inpnpm-lock.yaml. This also prevents repeated lifecycle script execution and unnecessary installs beforepnpm runandpnpm execwithverifyDepsBeforeRun#14891.Resolving and updating dependencies
Node.js runtime resolution now reports network failures from unofficial-builds.nodejs.org. These failures previously omitted musl builds from
pnpm-lock.yaml, making its contents depend on network access #14813.pnpm installnow rejects invalidpeerDependenciesspecifiers withERR_PNPM_INVALID_PEER_DEPENDENCY_SPECIFICATION. A value such as"foo": "foo@1.0.0"previously created a broken directory link #14791.pnpm deploynow writes plain registry versions in the deployedpackage.json, without peer dependency suffixes. The lockfile retains peer bindings, and npm aliases retain their target package names #14873.pnpm add <git repository>now names repositories without apackage.jsonas@owner/repo, allowing dependencies on equally named repositories from different owners #14870.Peer dependency resolution now deduplicates packages whose child dependency resolves an optional peer in only some workspace projects, such as
nextwithstyled-jsx's optionalbabel-plugin-macrospeer #14800.pnpm updatenow settles the lockfile in one run when an upgrade removes the package providing an optional peer dependency #14895.pnpm update --no-savenow preserves override-applied specifiers for dependencies it is not updating, preventing subsequent frozen installs from failing withERR_PNPM_OUTDATED_LOCKFILE#14836.pnpm update --no-savenow succeeds underminimumReleaseAgeStrictwhen every resolved version is old enough #14835.Performance
Workspace installs and
pnpm peers checkare faster when projects depend on each other, fixing a slowdown introduced in 12.3.0. Unmet peer dependencies of workspace packages are now reported only under projects that link them directly #14906.Hoisted installs use less memory when packages are cached. Frozen-lockfile hoisted installs on macOS are also faster when reusable package directories are cached.
Python projects
pnpm install --frozen-lockfilenow reusespylock.tomlacross compatible Python targets, including after kernel updates. Reuse requires unchanged requirements, index, andrequires-python, compatible wheels, and a locked dependency graph matching the target's markers #14843.The lockfile's
environmentsmarker now includes only the interpreter version and marker variables used by the dependency graph. Without--frozen-lockfile, pnpm warns and resolves again when the locked graph no longer matches the target.Python resolution no longer fails on malformed
Requires-Pythonvalues, such as the trailing comma inopenpyxl3.0.x. pnpm treats these releases as declaring no interpreter range #14910.pnpm add pypi:...now rejects unsupported--save-prefixvalues before editing the manifest or resolving dependencies.Workspaces and scripts
Scripts listed in
syncInjectedDepsAfterScriptsno longer fail withERR_PNPM_INJECTED_DEPS_SYNC_READ_DIRwhen the lockfile contains an injected package copy that no project depends on.shellEmulatornow expands${VAR},${VAR:-default}, and${VAR:+alternative}in scripts #14814.Cargo and Python project discovery now honors
!exclusions inpnpm-workspace.yamlpackages, skipping both parsing and generated source configuration for excluded projects #14844.pnpm --filter "./packages/{app,lib}"now selects either alternative. Brace alternatives can nest, span path separators, and combine with other wildcards.GitHub Actions updates now stop if an action reference changes during version resolution, and preserve unrelated workflow edits.
CLI and output
pn,pnpx, andpnxnow run the pnpm installed alongside them, even when that directory is absent fromPATHor another pnpm comes first #14803.pnpm --versionnow reports failures to install or record a project's pinned pnpm, then prints the running CLI's version. It also honors--store-dirand--store#14831.pnpm self-updateno longer reinstalls the active version when it was installed by the standalone installation script #14823.pnpm tandpnpm tstwork again as aliases forpnpm test.pnpm sbomnow emits valid repository URLs in CycloneDXexternalReferences[].urland SPDXhomepage. Shorthands such asvercel/msbecomegit+httpsURLs, embedded credentials are removed, and invalid repository values are omitted #14773.Platinum Sponsors
Gold Sponsors
v12.4.1: pnpm 12.4.1Compare Source
pnpm 12.4.1 fixes installs that failed on filesystems refusing hard links or clones, on Android, and under
nodeLinker: hoisted. Repeat installs are faster.Patch Changes
Installing packages
pnpm installno longer fails withOperation not permittedwhen the filesystem refuses a hard link or a copy-on-write clone #14722. UnderpackageImportMethod: autoandclone-or-copy, pnpm copies the file instead. EdenFS checkouts, which have no hard links, and rootless containers, which refuse the clone syscall, both hit this. An explicitpackageImportMethod: hardlinkorclonestill reports the error.pnpm also copies a package file whose store entry has reached the filesystem's limit on names for one file, 1024 on NTFS and 65000 on ext4. Such a file failed the install under
packageImportMethod: hardlink, and underautoit stopped pnpm hard linking for the rest of the install.pnpm installno longer writes a package file through a symlink left at the path it is importing to. Copying such a file overwrote whatever the link pointed at, and created that file when the link pointed nowhere. An executable package file also made the link's target executable.Fixed
pnpm installandpnpm dlxon Android. Registry requests crashed because pnpm found no system CA certificates, so pnpm uses bundled ones there #14777. Imports also failed with "Permission denied" on filesystems that deny hard links and reflinks, and now fall back to copying #14780.pnpm installno longer fails with "Invalid cross-device link" while preserving a package's nestednode_modulesdirectory during a Docker build #14758.pnpm installno longer fails on a package tarball that carries a file at the archive root, such as the._*entries macOStaradds #14701. The file is installed at the root of the package.A
file:tarball packed without the usualpackage/directory is now recorded under the name and version from its ownpackage.json. It was recorded under the alias the dependency was given, at version 0.0.0.Under
nodeLinker: hoisted,pnpm installno longer re-imports packages that are already in place. A repeat install replaced the wholenode_modulestree and reportedPackages: +N. A package is still imported when its directory is missing, when itspackage.jsonno longer carries the installed version, when it is afile:dependency, and when it is patched. Lifecycle scripts no longer run again for a package left in place, andpnpm rebuildand a change toallowBuildsstill reach it.pnpm installnow runs a dependency's build scripts again when its side-effects cache entry has no files to restore #14717. Such builds were skipped and nothing was put in their place, so a script whose whole effect lands outside its own package directory, such as a git hook installer, never took effect. pnpm no longer publishes empty artifacts to the shared side-effects cache either.Resolving and linking dependencies
pnpm install,pnpm add, andpnpm dedupenow applyignoredOptionalDependencies#14729. Matching optional dependencies are left out of the lockfile and are not installed. pnpm 12 installed them whenever it resolved dependencies from scratch.pnpm installno longer links a transitive dependency to a workspace package whenlinkWorkspacePackagesistrueand the dependency is declared with a plain version range #14781. EnablingpreferWorkspacePackagesdoes not change this. SetlinkWorkspacePackages: deepto link them.pnpm installno longer leaves dangling dependency links in workspace packages located above the workspace root #14726.pnpm installandpnpm addno longer leave a dangling symlink innode_moduleswhen a project starts depending directly on a package that the lockfile holds only as a transitive dependency with resolved peer dependencies #14714.pnpm dedupenow keeps a compatible auto-installed peer when another workspace project depends on a newer major #14697. Repeated runs alternated between compatible and incompatible peer versions.pnpm peers checkno longer reports a peer dependency declared asworkspace:^,workspace:~, or a bareworkspace:as unmet #14770. pnpm reported these as unmet whatever version the linked workspace project supplied.Performance
Sped up repeat installs #14540. pnpm checks the store's files only for the packages it links into
node_modules, instead of every package in the lockfile. Creating the command shims innode_modules/.binmakes about 1,500 fewer filesystem calls in a 76 project workspace. Installs that use the global virtual store read their slot paths from the cache directory instead of deriving them every time. Verifying a large lockfile also allocates less memory.Sped up
pnpm installin Cargo workspaces with many member crates. Repeated installs reuse verified Cargo checksum metadata.Installing several packages from the same Git repository and commit now downloads the source once per install #14725. Each package still runs its prepare scripts in its own copy of the checkout.
Running scripts and tasks
pnpm now passes Ctrl+C on to the script or command it started and waits for it to shut down #14723. pnpm exited first, so a script that was still writing landed on the shell prompt.
pnpm run "/pattern/" --no-bailnow lets every matched script finish after one of them fails #14718. The command exits withERR_PNPM_RUN_FAILED, and its message lists the scripts that failed in the order they were selected.pnpm pipelineno longer fails on a project that tracks a symlink, such as aCLAUDE.mdpointing atAGENTS.md#14692. Changing a symlinked input's target invalidates that task's cache, andpnpm pipeline --no-cacheno longer hashes task inputs.Commands
pnpm add -g,pnpm update -g, andpnpm remove -gno longer change global bins or install directories after reading only part of an installed package group #13796. If any declared package manifest is missing, malformed, or unreadable, pnpm now fails before it activates or removes anything and leaves the existing global installation intact.pnpm dedupenow processes every workspace project by default, including workspaces that keep a separate lockfile per project #14732. Workspace filters select which projects it processes, and--fail-if-no-matchexits with an error when no project matches.pnpm update <name>@<version>now keeps the range operator the manifest declares #14745. Runningpnpm update react@19.3.0on"react": "^19.2.8"writes"react": "^19.3.0". Ajsr:entry keeps itsjsr:prefix, and a plainpnpm updatenow moves ajsr:range the way it moves an npm range.pnpm --filterdirectory selectors now support?wildcards and character classes such as[ab]. A*or?wildcard no longer selects a directory whose name starts with a dot, as on pnpm 11.pnpm deploy --legacynow prefers the dependency versions pinned in the source workspace lockfile when they still satisfy the deployed project's range #13857.pnpm sbomnow leaves out a package's author field when the manifest author name is empty or contains only whitespace #14685. In a filtered or split workspace run, only a project with noauthorfield inherits the workspace root's author.pnpm sbom --sbom-format spdxnow writescreationInfo.createdwith whole seconds, such as2026-09-08T10:38:21Z#14684. The fractional seconds it carried were rejected by strict SPDX consumers.Configuration
The
updateConfigpnpmfile hook now receives the resolved configuration, including settings that came from.npmrc, the command line, or a default #14676. Scoped registries are reported underregistriesByScope, and a hook may rewrite that map to change where packages are fetched from. Registry credentials are reported underconfigByUri, as pnpm 11 reports them. An unset setting is left out rather than reported asnull.pnpm audit --fixand theminimumReleaseAgeStrictapproval prompt now keep the comments inminimumReleaseAgeExcludewhen they append an entry to it inpnpm-workspace.yaml. The rest of the list is left as written, and thetrustPolicyExcludePruneandminimumReleaseAgeExcludePrunecleanups keep the comments of the entries they retain.pnpm installandpnpm dedupenow run those cleanups too #14759. Onlypnpm add,pnpm update, andpnpm removepruned the entries that the freshly written lockfile no longer resolves.pnpm config set --global node-download-mirrorsno longer rejects the key #13611. The global config file already acceptednodeDownloadMirrors, but the command refused to write it.NO_PROXYentries that start with a dot, such as.npmjs.org, now bypass the proxy for the domain and its subdomains #14686.pnpm no longer creates a project
pnpm-lock.yamlwhendevEngines.packageManager.onFailisdownloadand lockfile writing is off throughlockfile: falseor--no-lockfile#14728. pnpm still switches to the pinned version.pnpm now writes
node_modules/.package-map.jsononly whennodeExperimentalPackageMapis enabled. Nothing reads the file without that setting, and an install that stops writing the map removes the one a previous install left.Windows
pnpm pipelineno longer fails with intermittent access denied errors when concurrent tasks save their cache entries on Windows.Windows filesystem operations now retry permission errors for up to one second #14682. A permanent permission error delayed the failure by a minute. Sharing and lock violations keep their one minute retry budget.
Messages and output
pnpm now warns when the root
package.jsondeclares a non-emptyworkspacesarray and the project has nopnpm-workspace.yaml#2255. Such an install linked no project and said nothing about why.ERR_PNPM_PACKAGE_MANAGER_REMOVE_MODULES_DIRnow names the file or directory innode_modulesthat pnpm could not clean up. It reported only the underlying OS error, such as "Access is denied (os error 5)".pnpm --helpno longer describes pnpm as experimental.Platinum Sponsors
Gold Sponsors
v12.4.0: pnpm 12.4Compare Source
Minor Changes
pnpm can now manage npm, Python, and Cargo dependencies in the same workspace. Enable
python.enabledorcargo.enabledinpnpm-workspace.yaml, then usepnpm installto install them together.pnpm add pypi:<package>. pnpm usespyproject.toml,pylock.toml, and a managed.venv. Frozen and offline installs are supported, andpnpm runandpnpm execmake the environment's executables available #14566.pnpm add crate:<package>. pnpm supports crates.io and custom sparse registries configured withcargo.indexUrl. Registry authentication supports pnpm credentials and, for crates.io,CARGO_REGISTRY_TOKENor$CARGO_HOME/credentials.toml.Both ecosystems support faster dependency resolution through
pnprServer, with local resolution as a fallback when the server does not support it.Added
pnpm pipeline [name]to install frozen dependencies and run workspace tasks declared inpipelines. It selects affected projects, runs their task graph, and continues running tasks after a task fails.Tasks support
inputs,outputs,env, andcachesettings. Cached results restore task outputs and replay logs. Cargo tasks can reuse local build state between worktrees withtasks.<name>.cargoTargetDir. SetincludeWorkspaceRoot: trueto include root tasks.Use
pnpm pipeline --dry-runto preview the task graph without installing configuration dependencies or running workspace hooks.Added support for Android on arm64 and x64, FreeBSD on x64, and Linux on ppc64le, s390x, and RISC-V (riscv64 with glibc) #14431, #14597, #7582.
Added
trustPolicyExcludePruneto automatically remove unused versions and packages fromtrustPolicyExcludewhen runningpnpm add,pnpm update, orpnpm remove. It is disabled by default. Package name patterns such as@scope/*are kept, and cleanup is skipped whensharedWorkspaceLockfileisfalse.Added
pnpm change checkfor CI validation of package versions against theversioning.epicsbands andversioning.fixedgroups inpnpm-workspace.yaml. It reports all violations, including packages that are not part of the current release.Patch Changes
Registry metadata is now kept separate for registries with different URL paths or schemes. This prevents installs from using another registry's package versions or tarball URLs, and keeps metadata fetched over HTTP from being reused for HTTPS #13558.
The first install after upgrading refetches registry metadata. The package store is unchanged.
pnpm cache viewnow shows full registry URLs. Scripts that parse the directory names frompnpm cache list-registriesorpnpm cache listneed updating.Patches that add build scripts or a
binding.gypnow trigger a build, subject to build approval. Unapproved builds appear under "Ignored build scripts" #14648.Build scripts can now be rejected before installing a package with
pnpm add --allow-build=!<pkg>, including global installs.pnpm approve-builds <pkg>andpnpm approve-builds !<pkg>also save decisions when no packages are awaiting approval. They warn if the named package is not awaiting approval #14067.A registry configured in
.npmrcnow takes precedence over registry settings saved bypnpm loginin the globalconfig.yaml. This fixes installs using the wrong registry after login #14614.Large downloads over slow connections no longer time out while data is still arriving.
fetch-timeoutnow limits how long a request can go without making progress #14604.Sped up installs in workspaces with many projects when reusing a warm global virtual store #14540.
pnpm deployis faster in large workspaces and no longer fails withERR_PNPM_LOCKFILE_CONFIG_MISMATCHwhen the project includes a.pnpmfile.mjs#14539, #14671.pnpm add --workspace <pkg>works again. It saves the dependency with theworkspace:protocol and links it from the workspace. The command fails if no workspace project provides the package #14602.pnpm addandpnpm installnow accept protocol-prefixed selectors such asjsr:@scope/pkg,npm:pkg@^1.0.0, andworkspace:pkg@*#14590. Installs with JSR dependencies in the lockfile also no longer fail withERR_PNPM_META_FETCH_FAIL#14649.Boolean flags now accept explicit inline values. For example,
pnpm install --prod=falseinstalls devDependencies, while--prod=trueskips them #14553.pnpm install <pkg>now accepts--offlineand--prefer-offline, aspnpm add <pkg>already did #14194.Fixed
pnpm install --frozen-lockfilerejecting a freshly generated lockfile when overrides use relativefile:orlink:paths in a workspace #14555.Fixed installs with config dependencies failing on symlinked lockfiles, such as those used by Bazel and Nix, when the config dependencies have not changed. Updates that would write through a symlink remain disallowed. Updating config dependencies also preserves lockfiles that start with a byte order mark #14372.
Fixed package manager version pins being written to the wrong lockfile when
lockfileDiris set. The pins also remain consistent across commands when version switching is disabled, avoiding unnecessary lockfile changes #14633, #14575.pnpm importnow respectslockfileDirand branch lockfiles without modifying other lockfiles. Failed imports restore the destination lockfile #14563.pnpm patch-commitnow produces valid patches when files are added or deleted.pnpm installalso accepts patches that delete files without listing their contents, and patch files with CRLF line endings #14559, #14557.Fixed version ranges with partial upper bounds. For example,
<=16now includes all 16.x versions, and>=0.11 <=3correctly accepts 3.0.1 #14419.Workspace package patterns now support
.and..segments and repeated slashes. Patterns such as./packages/*and exclusions such as!./packages/foonow match correctly #14571.packageConfigssettings now apply to the specified projects whensharedWorkspaceLockfileisfalse, includingoverrides,hoist,modulesDir,saveExact, andsavePrefix. Workspaces with a shared lockfile report which entries were ignored #14556.pnpm runandpnpm execno longer report a changed workspace structure after a successful install whensharedWorkspaceLockfileisfalseandverifyDepsBeforeRunis enabled #14588.Commands run from a project's subdirectory now find the nearest ancestor with a manifest. This fixes commands such as
pnpm binreturning paths under the wrong directory.pnpm initstill creates its manifest in the current directory, andpnpm execstill runs there #14622.Relative
scriptShellpaths inpnpm-workspace.yamlnow resolve from the workspace root, including when scripts run in nested packages. Bare command names such asbashstill usePATH#14422.Fixed installing the pnpm version pinned in
packageManagerwhennodeLinkerishoisted. Managed Node.js, Deno, and Bun installations also work when the global config usesnodeLinker: hoisted#14595.The JavaScript pnpm can again switch to a project's pinned pnpm version on platforms without a native binary for that version, such as Alpine Linux with pnpm 10 or Intel Macs with pnpm 11. If a native pnpm version does not support the platform, the error now names the missing target #13622.
Provisioning Yarn 6 now uses
GH_TOKENorGITHUB_TOKENwhen available to avoid GitHub's anonymous API rate limit in CI. Tokens are only sent whenstrict-sslis enabled.Fixed concurrent installs sharing a global virtual store on macOS failing with "failed to import ... No such file or directory" #14560.
Fixed
pnpm setupfailing withERR_PNPM_DIRECTORY_FETCHER_PATH_ESCAPEon Windows. Localfile:dependencies whose directories are symlinks or junctions are now packed correctly #14618.On Windows, installs now retry replacing command shims temporarily locked by another process #14549.
Fixed argument forwarding on Windows with
shellEmulatorenabled. Paths ending in a backslash, line breaks, and literal shell expressions are preserved #14548.Windows store paths now consistently use backslashes in
pnpm store pathoutput and in thestoreDirandvirtualStoreDirfields ofnode_modules/.modules.yaml.Invalid certificates in
caorcafileno longer cause anInvalid CA certificateerror. Valid certificates still apply, and blankcertorkeyvalues are treated as unset #14646.Installs now respect the archive extraction concurrency limit even after a download is abandoned #14585.
pnpm auditsummaries now exclude advisories ignored throughauditConfig.ignoreGhsasand report them separately. When all advisories are ignored, the summary says so #14535.pnpm pack --jsonnow reports errors as JSON. Lifecycle script output appears before the final JSON output.pnpm outdated -rnow wraps theDependentscolumn, keeping the table readable when many workspace projects use the same dependency #14591.Shell completions now support the
pnalias in bash, fish, pwsh, and zsh #11955.pnpm versionnow accepts-mas a short alias for--message#14567.Platinum Sponsors
Gold Sponsors
kulshekhar/ts-jest (ts-jest)
v29.4.14Compare Source
Reverts
v29.4.13Compare Source
Features
Performance Improvements
Configuration
📅 Schedule: (UTC)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
👻 Immortal: This PR will be recreated if closed unmerged. Get config help if that's undesired.
This PR has been generated by Mend Renovate CLI.
3c10731166toa38059ed37chore(deps): update all-minor-updatesto fix(deps): update all-minor-updatesa38059ed37toe0b98d28e2e0b98d28e2to6ab996d83c6ab996d83ctoff19289be8ff19289be8tofacd8256d2View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.