-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Comparing changes
Open a pull request
base repository: pnpm/pnpm
base: v11.2.0
head repository: pnpm/pnpm
compare: v11.2.1
- 6 commits
- 119 files changed
- 1 contributor
Commits on May 20, 2026
-
feat(pacquet): wire NpmResolver into install; fix(pick-registry) unsc…
…oped npm-alias routing (#11760) Two changes ship together: the bulk is the pacquet refactor described in #11756, plus a TypeScript-side fix to `@pnpm/config.pick-registry-for-package` that surfaced during review. ### Pacquet — wire NpmResolver into install (Phases A/B/C of #11756) - **Phase A.** New `parse_bare_specifier.rs` and `npm_resolver.rs` in `pacquet-resolving-npm-resolver`. `NpmResolver` implements the `Resolver` trait: parses the bare specifier (including npm-alias `npm:@scope/name@<spec>` and tarball-URL forms — with prefix-anchored name validation), picks a version via `pick_package`, surfaces `minimumReleaseAge` violations inline via `detect_min_release_age_violation`. `workspace:` specs decline so the chain falls through. `published_by` / `published_by_exclude` / `dry_run` added to `ResolveOptions`. - **Phase B.** `install_without_lockfile.rs` constructs an `NpmResolver` at install entry from the config-derived registries map and an `InMemoryPackageMetaCache` that's shared across the resolve pass and dropped before the install pass. - **Phase C.** New `pacquet-resolving-deps-resolver` crate exposes `resolve_dependency_tree`: a flat `name@version`-keyed package map with parent-child edges, concurrent sibling resolution via `try_join_all`, per-id dedup gate. `install_package_from_registry.rs` no longer calls `Package::fetch_from_registry` / `Package::pinned_version`; it takes a pre-resolved `ResolveResult` and reads tarball URL + integrity off `LockfileResolution::Tarball`. Additional behaviors landed during review: - **`minimumReleaseAge` policy in the resolve pass.** Previously only enforced by the lockfile-verification gate; the no-lockfile resolve pass now derives `published_by` and the exclude policy from `Config` so resolver-time picks match the configured policy. - **`SPEC_NOT_SUPPORTED_BY_ANY_RESOLVER` surfaces correctly.** `resolve_dependency_tree` now returns a typed error when the chain returns `Ok(None)` — silently dropping the edge would leave installs missing transitive deps. Mirrors upstream's `default-resolver` error shape. - **Per-package progress events.** `InstallPackageFromRegistry` takes a `first_visit: bool`; `pnpm:progress resolved` / `pnpm:progress imported` plus the tarball download fire once per `(name, version)`, while the per-parent `symlink_package` runs on every edge. Matches upstream's per-package (not per-edge) reporter contract. - **Windows symlink race fix.** `ResolvedPackages` is now `DashMap<String, watch::Sender<bool>>`; the first writer signals completion after `import_indexed_dir`, so a second visitor's `symlink_package` (which may fall back to a Windows junction requiring an existing target) doesn't race ahead of the materialization. A dropped first-writer task surfaces as a typed `FirstWriterAborted` error. - **Scope routing.** `pick_registry_for_package` is now bareSpecifier-aware so an entry like `"foo": "npm:@acme/bar@^1"` routes through `registries[@acme]`. ### TS — `@pnpm/config.pick-registry-for-package` unscoped-target fix A separate bug surfaced during the scope-routing port: `pickRegistryForPackage('@private/foo', 'npm:lodash@^1')` was routing through `registries['@Private']`, even though `lodash` is unscoped and doesn't live on the `@private` registry. `getScope` now returns `null` in the npm-alias branch when the alias target is unscoped (instead of falling through to the local pkgName's scope). Changeset is in `.changeset/pick-registry-unscoped-npm-alias.md` (patch bump for `@pnpm/config.pick-registry-for-package` and `pnpm`). Added matching tests on both the TS and pacquet sides. ### Out of scope (left as #11756 follow-ups) - Preferred-versions harvesting from the lockfile (Phase D). - Install-side aggregation of `policy_violation` from the tree (Phase E) — the resolver attaches them per-pick already, but the install layer doesn't yet collect or fail on them. - Other-protocol resolvers (git, tarball, workspace, jsr, named-registry, …) — `NpmResolver` is the only chain entry today; once a second resolver lands, `DefaultResolver` will get wired in too. - Full `parseBareSpecifier.test.ts` corpus port — the parser tests pacquet ships cover the cases the install path exercises; remaining corpus items land alongside Phase F. Closes part of #11756.
Configuration menu - View commit details
-
Copy full SHA for 097983f - Browse repository at this point
Copy the full SHA 097983fView commit details -
test(pnpm): group release-brittle tests under a shared describe block (…
…#11767) Three tests resolve the running pnpm version's integrity from registry-mock, which proxies pnpm to npmjs. They fail every release between the version bump commit and the matching npm publish ("No matching version found for pnpm@<version>"), then pass again once the version lands on npmjs. Group them under a 'release-brittle' describe in each file so the failure mode is obvious from the test name and only stated once.
Configuration menu - View commit details
-
Copy full SHA for ef87f3c - Browse repository at this point
Copy the full SHA ef87f3cView commit details -
fix(env-installer): suppress 'Installing config dependencies...' on n…
…o-op installs (#11766) * fix(env-installer): only print "Installing config dependencies..." when work is actually being done Previously the message was emitted unconditionally for every config dependency, before any of the "do we need to fetch / re-symlink?" checks. As a result the banner printed on every install even when everything was already cached and correctly linked. Emit the started event lazily — at most once per install, and only when an orphan is being removed, a parent or subdep needs fetching, a parent symlink needs (re)creating, or orphan subdep siblings are being pruned. --- Written by an agent (Claude Code, claude-opus-4-7). * test(env-installer): assert installing-config-deps events fire only when work happens Captures `streamParser` events around `resolveAndInstallConfigDeps` to verify the lazy emission introduced in the previous commit: - fresh install emits both `started` and `done`, - a follow-up no-op install emits neither, - removing a config dep still emits `started` (orphan cleanup work). --- Written by an agent (Claude Code, claude-opus-4-7). * test(env-installer): subscribe to streamParser once at module load `streamParser` is a `split2` Transform stream that buffers writes until the first 'data' listener attaches and then drains the whole buffer into it. Subscribing per-test made the new install-config-deps test capture events from every earlier test in the file. Move the subscription to module load and have each test drain the accumulated events around its own call. Also drop the "removal" assertion: `resolveAndInstallConfigDeps` does not prune entries that disappear from the configDeps argument (lockfile pruning happens at a higher layer), so the scenario it claimed to test never actually fired the orphan-cleanup path. --- Written by an agent (Claude Code, claude-opus-4-7). * fix(env-installer): emit started when only the sibling symlink needs relinking If a config dep's optional subdep is already cached in the global virtual store but the sibling symlink under the parent's node_modules is missing or points at a stale target, symlinkDir() does real work without reportStarted ever firing. Check whether the link already points at the expected target and only fire reportStarted + symlinkDir when it doesn't, mirroring the parentSymlinkAlreadyCorrect path. Also clean up the test-level streamParser listener in afterAll so the subscription doesn't outlive the test file. --- Written by an agent (Claude Code, claude-opus-4-7).
Configuration menu - View commit details
-
Copy full SHA for e5e7b72 - Browse repository at this point
Copy the full SHA e5e7b72View commit details -
fix(pacquet): shorten long virtual store dirnames to avoid ENAMETOOLO…
…NG (#11768) * fix(pacquet): shorten long virtual store dirnames to avoid ENAMETOOLONG Peer-heavy snapshot keys (e.g. vitest with a dozen browser / coverage / DOM peers) produced flat-name directories that overflowed macOS's 255- byte filename limit, so `install` aborted with errno 63 before unpacking any tarballs. Port the trailing length / case-shortening branch of upstream's `depPathToFilename` (deps/path/src/index.ts:169) so the name becomes `<prefix>_<32-hex-sha256>` capped at `virtualStoreDirMaxLength` bytes (default 120). Extract `create_short_hash` and `shorten_virtual_store_name` into a new `pacquet-crypto-hash` crate mirroring upstream `@pnpm/crypto.hash`; `pacquet-lockfile`, `pacquet-registry`, and `pacquet-store-dir` all consume it instead of duplicating the sha2 + truncate logic. Reported via pnpm/pacquet issue triage (vitest@4.1.6 peer suffix). * fix(pacquet): taplo format and remove broken intra-doc link Format `pacquet/crates/crypto-hash/Cargo.toml` per the workspace `.taplo.toml` (aligns the `[package]` keys) and downgrade the `pacquet_modules_yaml::DEFAULT_VIRTUAL_STORE_DIR_MAX_LENGTH` reference in `PkgNameVerPeer::to_virtual_store_name` to plain text, since `pacquet-lockfile` deliberately does not depend on `pacquet-modules-yaml` and `RUSTDOCFLAGS=-D warnings` rejected the unresolved intra-doc link. * feat(pacquet/config): expose virtualStoreDirMaxLength Add `virtual_store_dir_max_length: u64` to `Config` with default 120 (matching `pacquet_modules_yaml::DEFAULT_VIRTUAL_STORE_DIR_MAX_LENGTH`). Wire it through `WorkspaceSettings.virtualStoreDirMaxLength` and the `PNPM_CONFIG_VIRTUAL_STORE_DIR_MAX_LENGTH` env-overlay so users can override the threshold via `pnpm-workspace.yaml`, global `config.yaml`, or environment variables — mirroring upstream `Config.virtualStoreDirMaxLength`. The three flat-name call sites (`install_without_lockfile.rs`, `install_package_from_registry.rs`, `virtual_store_layout.rs`) and the `.modules.yaml` writer now read the configured value instead of the hardcoded constant. `VirtualStoreLayout::legacy` takes the value as an explicit second arg so test fixtures don't silently inherit a default.
Configuration menu - View commit details
-
Copy full SHA for c068720 - Browse repository at this point
Copy the full SHA c068720View commit details -
fix(env-installer): mark optional config subdep snapshots with option…
…al: true (#11770) Match how optional packages are recorded elsewhere in pnpm-lock.yaml so non-host platform variants pulled in via a config dep's optionalDependencies aren't treated as required.
Configuration menu - View commit details
-
Copy full SHA for 2061c55 - Browse repository at this point
Copy the full SHA 2061c55View commit details -
Configuration menu - View commit details
-
Copy full SHA for 11a43b1 - Browse repository at this point
Copy the full SHA 11a43b1View commit details
This comparison is taking too long to generate.
Unfortunately it looks like we can’t render this comparison for you right now. It might be too big, or there might be something weird with your repository.
You can try running this command locally to see the comparison on your machine:
git diff v11.2.0...v11.2.1