Pass core.quotePath=false when patch-commit asks Git to diff the extracted and edited package directories. Otherwise Git C-quotes non-ASCII arguments in diff headers, preventing pnpm from normalizing and parsing its own generated patch.
Mirror the option and regression coverage in the TypeScript CLI and the Rust pnpm port.
Closespnpm/pnpm#13801.
Registries do not all lay out tarball URLs the way the npm registry does.
JFrog Artifactory repeats the scope in a scoped package's tarball filename
(`@acme/widget/-/@acme/widget-1.0.0.tgz`) where npm strips it. pnpm cannot
rebuild such a URL, so it writes it out for every scoped package instead of
omitting it, and the lockfile carries a host-specific URL per dependency.
pnpm had no place to record such a fact, because it had no place to
describe a registry at all — only three ways to name one. `registries`
mapped a scope to a URL, `namedRegistries` mapped a bare-specifier prefix
to a URL, and neither could carry anything else.
`registries` now declares a registry once, keyed by its URL, with every
fact about it in the entry: a `serverType`, the `scopes` routed to it,
and the `prefix` it answers to. `serverType` has three states:
undeclared strict; only the exact canonical URL is reconstructible
npm also serves the percent-encoded scoped path
artifactory repeats the scope in the tarball filename
registry.npmjs.org resolves to `npm` as a built-in, so its behavior is
unchanged and the old hostname check becomes that one default rather than a
special case in the predicate. `npm` cannot be the default: asserting
npmjs-compatibility is a claim only the operator can make.
The URL is the key because every fact in an entry is a fact about that
server. Keying the layout by scope would bind it to whoever the scope
currently points at, so two developers whose scope resolves differently
would write lockfiles that disagree about which URLs may be omitted.
`scopes` and `prefix` are routes to the registry and are inverted at
config-read time into the two lookups the rest of pnpm already queries,
leaving the resolver, installer, and lockfile layers untouched and the
precedence chain (builtin < .npmrc < yaml < `_auth` < CLI) unchanged.
The layout is declared, never inferred. Sniffing the registry URL cannot work:
a virtual repository serves both layouts at once, depending on whether each
package was synced from upstream or published locally, so no registry-level
signal — route, response header, or probe — decides it. Declaring it also
keeps a wrong guess a fixable misconfiguration instead of a silent breakage.
`serverType` feeds a single URL builder that both sides of the lockfile use:
the writer omits a tarball URL only when the builder reproduces it, and the
reader rebuilds it with the same call. They therefore agree by construction,
so pnpm never has to assume a registry serves some second URL as well.
The setting lives in pnpm-workspace.yaml rather than .npmrc because the
lockfile depends on it: one developer omitting URLs that another reconstructs
differently would break a frozen install. A `serverType` in the global
config.yaml is ignored for the same reason, while the routes declared
alongside it are kept. Credentials are rejected there — the file is
committed — and still belong in .npmrc. The registry URL is the map key, so
the request-destination env gate applies to keys as well as values.
Credentials and unknown fields are refused after parsing, since a parse
error renders the offending source line verbatim.
A map whose values are all strings is the older `<scope>: <url>` shape and
is still read as one. Mixing the two shapes in one map is refused, and so is
a URL-keyed entry written as a string. `namedRegistries` is deprecated in
favor of `prefix` and is read only for prefixes `registries` does not
declare; a prefix stays singular because it is the registry's identity in a
lockfile dep path.
An entry that routes nothing to itself and matches no configured registry is
reported as a warning rather than silently ignored; it is a warning and not
an error because a shared config dependency can legitimately describe
registries a given project does not use.
Config dependencies and pnpr-server-mode resolution pass no server type; in
both, the writer and the reader share that default, so they stay consistent.
Closespnpm/get-npm-tarball-url#16. Supersedes pnpm/pnpm#13920.
Resolution never reads a patch. It appends the patch file's hash to an
already-resolved package id, so a `patchedDependencies` change leaves the
set of packages and versions untouched and moves only the affected keys.
Rewrite those keys and the references pointing at them instead of
re-resolving.
`packages:` is keyed without the patch hash, so only `snapshots:`, the
importers' resolved versions, and dependents' dependency references move.
The patched tarball is already in the store, so materialization applies
the patch with no network access, and the new depPath puts the package in
a fresh virtual-store slot so an install script reruns against the
patched sources.
Fall back to the resolver when the patched package is reachable as a peer
(its depPath is embedded in dependents' peer suffixes), when a peer suffix
was shortened into a hash and cannot be inspected, when the new
configuration would leave a patch unused while `allowUnusedPatches` is
off, and when a patch file cannot be read.
Implemented in both the TypeScript CLI and pacquet.
Related to pnpm/pnpm#13474.
Remove the legacy repository changelog files now that release changelog storage defaults to the registry. The publish path composes and injects CHANGELOG.md into release tarballs, so keeping historical copies in source control duplicates generated release data.
Update adm-zip to the patched 0.6 release and override vulnerable transitive versions after the dependency audit began rejecting versions below 0.6.0.
The single git-hosted version branch of getPatchedDependency spread the options
object instead of the parsed dependency, so the result lost `alias` (the package
name) and carried unrelated option fields. Spread `dep`, matching the sibling
branches.
Co-authored-by: Claude <noreply@anthropic.com>
Some registries generate tarballs on demand and cannot list an integrity in
their packument. pnpm then wrote integrity-less lockfile entries on the first
install and failed the next one with ERR_PNPM_MISSING_TARBALL_INTEGRITY, unable
to install from its own lockfile.
Compute the missing integrity from the downloaded bytes and write it into the
resolution before the lockfile is built:
- Add an optional `resolutionNeedsFetch` contract to the fetcher API (backward
compatible, since custom fetchers come from hooks). The remote-tarball fetcher
reports it when a resolution lacks integrity; the picked fetcher's signal flows
through PackageResponse -> ResolvedPackage so nothing re-derives it.
- The package requester downloads such tarballs (including under --lockfile-only /
skipFetch / not-installable) and fills the computed integrity onto the resolution
via the already-running `fetching` promise, so dependency resolution isn't
blocked. The deps-resolver awaits only the flagged entries before updateLockfile,
because the integrity feeds the global virtual-store paths.
- Move read-side enforcement into the npm resolver's lockfile verifier
(MISSING_TARBALL_INTEGRITY): reject a registry/http(s) tarball entry whose
integrity is missing/empty/non-string, fail-closed, before the URL-keyed and
semver short-circuits. Drop the earlier read-side auto-heal (a missing-field
bypass). Harden against tampered lockfiles (non-string tarball/integrity).
- Reuse the fetcher picked during resolution on the fetch path instead of running
pickFetcher (and a custom fetcher's async canFetch) twice per package.
Mirrored in pacquet: PrefetchingResolver computes the integrity for integrity-less
tarball resolutions during resolution (FetchTarballForResolution::run), deduped per
URL with a singleflight cache.
Closespnpm/pnpm#12145.
---------
Co-authored-by: Zoltan Kochan <z@kochan.io>
The TypeScript pnpm CLI freezes at v11; pnpm 12 will be the Rust pacquet
port. To make that split legible, all TypeScript source, test, and build
directories move under a new top-level pnpm11/ directory. The name states
the version boundary rather than implying a behavioral fork, since the two
stacks are meant to behave identically.
Scope is source-only: the shared workspace root stays at the repo root.
pnpm-workspace.yaml, package.json, pnpm-lock.yaml, .pnpmfile.cjs,
.meta-updater, __patches__, .changeset, .husky, and the lint/spell configs
remain in place, so one pnpm workspace and one Cargo workspace still span
all three products. pnpr/client and pacquet/tasks/registry-mock stay as
cross-product workspace members.
Rewiring the move required:
- pnpm-workspace.yaml globs prefixed with pnpm11/
- root package.json script paths, eslint.config.mjs, tsconfig.lint.json,
.gitignore, and CODEOWNERS updated
- .meta-updater/src/index.ts literals repointed (pnpm11/pnpm/package.json,
pnpm11/__utils__, pnpm11/__typings__, and the main package directory)
- regenerated every moved package's repository/homepage URL via meta-updater
- pnpm11/pnpm/bundle-deps.ts and __utils__/scripts/src/typecheck-only.ts
climb one more level to reach the repo root
.meta-updater stays at the repo root because @pnpm/meta-updater resolves
its config at <cwd>/.meta-updater/main.mjs.
TS CI (.github/workflows/ci.yml) now only runs when pnpm11/-relevant paths
change, via a dorny/paths-filter changes job plus a TS CI / Success
aggregate gate; branch protection should require only that gate.