pnpm 12.7.0 → 12.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,283 @@
1
1
  # pnpm
2
2
 
3
+ ## 12.8.0
4
+
5
+ pnpm 12.8.0 warns when `pnpm pack` or `pnpm publish` would ship a `.env` file that `files` does not list, installs `sharedWorkspaceLockfile: false` workspaces concurrently, applies every setting passed as `--config.<name>=<value>`, and no longer leaves the Windows terminal stuck after Ctrl+C in a script.
6
+
7
+ ### Minor Changes
8
+
9
+ - `pnpm pack` and `pnpm publish` now warn when the tarball includes a `.env` or `.env.*` file that the `files` field of `package.json` does not list. Templates such as `.env.example` are not reported. List the file in `files` to publish it on purpose, or exclude it in `.npmignore` or `.gitignore` [#7826](https://github.com/pnpm/pnpm/issues/7826).
10
+
11
+ - `pnpm pack` now honors `--silent`, `--reporter=silent`, and `--loglevel=silent` to hide the tarball contents and summary. With `--json`, lifecycle script output and the final JSON output remain visible [#10297](https://github.com/pnpm/pnpm/issues/10297).
12
+
13
+ ### Patch Changes
14
+
15
+ #### Installing packages
16
+
17
+ - Installing through a `pnpr` server now records the pnpmfile checksum in the lockfile, so a later `pnpm install --frozen-lockfile` accepts that lockfile [#14460](https://github.com/pnpm/pnpm/issues/14460). A frozen install through the pnpr server now fails if the pnpmfile changed. If the pnpmfile defines a `readPackage`, `afterAllResolved` or `preResolution` hook or custom resolvers, pnpm resolves dependencies locally and prints a warning that the pnpr server was not used.
18
+
19
+ Installing through a `pnpr` server also links a workspace project at the directory its `publishConfig.directory` names. A server that does not forward the setting makes the install fail with `ERR_PNPM_PNPR_PUBLISH_DIRECTORY_MISMATCH`, so pnpm never writes a lockfile that points at the wrong directory. The server rejects a `publishConfig.directory` that points outside its project.
20
+
21
+ - Installing a git-hosted dependency that has to be built no longer fails when that dependency's own dependencies have build scripts nobody approved. pnpm skips those builds while preparing the dependency, as it does without `strictDepBuilds` [#9764](https://github.com/pnpm/pnpm/issues/9764).
22
+
23
+ - A git-hosted dependency that is a pnpm workspace with no committed lockfile is now detected as a pnpm project [#14011](https://github.com/pnpm/pnpm/issues/14011).
24
+
25
+ - `pnpm install --dev` and `pnpm fetch --dev` now install the optional dependencies of devDependencies, such as the platform binaries of Biome and oxlint. The project's own `optionalDependencies` are still skipped [#9678](https://github.com/pnpm/pnpm/issues/9678).
26
+
27
+ - `pnpm install --offline` and `pnpm add --offline` now resolve a version range to the newest matching version whose tarball is already in the store. They used to pick the newest version in the cached metadata and fail with `ERR_PNPM_NO_OFFLINE_TARBALL` when its tarball was missing [#10715](https://github.com/pnpm/pnpm/issues/10715).
28
+
29
+ - If an offline install fails because the registry metadata cache uses the layout from before pnpm 11.27 and 12.4, the error now names the older mirror on disk and explains that one online install repopulates the cache. The error also carries the `ERR_PNPM_NO_OFFLINE_META` code. `pnpm cache prune --help` now says that pnpm 11.26 and earlier, and pnpm 12.3 and earlier, depend on the directories it removes [#15656](https://github.com/pnpm/pnpm/issues/15656).
30
+
31
+ - Running `pnpm install` now refreshes dependencies when a package declared with a local `file:` directory changes its dependencies [#4623](https://github.com/pnpm/pnpm/issues/4623).
32
+
33
+ - A repeat `pnpm install` now keeps its fast up-to-date check when an override replaces a declared local `file:` dependency [#12892](https://github.com/pnpm/pnpm/issues/12892).
34
+
35
+ - `pnpm install` now removes an optional dependency from `node_modules` if its install script fails. Code that checks whether the package is installed no longer finds a package that cannot load [#8756](https://github.com/pnpm/pnpm/issues/8756).
36
+
37
+ - With `nodeLinker: hoisted`, `pnpm install` now restores a workspace project's `node_modules` after it was deleted. Before, the install printed "Already up to date" and left the project without the dependencies nested under it. On Windows, the install also no longer fails with "Access is denied" when another project's copy of a shared dependency links to the deleted directory.
38
+
39
+ - Under `nodeLinker: hoisted`, `pnpm install` now clears orphaned package directories that an interrupted or failed install leaves in a project's `node_modules`. A directory recorded by the previous install is removed, while an unrecorded directory is moved to `node_modules/.ignored`. A copy already in `.ignored` is never overwritten [#13676](https://github.com/pnpm/pnpm/issues/13676).
40
+
41
+ - Concurrent installs no longer fail when they replace the same stale hoisted dependency link. Virtual store cleanup now keeps the temporary lockfiles that concurrent installs write.
42
+
43
+ #### Resolving and linking dependencies
44
+
45
+ - `pnpm install` no longer aborts on a failed allocation of many gigabytes when peer dependency ranges combine overlapping `||` alternatives [#15867](https://github.com/pnpm/pnpm/issues/15867).
46
+
47
+ - `pnpm install` no longer fails when a package from the registry declares a `file:` dependency on a directory inside itself, such as `"@types/css-tree": "file:./typings/css-tree"`. pnpm links that dependency to the directory inside the package, as npm and Yarn do. The lockfile records it as `link:<root>/typings/css-tree` [#9141](https://github.com/pnpm/pnpm/issues/9141).
48
+
49
+ - An `npm:` alias written by `overrides` now stays in place when a change elsewhere makes pnpm re-resolve the aliased dependency. Before, pnpm could look up the alias name at the aliased version, which failed with `ERR_PNPM_NO_MATCHING_VERSION` or locked an unrelated package [#16309](https://github.com/pnpm/pnpm/issues/16309).
50
+
51
+ - A peer dependency no longer resolves to two different versions for one package. This happened when the package peer-depends on another package and on one of that package's peers, and it is installed deeper than a direct dependency of the package that provides them [#12098](https://github.com/pnpm/pnpm/issues/12098).
52
+
53
+ - An optional peer dependency is no longer resolved from another workspace project's package when the project provides one of that package's own peers at a version it rejects. This avoids bogus unmet peer errors [#13989](https://github.com/pnpm/pnpm/issues/13989).
54
+
55
+ - `pnpm dedupe` no longer changes the lockfile on every run when a nested peer dependency is provided through an npm alias [#15709](https://github.com/pnpm/pnpm/issues/15709).
56
+
57
+ - With `resolutionMode: time-based` and `minimumReleaseAge` both set, `pnpm install` no longer reports a subdependency as too new when only the time-based cutoff excludes it. Such subdependencies used to fail a strict install with `ERR_PNPM_NO_MATURE_MATCHING_VERSION`, or were added to `minimumReleaseAgeExclude` [#13569](https://github.com/pnpm/pnpm/issues/13569). A transitive dependency that has no matching version published before the time-based cutoff now resolves to the lowest matching version allowed by `minimumReleaseAge`. pnpm picks a version younger than `minimumReleaseAge` only if no older version matches [#16298](https://github.com/pnpm/pnpm/issues/16298).
58
+
59
+ - `pnpm install` retries registry metadata fetches that fail with a timeout, a dropped connection, or an interrupted response body before it applies `trustPolicy` or `minimumReleaseAge`. A transient fetch failure is not reported as `TRUST_DOWNGRADE` or `MINIMUM_RELEASE_AGE_VIOLATION` [#12031](https://github.com/pnpm/pnpm/issues/12031).
60
+
61
+ - pnpm's built-in package compatibility database no longer applies to a project's own manifest. A project named like a published package, such as `vue-loader`, no longer gains dependencies on `pnpm install` or `pnpm update`. User-configured `packageExtensions` still apply to project manifests [#11700](https://github.com/pnpm/pnpm/issues/11700).
62
+
63
+ - Packages in an external `virtualStoreDir` can resolve the project's direct dependencies selected by `hoistPattern`. Run `pnpm install --force` to repair an existing installation [#5652](https://github.com/pnpm/pnpm/issues/5652).
64
+
65
+ - `pnpm install` now links the executables of auto-installed peer dependencies into the workspace root's `node_modules/.bin`, including after a frozen-lockfile reinstall [#8511](https://github.com/pnpm/pnpm/issues/8511).
66
+
67
+ #### Lockfiles and frozen installs
68
+
69
+ - `pnpm install --frozen-lockfile` now works on a detached HEAD when `gitBranchLockfile` is enabled. The install reads the lockfiles of the local and remote-tracking branches that contain the checked-out commit. It still writes the shared `pnpm-lock.yaml` [#7672](https://github.com/pnpm/pnpm/issues/7672).
70
+
71
+ - `pnpm install --frozen-lockfile` now accepts a lockfile that has no importer entry for a workspace package without dependencies. Such a package added after the lockfile was written made the install fail with `ERR_PNPM_PACKAGE_MANAGER_NO_IMPORTER` [#15875](https://github.com/pnpm/pnpm/issues/15875).
72
+
73
+ - `pnpm install` now fails with `ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY` when an importer references a dependency version that has no snapshot entry. Before, the install succeeded and left a `node_modules` symlink pointing at a missing virtual-store directory [#14764](https://github.com/pnpm/pnpm/issues/14764).
74
+
75
+ - `pnpm install` on CI now fails on an outdated lockfile when `preferFrozenLockfile` is explicitly set to `true`. Setting it to `true` used to let CI update the lockfile [#9072](https://github.com/pnpm/pnpm/pull/9072).
76
+
77
+ - With `gitBranchLockfile` enabled, each emoji or other character outside the Basic Multilingual Plane in a branch name now becomes `!!` in the lockfile name. Before, each such character became one `!`.
78
+
79
+ #### Workspaces and filtering
80
+
81
+ - `pnpm install` in a workspace with `sharedWorkspaceLockfile: false` now installs projects concurrently, up to `workspaceConcurrency` at a time [#14480](https://github.com/pnpm/pnpm/issues/14480). A project is resolved, fetched, and written to its virtual store without waiting for the workspace projects it depends on. It waits for them only before it links its dependencies and runs its lifecycle scripts, so its scripts still run after theirs. A project with a `preinstall` or `pnpm:devPreinstall` script, or with an injected or `file:` workspace dependency, waits for its workspace dependencies before it starts.
82
+
83
+ The installs of the projects also share their package metadata, lockfile verification, and store caches, so they use less CPU and memory when several projects depend on the same packages. An install with a pnpmfile no longer starts an extra Node.js process when the pnpmfile has no `preResolution` hook.
84
+
85
+ - With `enableGlobalVirtualStore` and `sharedWorkspaceLockfile: false`, each project now keeps its current lockfile and its hidden hoisted dependencies in its own `node_modules/.pnpm`. Before, every project wrote them to the workspace root's `node_modules/.pnpm`, so each repeat install treated the other projects' packages as its own and relinked them [#14480](https://github.com/pnpm/pnpm/issues/14480).
86
+
87
+ - `pnpm rebuild`, `pnpm approve-builds`, and `pnpm ignored-builds` now work on the current project's `node_modules` when they run inside a project of a workspace with `sharedWorkspaceLockfile: false`. They used to read the workspace root's `node_modules`, so `pnpm rebuild` did not rebuild the project's dependencies and created a second virtual store at the workspace root [#9402](https://github.com/pnpm/pnpm/issues/9402).
88
+
89
+ - `pnpm install` no longer creates a `node_modules` symlink inside the `publishConfig.directory` of a workspace package linked with `linkDirectory`. A build tool that cleaned its output directory through that symlink deleted the files of the package's dependencies. `pnpm install` also removes a symlink that an earlier install left there [#16226](https://github.com/pnpm/pnpm/issues/16226). It also no longer fails with `ERR_PNPM_CMD_SHIM_RESOLVE_PATH` when such a package has a `bin` field and its `publishConfig.directory` does not exist yet.
90
+
91
+ - `pnpm install` no longer fails for an injected workspace dependency whose package publishes from a `publishConfig.directory` that its own `prepare` script builds. The injected copy now picks up that directory once `prepare` finishes building it. `pnpm install --frozen-lockfile` no longer reports the dependency as outdated while the directory has not been built yet [#7811](https://github.com/pnpm/pnpm/issues/7811).
92
+
93
+ - An in-place edit to the source of an injected workspace package now shows up in its injected copy, unless a build writes to that package or `packageImportMethod` is set. pnpm hardlinks such packages under the default import method [#4410](https://github.com/pnpm/pnpm/issues/4410). Scripts listed in `syncInjectedDepsAfterScripts` now update injected dependencies while they run, so a watcher on the injected package, such as a dev server, sees each change before the script exits.
94
+
95
+ - With `sharedWorkspaceLockfile: false`, an injected workspace package that has lifecycle scripts is now hard linked into the projects that depend on it. Before, pnpm left a plain copy, so later edits to the package did not reach those projects [#9828](https://github.com/pnpm/pnpm/issues/9828).
96
+
97
+ - `injectWorkspacePackages` now hard links a workspace dependency declared with a relative path, such as `workspace:../foo`, the same way it already does for `workspace:*` [#10446](https://github.com/pnpm/pnpm/issues/10446).
98
+
99
+ - Workspace discovery prunes dot-prefixed directories, so a `packages` pattern such as `**` no longer matches projects inside `.cache` and other hidden directories [#16250](https://github.com/pnpm/pnpm/issues/16250).
100
+
101
+ - `pnpm import` in a workspace now keeps the versions pinned by a `yarn.lock` inside a workspace project [#4385](https://github.com/pnpm/pnpm/issues/4385).
102
+
103
+ #### Store and caches
104
+
105
+ - Files imported from the store now follow the umask of the install that writes them. Installing with a umask of `077` no longer leaves imported files readable by the group and others [#3807](https://github.com/pnpm/pnpm/issues/3807).
106
+
107
+ - `pnpm install` keeps the owner, group, and mode of files already in a shared store, including `index.db`. New store files and directories inherit the store directory's group-write bit. When that directory is setgid, new files inherit its group. pnpm does not change a file's owner or group [#12765](https://github.com/pnpm/pnpm/issues/12765).
108
+
109
+ - When `pnpm install` repairs a store file that was modified through a hard link in `node_modules`, the repair now keeps the file's inode on Linux and macOS, so hard-linked copies in other projects are healed at the same time. On Windows the repair still replaces the file, so other projects are healed on their next install [#3445](https://github.com/pnpm/pnpm/issues/3445).
110
+
111
+ - `pnpm install` now reports a full store at once when writing package files fails. It no longer retries the tarball [#8581](https://github.com/pnpm/pnpm/issues/8581).
112
+
113
+ - pnpm now warns when it cannot hard link packages from an existing store in the pnpm home directory and falls back to a store on the project's filesystem. This can happen when the project is on another filesystem, such as a bind-mounted workspace in a container. The warning names both stores and suggests setting `storeDir` [#14505](https://github.com/pnpm/pnpm/issues/14505).
114
+
115
+ - The side-effects cache now restores the symlinks that a build script creates inside a package. A warm install used to replace each of them with a copy of its target [#12859](https://github.com/pnpm/pnpm/issues/12859).
116
+
117
+ After upgrading, every package with a build script is built once more.
118
+
119
+ - The global virtual store and the side-effects cache now key built packages by the Node.js version that the root project's `devEngines.runtime` or `engines.runtime` pins. That is the Node.js their build scripts run with. A dependency that declares its own `engines.runtime` no longer changes the key for every other package.
120
+
121
+ - With `enableGlobalVirtualStore`, an install into a fresh `node_modules` no longer runs the build scripts of a dependency whose global virtual store slot an earlier install already built. `pnpm rebuild` still runs them [#14480](https://github.com/pnpm/pnpm/issues/14480).
122
+
123
+ - Concurrent installs that share a global virtual store now run a package's build in its shared slot one at a time. A failed build leaves the slot in place and marks it for the next install to rebuild [#15568](https://github.com/pnpm/pnpm/issues/15568).
124
+
125
+ - A warm `pnpm install` reuses on-disk package metadata for five minutes when the registry does not send an ETag. Registries that send an ETag, including the public npm registry, still revalidate with a conditional request. `pnpm update` still fetches current metadata [#13976](https://github.com/pnpm/pnpm/issues/13976).
126
+
127
+ - pnpm no longer revalidates cached registry metadata when the registry sends `Cache-Control: max-age=0`, `no-cache`, or `no-store`. It downloads the metadata again, so a version newly published to such a registry is visible on the next install [#13487](https://github.com/pnpm/pnpm/issues/13487).
128
+
129
+ - `pnpm install` honors `Cache-Control` for dependencies named with an `http:` or `https:` tarball URL. A fresh response is taken from the store with no request, and a stale one is revalidated with `If-None-Match` [#15648](https://github.com/pnpm/pnpm/issues/15648).
130
+
131
+ #### Patched dependencies
132
+
133
+ - `pnpm install` now repairs a `pnpm-lock.yaml` whose `(patch_hash=<hash>)` dependency paths disagree with its `patchedDependencies` map, including paths that lack the hash their patch calls for. Before, pnpm accepted such a lockfile as up to date and kept the old patched files. `pnpm install --frozen-lockfile` now fails on such a lockfile with `ERR_PNPM_INCONSISTENT_PATCH_HASH`. It fails with `ERR_PNPM_UNCHECKABLE_PATCH_HASH` when a patch hash in the lockfile is malformed, or when the lockfile lacks the package version or patch entry that the check needs [#15336](https://github.com/pnpm/pnpm/pull/15336).
134
+
135
+ - `pnpm install` with `nodeLinker: hoisted` now applies a patch once to each copy of a patched dependency in a workspace. Before, a copy that several workspace projects shared could receive the patch twice and end up with the patched content duplicated [#7565](https://github.com/pnpm/pnpm/issues/7565).
136
+
137
+ - `pnpm install` and `pnpm fetch` now fail with `ERR_PNPM_PATCH_NOT_FOUND` when a patch file listed in `patchedDependencies` does not exist [#5268](https://github.com/pnpm/pnpm/issues/5268).
138
+
139
+ - `engineStrict` now checks the patched `package.json` when a `patchedDependencies` entry changes `engines`. A patch that relaxes `engines.node` no longer fails the install against the published range [#9603](https://github.com/pnpm/pnpm/issues/9603).
140
+
141
+ - `pnpm patch` now applies the existing patch file to the edit directory of a git-hosted dependency, as it already does for packages from the registry [#9699](https://github.com/pnpm/pnpm/issues/9699).
142
+
143
+ #### Adding, updating, and removing dependencies
144
+
145
+ - `pnpm add <dir>` now warns when the added directory declares peer dependencies, as `pnpm link` does. The directory is saved as a `link:` dependency, and its peers are not resolved from the project that adds it. Use the `file:` protocol to have them resolved [#5523](https://github.com/pnpm/pnpm/issues/5523).
146
+
147
+ - `pnpm add --save-types` no longer adds a `@types/*` package whose resolved version is deprecated. DefinitelyTyped publishes such stubs for packages that ship their own types, such as `@types/typescript` for `typescript` [#15636](https://github.com/pnpm/pnpm/issues/15636).
148
+
149
+ - `pnpm version`, `pnpm add`, and `pnpm pkg set` keep JSON5 style when they update `package.json5`. ASCII identifier keys stay unquoted, strings keep JSON5 quotes, and indented files keep trailing commas [#15717](https://github.com/pnpm/pnpm/issues/15717).
150
+
151
+ #### Running scripts and commands
152
+
153
+ - `pnpm run` and `pnpm exec` no longer install dependencies automatically when the root `package.json` still keeps `overrides`, `packageExtensions`, `patchedDependencies`, or `ignoredOptionalDependencies` in its `pnpm` field. pnpm no longer reads that field, so the install rewrote the lockfile without those settings. The command now fails and asks to move the settings to `pnpm-workspace.yaml` [#16278](https://github.com/pnpm/pnpm/issues/16278).
154
+
155
+ - When `verifyDepsBeforeRun` triggers an install before a filtered `pnpm run` or `pnpm exec`, pnpm now installs only the selected projects and their dependencies. A later filtered command also installs a selected project that an earlier filtered install skipped [#11865](https://github.com/pnpm/pnpm/issues/11865).
156
+
157
+ - `pnpm -r run /regexp/` now honors the `tasks` `dependsOn` declared for each script the selector matches, like running the script by name does. Matched scripts that depend on each other run in order. Each matched script runs once [#15596](https://github.com/pnpm/pnpm/issues/15596).
158
+
159
+ - `pnpm run` exits with the code of a script that handles Ctrl+C and shuts down. A script that finished cleanly is not reported as a lifecycle failure. The commands after it in the same script still run [#9945](https://github.com/pnpm/pnpm/issues/9945).
160
+
161
+ - pnpm no longer hangs after a lifecycle script exits while a process it started in the background keeps the script's output open. pnpm stops reading that output one second after the script exits [#5730](https://github.com/pnpm/pnpm/issues/5730).
162
+
163
+ - `pnpm run` and lifecycle scripts use the configured `scriptShell`, including Git Bash on Windows, when `shellEmulator` is also enabled. `shellEmulator` still runs scripts when `scriptShell` is not set. Extra arguments passed to `pnpm run` are quoted for the shell that runs the script, so a Windows path stays intact [#14719](https://github.com/pnpm/pnpm/issues/14719).
164
+
165
+ - With `enableGlobalVirtualStore`, dependency build scripts now see the workspace root's `node_modules/.bin`, as they do with a local virtual store. A `postinstall` script that runs `node` finds the Node.js installed by `devEngines.runtime` and no longer fails with "command not found" on machines without a system Node.js [#15652](https://github.com/pnpm/pnpm/issues/15652). Dependency build scripts also see the bins of privately hoisted dependencies.
166
+
167
+ - Dependency install scripts now find the node-gyp bundled with pnpm when pnpm runs through a symlink, such as `node_modules/.bin/pnpm` or the `pnpm` that `npm install -g pnpm` links. They used to fail with `node-gyp: command not found` on macOS [#15694](https://github.com/pnpm/pnpm/issues/15694).
168
+
169
+ - `pnpm run` and lifecycle scripts now set `npm_config_node_gyp` to the bundled `node-gyp` entry point. Tools that read the variable resolve the same `node-gyp` pnpm builds with. An `npm_config_node_gyp` value the environment already sets is kept as is [#16270](https://github.com/pnpm/pnpm/issues/16270).
170
+
171
+ - Scripts now see the `npm_command` environment variable that npm sets. It holds `run-script` when the command runs a script, and the command's own name otherwise [#16265](https://github.com/pnpm/pnpm/issues/16265).
172
+
173
+ - Commands run from a POSIX shell through a dependency's own `node_modules/.bin`, such as `node_modules/vite/node_modules/.bin/esbuild`, no longer fail with `MODULE_NOT_FOUND` [#10189](https://github.com/pnpm/pnpm/issues/10189).
174
+
175
+ - `pnpx --version` and `pnpm dlx --version` now print the pnpm version. Other unknown options before the command are reported as errors. Before, pnpm tried to download a package named after the option [#16259](https://github.com/pnpm/pnpm/issues/16259).
176
+
177
+ - `pnpm dlx` now keeps the virtual store of its cached installs in `node_modules/.pnpm`, like every other install [#13955](https://github.com/pnpm/pnpm/issues/13955). `pnpm pack-app` now names the manifest of its runtime install directory `pnpm-pack-app-<target>`.
178
+
179
+ #### Publishing, packing, and deploying
180
+
181
+ - `pnpm pack` and `pnpm publish` now ship a file that the `files` field names even when another entry excludes the directory holding it. For example, `["**", "!dist", "dist/index.d.ts"]` ships `dist/index.d.ts` [#16213](https://github.com/pnpm/pnpm/issues/16213).
182
+
183
+ - `pnpm pack` prunes a directory that a `files` field exclusion names, such as `!**/test`, excluding the directory and its contents from the packed package [#15738](https://github.com/pnpm/pnpm/issues/15738).
184
+
185
+ - `pnpm publish` now waits at least 5 minutes for the registry to answer a publish request, like npm. This fixes "409 Conflict - Failed to save packument" errors when the registry is slow to answer [#11454](https://github.com/pnpm/pnpm/issues/11454).
186
+
187
+ - `pnpm deploy --prod` no longer fails with `ERR_PNPM_OUTDATED_LOCKFILE` when the deployed project declares a `devEngines.runtime` with `onFail: download`. The runtime stays out of the deployed `node_modules` with the rest of the dev dependencies [#15703](https://github.com/pnpm/pnpm/issues/15703).
188
+
189
+ - `pnpm deploy` with a shared lockfile now copies workspace dependencies into the deploy directory, even when `packageImportMethod` is set to `hardlink`. Before, their files were hard-linked to the workspace sources, so editing a source file also changed the deployed copy [#12176](https://github.com/pnpm/pnpm/issues/12176).
190
+
191
+ - `pnpm deploy --legacy` no longer leaves broken links to nested local dependencies of workspace packages [#9575](https://github.com/pnpm/pnpm/issues/9575).
192
+
193
+ #### Configuration and pnpmfile hooks
194
+
195
+ - Every setting pnpm supports can now be set with `--config.<name>=<value>` on the command line, not only the ones whose command also carries a matching flag. Before, `pnpm install --config.frozen-lockfile=true` dropped the setting and rewrote `pnpm-lock.yaml` as though the install had not been frozen [#16276](https://github.com/pnpm/pnpm/issues/16276).
196
+
197
+ - Settings given on the command line, such as `--registry` and `--store-dir`, now take precedence over the values a pnpmfile `updateConfig` hook sets [#14063](https://github.com/pnpm/pnpm/issues/14063).
198
+
199
+ - `pnpm config set --location=project` and `pnpm config delete --location=project`, run from a package inside a workspace, now write settings that belong in `pnpm-workspace.yaml` to the workspace root's `pnpm-workspace.yaml`. Before, they created a new `pnpm-workspace.yaml` in the current package, which made that package the workspace root. Settings stored in `.npmrc` are still written to the current directory [#13757](https://github.com/pnpm/pnpm/issues/13757).
200
+
201
+ - pnpm now reads the workspace directory override from `PNPM_CONFIG_WORKSPACE_DIR`, like other settings. `NPM_CONFIG_WORKSPACE_DIR` still works as a fallback [#16275](https://github.com/pnpm/pnpm/issues/16275).
202
+
203
+ - pnpm now fails with `ERR_PNPM_AUTH_INVALID_BASE64` when a registry's `_password` in `.npmrc` is not valid base64. Before, it sent the value as the raw password. A `username` or `_password` left empty, for example by an unset environment variable, now supplies no credential [#16273](https://github.com/pnpm/pnpm/issues/16273).
204
+
205
+ - `proxy=false` now turns proxying off even when `HTTP_PROXY`, `HTTPS_PROXY`, or `ALL_PROXY` is set. pnpm no longer sends requests through a proxy named only in `ALL_PROXY`.
206
+
207
+ - `pnpm install` now runs the install hooks of a config dependency plugin's pnpmfile, including `readPackage`, `afterAllResolved`, and custom resolvers. Its pnpmfile is also counted in `pnpmfileChecksum`. Before, only the plugin's `updateConfig` hook ran, so a plugin could not change the resolved dependencies.
208
+
209
+ - A pnpmfile `fetchers` hook now runs once per package on a fresh install when it handles a resolution with a custom `type` or delegates a git-hosted one to the same subdirectory [#15584](https://github.com/pnpm/pnpm/issues/15584). These packages were fetched a second time for installation, so the installed files could come from a different archive than the one their dependencies were read from. The hook also no longer runs twice when a `resolvers` hook returns a tarball resolution without a manifest [#15025](https://github.com/pnpm/pnpm/issues/15025).
210
+
211
+ - `pnpm install` now re-fetches a package from a custom resolver when the `integrity` of its resolution changes, with or without `enableGlobalVirtualStore`. It used to update the lockfile but keep the old files in `node_modules` [#15670](https://github.com/pnpm/pnpm/issues/15670).
212
+
213
+ - `pnpm install` now rejects invalid results from a `readPackage` hook. A hook that returns a non-object value fails with `ERR_PNPM_BAD_READ_PACKAGE_HOOK_RESULT` [#15730](https://github.com/pnpm/pnpm/issues/15730). A hook that sets a dependency range to a value other than a string, such as `undefined`, fails with an error that names the dependency, the package and the pnpmfile. Delete the property to remove a dependency [#15705](https://github.com/pnpm/pnpm/issues/15705).
214
+
215
+ #### Global packages, pnpm versions, and runtimes
216
+
217
+ - `pnpm update --global` now reinstalls the global packages that pnpm 10 installed into the previous global directory, `<global-dir>/5`, so their commands are linked into the pnpm home `bin` directory again and `pnpm list --global` lists them. Once every package is migrated, pnpm deletes the previous directory and the commands pnpm 10 linked into the pnpm home [#11528](https://github.com/pnpm/pnpm/issues/11528).
218
+
219
+ - A signal sent to pnpm, such as `SIGTERM`, now reaches the pnpm that pnpm switches to because of `packageManager` or `devEngines.packageManager`, and the one that `pnpm with` runs. The signal used to be dropped, so scripts running under that pnpm never got to shut down [#9948](https://github.com/pnpm/pnpm/issues/9948).
220
+
221
+ - On arm64 musl Linux, such as Alpine on ARM, switching to a pinned pnpm older than 12 now runs the JavaScript `pnpm` package. The standalone executable of those versions crashed at startup on that platform [#10443](https://github.com/pnpm/pnpm/issues/10443).
222
+
223
+ - Global shims such as `node` now work when pnpm runs through a relative symlink, as with a Homebrew install. They were copies of that symlink and did not resolve from the global bin directory [#15691](https://github.com/pnpm/pnpm/issues/15691).
224
+
225
+ - `pnpm env remove --global` deletes Node.js versions that pnpm installed into its own store, including when another tool installed pnpm [#8357](https://github.com/pnpm/pnpm/issues/8357).
226
+
227
+ - `pnpm self-update` no longer suggests a downgrade when `minimumReleaseAge` holds back the registry's `latest` release. It now says that release is still within the cutoff [#12006](https://github.com/pnpm/pnpm/issues/12006).
228
+
229
+ #### Windows
230
+
231
+ - Interrupting a script with Ctrl+C on Windows no longer leaves the terminal stuck [#14860](https://github.com/pnpm/pnpm/issues/14860). A script that runs through a batch shim, as `vite dev` does through `vite.CMD`, made cmd.exe wait forever on its "Terminate batch job (Y/N)?" answer, and every following keystroke went to that prompt. pnpm now ends a cmd.exe script shell once it has sat for a second after the interrupt with nothing running under it. A script that takes longer to shut down is still waited for. A second Ctrl+C ends the script's shell at once.
232
+
233
+ - On Windows, `pnpm run` now passes the arguments after the script name to the script as typed. Before, `cmd` expanded `%VAR%` in them and backslashes arrived doubled. Line breaks still arrive as the two characters `\n`, because `cmd` cannot pass them. The command line pnpm prints for the script quotes the arguments the same way on every platform [#16257](https://github.com/pnpm/pnpm/issues/16257).
234
+
235
+ - The Windows `pnpm.exe` runs on a clean Windows install that does not have the Visual C++ Redistributable. It used to exit immediately on startup because that runtime was missing [#15723](https://github.com/pnpm/pnpm/issues/15723).
236
+
237
+ - On Windows, the `.cmd` command shims in `node_modules/.bin` now keep a `%` in the project path. Before, cmd.exe expanded it as a variable reference, so the command received a mangled `NODE_PATH` [#15716](https://github.com/pnpm/pnpm/issues/15716). Command shims also run tools whose paths contain non-ASCII characters [#6999](https://github.com/pnpm/pnpm/issues/6999), including the PowerShell shims in Windows PowerShell 5.1 [#16217](https://github.com/pnpm/pnpm/issues/16217).
238
+
239
+ - Bin shims in `node_modules/.bin` run from Cygwin on Windows again. The shims passed a `/cygdrive/c/...` path to the Windows `node` found on `PATH`, so Node.js failed with `Cannot find module 'C:\cygdrive\c\...'` [#12845](https://github.com/pnpm/pnpm/issues/12845).
240
+
241
+ - On Windows, installing pnpm with npm inside a project now writes `node_modules/.bin` shims that run `pnpm.exe`. A global install with `npm install --location=global` now gets the same shims as `npm install -g` [#15688](https://github.com/pnpm/pnpm/issues/15688).
242
+
243
+ - `pnpm install` no longer fails with `ERR_PNPM_WORKSPACE_INVALID_GLOB` on Windows for a wildcard pattern such as `plugins/*/*` in `pnpm-workspace.yaml` when the workspace is on a different drive than the pnpm cache or state directory [#16239](https://github.com/pnpm/pnpm/issues/16239).
244
+
245
+ - On Windows, `pnpm install` no longer skips a dependency's build script on a later install when the package ships an executable file and the script changes nothing inside the package directory [#15667](https://github.com/pnpm/pnpm/issues/15667).
246
+
247
+ - `pnpm setup` no longer writes the `pn.ps1`, `pnpx.ps1`, and `pnx.ps1` PowerShell wrappers. It also removes the ones an earlier setup wrote. PowerShell now runs `pn`, `pnpx`, and `pnx` through their `.cmd` wrappers, like `pnpm` itself. Before, these aliases failed with a "not digitally signed" error wherever the execution policy blocks unsigned scripts [#8444](https://github.com/pnpm/pnpm/issues/8444).
248
+
249
+ - `pnpm setup` on Windows no longer panics when an unrelated environment variable has a name containing a non-ASCII character. It skips that variable [#15684](https://github.com/pnpm/pnpm/issues/15684).
250
+
251
+ - On Windows, `pnpm setup` repairs the `PNPM_HOME` registry type left by older pnpm versions, even when the configured directory has not changed.
252
+
253
+ - On Windows, the `ERR_PNPM_BAD_ENV_FOUND` error of `pnpm setup` now shows the value `PNPM_HOME` is currently set to. Before, it showed the directory pnpm wanted to set.
254
+
255
+ - On Windows, pnpm expands nested `%VAR%` references in `PNPM_HOME` and the other directory environment variables it uses for its home, store, cache, state, and config directories. pnpm fails with an error when a `%VAR%` reference remains after expansion [#13236](https://github.com/pnpm/pnpm/issues/13236).
256
+
257
+ - On Windows, if the global bin directory is not in `PATH` and a `PATH` entry still contains an unexpanded variable such as `%PNPM_HOME%`, the error now names that entry. A variable referenced from the user `Path` must be set to a full path and stored as a plain string (`REG_SZ`) for the entry to expand [#5283](https://github.com/pnpm/pnpm/issues/5283).
258
+
259
+ #### Inspecting dependencies
260
+
261
+ - `pnpm audit` and `pnpm audit signatures` now fail with an error when the lockfile contains unresolvable dependency references [#13638](https://github.com/pnpm/pnpm/issues/13638).
262
+
263
+ - `pnpm licenses list` now reports the actual on-disk package locations when using `nodeLinker: hoisted` or `shamefully-hoist: true` [#8589](https://github.com/pnpm/pnpm/issues/8589). With `--json`, its `paths` array now includes every installed copy of a package, including hoisted copies and isolated installations with different peer dependencies.
264
+
265
+ - `pnpm root` now prints the configured `modulesDir`. It used to print `node_modules` regardless of the setting. A project's own `modulesDir` from `packageConfigs` is printed too [#9113](https://github.com/pnpm/pnpm/issues/9113).
266
+
267
+ #### Output and messages
268
+
269
+ - With the default and append-only reporters, installs with `--loglevel warn` or `--loglevel error` now print the full output of a failed install script. The output of successful scripts, including the root project's own install hooks, stays hidden. With `--loglevel warn`, pnpm also prints ignored build script warnings.
270
+
271
+ - When a dependency fails to resolve, the error now shows the cause. For example, a Node.js runtime download behind a proxy that re-signs TLS now reports `invalid peer certificate: UnknownIssuer` [#9556](https://github.com/pnpm/pnpm/issues/9556).
272
+
273
+ - When installing a git dependency over SSH fails with `Permission denied (publickey)`, pnpm suggests checking the loaded keys with `ssh-add -l`. Resolving an SSH URL that refuses the key also shows a local HTTPS rewrite that leaves the recorded URL alone [#13743](https://github.com/pnpm/pnpm/issues/13743).
274
+
275
+ - Lockfile verification now fails with `ERR_PNPM_TARBALL_URL_MISMATCH`, `ERR_PNPM_TARBALL_REVISION_MISMATCH`, or `ERR_PNPM_MISSING_NAMED_REGISTRY` when every rejected entry failed that check. These failures were reported as the generic `ERR_PNPM_LOCKFILE_RESOLUTION_VERIFICATION`.
276
+
277
+ - The lockfile verification error now suggests relaxing the policy that flagged an entry only if a fresh resolution still fails and you trust the affected packages. Errors from checks that no policy controls, such as a missing tarball integrity, no longer suggest relaxing a policy [#14411](https://github.com/pnpm/pnpm/issues/14411).
278
+
279
+ - `pnpm install` no longer prints an extra `Progress:` line after the progress line is marked `done` [#16184](https://github.com/pnpm/pnpm/issues/16184).
280
+
3
281
  ## 12.7.0
4
282
 
5
283
  pnpm 12.7.0 ships with `.nvmrc` and `.node-version` support in the global `node` shim, `pnpm install --allow-build`, `pnpm publish --publish-wait-timeout`, and `pnpm-workspace.yaml` created from the `workspaces` field. `pnpm install --force` no longer installs optional dependencies built for other platforms. This release also carries security fixes for bin shims on Nix, for lifecycle scripts of packages in a `storeDir` inside the workspace, and for `userAgent` placeholders in `pnpm-workspace.yaml`.
@@ -7,6 +7,24 @@
7
7
  # path instead. A dependency's bin cannot stand in for one of them and take over
8
8
  # the shim before it reaches its target. Directories come from `${link%/*}`,
9
9
  # which needs no helper at all.
10
+ #
11
+ # Where no default path is compiled in, as on Nix, `command -p` searches PATH
12
+ # instead, so the helpers run with node_modules and relative entries dropped from
13
+ # PATH.
14
+ caller_path_set=${PATH+set}
15
+ caller_path=${PATH-}
16
+ helper_path=
17
+ rest=$caller_path:
18
+ while [ -n "$rest" ]; do
19
+ dir=${rest%%:*}
20
+ rest=${rest#*:}
21
+ case "$dir" in
22
+ */node_modules/*|*/node_modules) ;;
23
+ /*) helper_path=${helper_path:+$helper_path:}$dir ;;
24
+ esac
25
+ done
26
+ # An empty PATH searches the current directory.
27
+ PATH=${helper_path:-/}
10
28
  link="$0"
11
29
  # `${link%/*}` needs a separator to strip. A bare name came from a PATH lookup
12
30
  # and stands for a file in the current directory.
@@ -53,6 +71,7 @@ case `command -p uname -a` in
53
71
  fi
54
72
  ;;
55
73
  esac
74
+ if [ -n "$caller_path_set" ]; then PATH=$caller_path; else unset PATH; fi
56
75
 
57
76
  basedir_abs=$(CDPATH= cd -P -- "$basedir" && pwd -P) || exit $?
58
77
  if [ -n "$exe" ] && [ -x "$basedir/node.exe" ]; then
@@ -7,6 +7,24 @@
7
7
  # path instead. A dependency's bin cannot stand in for one of them and take over
8
8
  # the shim before it reaches its target. Directories come from `${link%/*}`,
9
9
  # which needs no helper at all.
10
+ #
11
+ # Where no default path is compiled in, as on Nix, `command -p` searches PATH
12
+ # instead, so the helpers run with node_modules and relative entries dropped from
13
+ # PATH.
14
+ caller_path_set=${PATH+set}
15
+ caller_path=${PATH-}
16
+ helper_path=
17
+ rest=$caller_path:
18
+ while [ -n "$rest" ]; do
19
+ dir=${rest%%:*}
20
+ rest=${rest#*:}
21
+ case "$dir" in
22
+ */node_modules/*|*/node_modules) ;;
23
+ /*) helper_path=${helper_path:+$helper_path:}$dir ;;
24
+ esac
25
+ done
26
+ # An empty PATH searches the current directory.
27
+ PATH=${helper_path:-/}
10
28
  link="$0"
11
29
  # `${link%/*}` needs a separator to strip. A bare name came from a PATH lookup
12
30
  # and stands for a file in the current directory.
@@ -53,6 +71,7 @@ case `command -p uname -a` in
53
71
  fi
54
72
  ;;
55
73
  esac
74
+ if [ -n "$caller_path_set" ]; then PATH=$caller_path; else unset PATH; fi
56
75
 
57
76
  basedir_abs=$(CDPATH= cd -P -- "$basedir" && pwd -P) || exit $?
58
77
  if [ -n "$exe" ] && [ -x "$basedir/node.exe" ]; then
@@ -7,6 +7,24 @@
7
7
  # path instead. A dependency's bin cannot stand in for one of them and take over
8
8
  # the shim before it reaches its target. Directories come from `${link%/*}`,
9
9
  # which needs no helper at all.
10
+ #
11
+ # Where no default path is compiled in, as on Nix, `command -p` searches PATH
12
+ # instead, so the helpers run with node_modules and relative entries dropped from
13
+ # PATH.
14
+ caller_path_set=${PATH+set}
15
+ caller_path=${PATH-}
16
+ helper_path=
17
+ rest=$caller_path:
18
+ while [ -n "$rest" ]; do
19
+ dir=${rest%%:*}
20
+ rest=${rest#*:}
21
+ case "$dir" in
22
+ */node_modules/*|*/node_modules) ;;
23
+ /*) helper_path=${helper_path:+$helper_path:}$dir ;;
24
+ esac
25
+ done
26
+ # An empty PATH searches the current directory.
27
+ PATH=${helper_path:-/}
10
28
  link="$0"
11
29
  # `${link%/*}` needs a separator to strip. A bare name came from a PATH lookup
12
30
  # and stands for a file in the current directory.
@@ -53,6 +71,7 @@ case `command -p uname -a` in
53
71
  fi
54
72
  ;;
55
73
  esac
74
+ if [ -n "$caller_path_set" ]; then PATH=$caller_path; else unset PATH; fi
56
75
 
57
76
  basedir_abs=$(CDPATH= cd -P -- "$basedir" && pwd -P) || exit $?
58
77
  if [ -n "$exe" ] && [ -x "$basedir/node.exe" ]; then
@@ -7,6 +7,24 @@
7
7
  # path instead. A dependency's bin cannot stand in for one of them and take over
8
8
  # the shim before it reaches its target. Directories come from `${link%/*}`,
9
9
  # which needs no helper at all.
10
+ #
11
+ # Where no default path is compiled in, as on Nix, `command -p` searches PATH
12
+ # instead, so the helpers run with node_modules and relative entries dropped from
13
+ # PATH.
14
+ caller_path_set=${PATH+set}
15
+ caller_path=${PATH-}
16
+ helper_path=
17
+ rest=$caller_path:
18
+ while [ -n "$rest" ]; do
19
+ dir=${rest%%:*}
20
+ rest=${rest#*:}
21
+ case "$dir" in
22
+ */node_modules/*|*/node_modules) ;;
23
+ /*) helper_path=${helper_path:+$helper_path:}$dir ;;
24
+ esac
25
+ done
26
+ # An empty PATH searches the current directory.
27
+ PATH=${helper_path:-/}
10
28
  link="$0"
11
29
  # `${link%/*}` needs a separator to strip. A bare name came from a PATH lookup
12
30
  # and stands for a file in the current directory.
@@ -53,6 +71,7 @@ case `command -p uname -a` in
53
71
  fi
54
72
  ;;
55
73
  esac
74
+ if [ -n "$caller_path_set" ]; then PATH=$caller_path; else unset PATH; fi
56
75
 
57
76
  basedir_abs=$(CDPATH= cd -P -- "$basedir" && pwd -P) || exit $?
58
77
  if [ -n "$exe" ] && [ -x "$basedir/node.exe" ]; then
@@ -7,6 +7,24 @@
7
7
  # path instead. A dependency's bin cannot stand in for one of them and take over
8
8
  # the shim before it reaches its target. Directories come from `${link%/*}`,
9
9
  # which needs no helper at all.
10
+ #
11
+ # Where no default path is compiled in, as on Nix, `command -p` searches PATH
12
+ # instead, so the helpers run with node_modules and relative entries dropped from
13
+ # PATH.
14
+ caller_path_set=${PATH+set}
15
+ caller_path=${PATH-}
16
+ helper_path=
17
+ rest=$caller_path:
18
+ while [ -n "$rest" ]; do
19
+ dir=${rest%%:*}
20
+ rest=${rest#*:}
21
+ case "$dir" in
22
+ */node_modules/*|*/node_modules) ;;
23
+ /*) helper_path=${helper_path:+$helper_path:}$dir ;;
24
+ esac
25
+ done
26
+ # An empty PATH searches the current directory.
27
+ PATH=${helper_path:-/}
10
28
  link="$0"
11
29
  # `${link%/*}` needs a separator to strip. A bare name came from a PATH lookup
12
30
  # and stands for a file in the current directory.
@@ -53,6 +71,7 @@ case `command -p uname -a` in
53
71
  fi
54
72
  ;;
55
73
  esac
74
+ if [ -n "$caller_path_set" ]; then PATH=$caller_path; else unset PATH; fi
56
75
 
57
76
  basedir_abs=$(CDPATH= cd -P -- "$basedir" && pwd -P) || exit $?
58
77
  if [ -n "$exe" ] && [ -x "$basedir/node.exe" ]; then
@@ -1,9 +1,9 @@
1
1
  {
2
- "lastValidatedTimestamp": 1790330643831,
2
+ "lastValidatedTimestamp": 1790578965103,
3
3
  "projects": {
4
4
  "/home/runner/_work/pnpm/pnpm/pnpm/npm/pnpm/temp-deploy": {
5
5
  "name": "pacquet",
6
- "version": "12.7.0"
6
+ "version": "12.8.0"
7
7
  }
8
8
  },
9
9
  "pnpmfiles": [],
@@ -34,14 +34,11 @@
34
34
  "@openpgp/web-stream-tools": "0.3.1",
35
35
  "@pnpm/byline": "^1.0.0",
36
36
  "@pnpm/colorize-semver-diff": "^2.0.0",
37
- "@pnpm/config.env-replace": "^4.1.0",
38
37
  "@pnpm/exec": "^4.0.0",
39
- "@pnpm/log.group": "4.0.1",
40
38
  "@pnpm/logger": "^1100.0.0",
41
39
  "@pnpm/meta-updater": "2.0.6",
42
40
  "@pnpm/nopt": "^0.3.1",
43
41
  "@pnpm/npm-package-arg": "^2.0.0",
44
- "@pnpm/os.env.path-extender": "^3.0.1",
45
42
  "@pnpm/patch-package": "0.0.1",
46
43
  "@pnpm/semver-diff": "^2.0.0",
47
44
  "@pnpm/tabtab": "^0.5.4",
@@ -86,6 +83,7 @@
86
83
  "@types/which": "^3.0.4",
87
84
  "@types/write-file-atomic": "^4.0.3",
88
85
  "@types/yarnpkg__lockfile": "^1.1.9",
86
+ "@types/yauzl": "^3.4.0",
89
87
  "@types/zkochan__table": "npm:@types/table@6.3.2",
90
88
  "@typescript-eslint/utils": "^8.70.1",
91
89
  "@typescript/native-preview": "7.0.0-dev.20260707.2",
@@ -100,6 +98,7 @@
100
98
  "@zkochan/rimraf": "^4.0.0",
101
99
  "@zkochan/table": "^2.0.1",
102
100
  "adm-zip": "^0.6.1",
101
+ "agentkeepalive": "^4.6.0",
103
102
  "amaro": "^1.2.1",
104
103
  "ansi-diff": "^1.2.0",
105
104
  "archy": "^1.0.0",
@@ -150,6 +149,8 @@
150
149
  "graceful-fs": "^4.2.11",
151
150
  "graph-cycles": "3.0.0",
152
151
  "hosted-git-info": "npm:@pnpm/hosted-git-info@1.0.0",
152
+ "http-proxy-agent": "^7.0.2",
153
+ "https-proxy-agent": "^7.0.6",
153
154
  "https-proxy-server-express": "0.1.2",
154
155
  "human-id": "^4.2.1",
155
156
  "husky": "^9.1.7",
@@ -201,10 +202,11 @@
201
202
  "path-name": "^1.0.0",
202
203
  "path-temp": "^3.0.0",
203
204
  "pidtree": "^1.0.0",
204
- "preferred-pm": "^5.0.0",
205
+ "preferred-pm": "^5.0.1",
205
206
  "pretty-bytes": "^7.1.3",
206
207
  "pretty-ms": "^9.3.1",
207
208
  "promise-share": "^2.0.1",
209
+ "proxy": "^4.1.0",
208
210
  "proxyquire": "^2.1.3",
209
211
  "ps-list": "^9.0.0",
210
212
  "qrcode-terminal": "^0.12.0",
@@ -231,6 +233,7 @@
231
233
  "shx": "^0.4.0",
232
234
  "signal-exit": "^4.1.0",
233
235
  "socks": "^2.8.10",
236
+ "socks-proxy-agent": "^8.0.5",
234
237
  "sort-keys": "^6.0.1",
235
238
  "spdx-expression-parse": "5.0.0",
236
239
  "split-cmd": "^1.1.0",
@@ -266,7 +269,8 @@
266
269
  "write-package": "7.2.0",
267
270
  "write-yaml-file": "^6.0.0",
268
271
  "yaml": "^2.9.1",
269
- "yaml-tag": "1.1.0"
272
+ "yaml-tag": "1.1.0",
273
+ "yauzl": "^3.4.0"
270
274
  }
271
275
  },
272
276
  "dedupeDirectDeps": false,
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/install.js CHANGED
@@ -5,7 +5,10 @@
5
5
  // stay shebang-less because pnpm 11 records its interpreter before installing
6
6
  // the native binary at the same path. npm's global Windows shims still target
7
7
  // the extensionless path after the `bin` rewrite, so postinstall asks npm to
8
- // regenerate them against `pnpm.exe`. When lifecycle scripts are blocked
8
+ // regenerate them against `pnpm.exe`. A global install uses `npm rebuild
9
+ // --global`. A project install passes the project prefix, because the
10
+ // script's working directory is the installed package. Any other install,
11
+ // such as `npm exec`, keeps its shims. When lifecycle scripts are blocked
9
12
  // (`--ignore-scripts`, pnpm/Bun default), the placeholder remains and runs pnpm
10
13
  // through Node.js wherever a shell reaches it (see the `pnpm` file).
11
14
  //
@@ -125,7 +128,6 @@ function relinkNpmWindowsShims () {
125
128
  const npmExecPath = process.env.npm_execpath
126
129
  if (
127
130
  process.platform !== 'win32' ||
128
- process.env.npm_config_global !== 'true' ||
129
131
  npmExecPath == null ||
130
132
  path.basename(npmExecPath).toLowerCase() !== 'npm-cli.js'
131
133
  ) {
@@ -136,13 +138,21 @@ function relinkNpmWindowsShims () {
136
138
  if (typeof packageName !== 'string') {
137
139
  fail('Could not determine the pnpm wrapper package name when regenerating npm shims.')
138
140
  }
139
- const result = spawnSync(process.execPath, [
141
+ const args = [
140
142
  npmExecPath,
141
143
  'rebuild',
142
- '--global',
143
144
  '--ignore-scripts',
144
- packageName,
145
- ], { stdio: 'inherit' })
145
+ ]
146
+ if (process.env.npm_config_global === 'true' || process.env.npm_config_location === 'global') {
147
+ args.push('--global', packageName)
148
+ } else {
149
+ const prefix = findNpmProjectPrefix()
150
+ if (prefix == null) {
151
+ return
152
+ }
153
+ args.push('--prefix', prefix, packageName)
154
+ }
155
+ const result = spawnSync(process.execPath, args, { stdio: 'inherit' })
146
156
  if (result.error != null) {
147
157
  fail(`Could not regenerate the npm shims for pnpm: ${result.error.message}`)
148
158
  }
@@ -151,6 +161,36 @@ function relinkNpmWindowsShims () {
151
161
  }
152
162
  }
153
163
 
164
+ /**
165
+ * The resolved path of the npm project whose `node_modules` holds this
166
+ * wrapper. Returns `null` when npm names no project, when its `node_modules`
167
+ * cannot be resolved, or when it does not contain the wrapper. `npm exec`
168
+ * installs into its own cache while the prefix still names the caller's
169
+ * project, and rebuilding there would touch an unrelated project.
170
+ *
171
+ * @returns {string | null}
172
+ */
173
+ function findNpmProjectPrefix () {
174
+ const prefix = process.env.npm_config_local_prefix
175
+ if (typeof prefix !== 'string' || prefix === '') {
176
+ return null
177
+ }
178
+ let realPrefix
179
+ let realModulesDir
180
+ try {
181
+ realPrefix = fs.realpathSync(prefix)
182
+ realModulesDir = fs.realpathSync(path.join(realPrefix, 'node_modules'))
183
+ } catch {
184
+ return null
185
+ }
186
+ // `wrapperDir` comes from the module URL, which Node resolves through symlinks.
187
+ const relative = path.relative(realModulesDir, wrapperDir)
188
+ if (relative === '' || relative.split(path.sep)[0] === '..' || path.isAbsolute(relative)) {
189
+ return null
190
+ }
191
+ return realPrefix
192
+ }
193
+
154
194
  function removeFileIfPossible (filePath) {
155
195
  try {
156
196
  fs.rmSync(filePath, { force: true })
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "./dist/node_modules/node-gyp/gyp/gyp"
10
10
  ]
11
11
  },
12
- "version": "12.7.0",
12
+ "version": "12.8.0",
13
13
  "description": "Fast, disk space efficient package manager",
14
14
  "keywords": [
15
15
  "pnpm",
@@ -46,20 +46,20 @@
46
46
  "THIRD-PARTY-NOTICES.md"
47
47
  ],
48
48
  "optionalDependencies": {
49
- "@pnpm/exe.win32-x64": "12.7.0",
50
- "@pnpm/exe.win32-arm64": "12.7.0",
51
- "@pnpm/exe.darwin-x64": "12.7.0",
52
- "@pnpm/exe.darwin-arm64": "12.7.0",
53
- "@pnpm/exe.linux-x64": "12.7.0",
54
- "@pnpm/exe.linux-arm64": "12.7.0",
55
- "@pnpm/exe.linux-riscv64": "12.7.0",
56
- "@pnpm/exe.linux-ppc64": "12.7.0",
57
- "@pnpm/exe.linux-s390x": "12.7.0",
58
- "@pnpm/exe.linux-x64-musl": "12.7.0",
59
- "@pnpm/exe.linux-arm64-musl": "12.7.0",
60
- "@pnpm/exe.freebsd-x64": "12.7.0",
61
- "@pnpm/exe.android-arm64": "12.7.0",
62
- "@pnpm/exe.android-x64": "12.7.0"
49
+ "@pnpm/exe.win32-x64": "12.8.0",
50
+ "@pnpm/exe.win32-arm64": "12.8.0",
51
+ "@pnpm/exe.darwin-x64": "12.8.0",
52
+ "@pnpm/exe.darwin-arm64": "12.8.0",
53
+ "@pnpm/exe.linux-x64": "12.8.0",
54
+ "@pnpm/exe.linux-arm64": "12.8.0",
55
+ "@pnpm/exe.linux-riscv64": "12.8.0",
56
+ "@pnpm/exe.linux-ppc64": "12.8.0",
57
+ "@pnpm/exe.linux-s390x": "12.8.0",
58
+ "@pnpm/exe.linux-x64-musl": "12.8.0",
59
+ "@pnpm/exe.linux-arm64-musl": "12.8.0",
60
+ "@pnpm/exe.freebsd-x64": "12.8.0",
61
+ "@pnpm/exe.android-arm64": "12.8.0",
62
+ "@pnpm/exe.android-x64": "12.8.0"
63
63
  },
64
64
  "scripts": {
65
65
  "preinstall": "node install.js",