Skip to content

feat(pacquet): port named registries to the install chain#11785

Merged
zkochan merged 3 commits into
mainfrom
named-registries
May 20, 2026
Merged

feat(pacquet): port named registries to the install chain#11785
zkochan merged 3 commits into
mainfrom
named-registries

Conversation

@zkochan

@zkochan zkochan commented May 20, 2026

Copy link
Copy Markdown
Member

Summary

Ports the user-facing <alias>: resolver surface to pacquet so a manifest entry like "@acme/private": "gh:^1.0.0" resolves against GitHub Packages (or any other registry the user configures under namedRegistries: in pnpm-workspace.yaml).

Mirrors upstream b61e268d57 (#11324).

What's new in pacquet

  • Parser (parse_named_registry_specifier_to_registry_package_spec): handles <alias>:[@<owner>/]<name>[@<version>] and <alias>:<version> bodies. Rejects scope-without-name with ERR_PNPM_INVALID_NAMED_REGISTRY_PACKAGE_NAME.
  • Validator (merge_named_registries): folds the user map onto pacquet's built-in aliases (gh: → GitHub Packages) and validates each URL at resolver construction with ERR_PNPM_INVALID_NAMED_REGISTRY_URL.
  • Resolver (NamedRegistryResolver): third Resolver alongside the npm / jsr paths, emitting resolved_via: "named-registry". Auth headers flow through the existing per-URL .npmrc lookup, so a //npm.pkg.github.com/:_authToken=… entry takes effect for gh: specifiers automatically.
  • Config (Config::named_registries): reads namedRegistries: from pnpm-workspace.yaml, with ${VAR} substitution on the values (mirrors upstream's replaceEnvInStringValues).
  • Wiring: install_without_lockfile constructs the merged map once and threads it through both the resolver chain (after npm/git, so configured aliases cannot hijack built-in schemes) and build_resolution_verifiers (so the tarball-URL prefix routing honours user aliases).

Test plan

  • cargo nextest run -p pacquet-resolving-npm-resolver — 164 tests pass (12 new parser cases, 4 new merger cases, 5 new resolver integration tests with mockito)
  • cargo nextest run -p pacquet-config — 206 tests pass (2 new for the yaml field + env substitution)
  • cargo nextest run -p pacquet-package-manager — 265 tests pass
  • just lint — clean
  • cargo fmt --check — clean
  • Full workspace test run (just test) — 1708 / 1708 pass

Written by an agent (Claude Code, claude-opus-4-7).

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for named registry aliases in project configuration files, enabling users to define custom registry mappings with environment variable substitution.
  • Improvements

    • Enhanced registry configuration validation to verify URLs use valid HTTP(S) schemes, preventing misconfiguration errors.
  • Tests

    • Added comprehensive test coverage for named registry functionality and resolver chain behavior.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR implements end-to-end named-registry alias support: users define registry aliases in pnpm-workspace.yaml (namedRegistries: { gh: "https://api.github.com", custom: "https://registry.example.com" }), which are deserialized, environment-variable substituted, validated, merged with pnpm built-ins, and used as dependency specifiers (e.g., gh:@owner/package``). The resolver chain prioritizes explicit local schemes (link:, `file:`, `workspace:`) over same-named aliases.

Changes

Named Registry Alias Resolution

Layer / File(s) Summary
Configuration schema and deserialization
pacquet/crates/config/src/lib.rs, pacquet/crates/config/src/workspace_yaml.rs, pacquet/crates/config/src/workspace_yaml/tests.rs
Config gains named_registries: BTreeMap<String, String>; WorkspaceSettings adds named_registries deserialization from YAML camelCase key and an substitute_env method to expand ${VAR} placeholders. Config layering now calls substitute_env on global, workspace, and env-derived settings before applying them.
Registry alias validation and merging
pacquet/crates/resolving-npm-resolver/src/named_registry.rs, pacquet/crates/resolving-npm-resolver/src/named_registry/tests.rs
MergeNamedRegistriesError and merge_named_registries function validate user-provided URLs against http(s) scheme requirements and merge with pnpm's built-in aliases (e.g., gh → GitHub). User entries override built-ins on key collision; invalid URLs fail fast with detailed error context.
Named-registry specifier parsing
pacquet/crates/resolving-npm-resolver/src/parse_bare_specifier.rs, pacquet/crates/resolving-npm-resolver/src/parse_bare_specifier/tests.rs
New parse_named_registry_specifier_to_registry_package_spec parser recognizes <alias>:<body> patterns, validates against known aliases, extracts scoped/unscoped package names, and classifies version selectors. Non-matching inputs return Ok(None) to allow resolver fallthrough.
NamedRegistryResolver implementation
pacquet/crates/resolving-npm-resolver/src/named_registry_resolver.rs, pacquet/crates/resolving-npm-resolver/src/named_registry_resolver/tests.rs
New NamedRegistryResolver<Cache: PackageMetaCache> struct implements the Resolver trait by parsing named-registry specifiers, mapping aliases to registry URLs, fetching packuments, and picking versions. Includes resolve and resolve_latest with appropriate metadata tagging (resolved_via = "named-registry") and latest-version handling with minimum-release-age policy compliance.
Local resolver refactoring
pacquet/crates/resolving-local-resolver/src/chain.rs, pacquet/crates/resolving-local-resolver/src/lib.rs
Refactored LocalResolver into LocalSchemeResolver (for link: / file: / workspace:) and LocalPathResolver (for bare paths like ./foo), with shared helpers wanted_local and local_options. New resolvers are exported publicly and can be wired separately to ensure proper precedence in the resolver chain.
InstallWithoutLockfile integration
pacquet/crates/package-manager/src/install_without_lockfile.rs
Install flow validates and merges user named_registries via merge_named_registries, building a shared in-memory metadata cache for npm resolution. The DefaultResolver chain replaces the single LocalResolver with LocalSchemeResolver, NamedRegistryResolver, and LocalPathResolver in that order.
Public API exports and test wiring
pacquet/crates/resolving-npm-resolver/src/lib.rs, pacquet/crates/resolving-npm-resolver/Cargo.toml, pacquet/crates/package-manager/src/build_resolution_verifiers.rs, pacquet/crates/package-manager/src/install_package_from_registry/tests.rs
New resolver types and parsing helpers (NamedRegistryResolver, parse_named_registry_specifier_to_registry_package_spec, MergeNamedRegistriesError) are re-exported; dev-dependencies added for integration testing; build_resolution_verifiers threads user named_registries into npm verifier options; test fixtures initialize Config::named_registries with defaults.
Integration tests for resolver precedence
pacquet/crates/resolving-npm-resolver/tests/chain.rs
New test suite validates that explicit local-scheme dependencies (link:./pkg, workspace:./pkg, file:./pkg) resolve to local-filesystem and take precedence over same-named registry aliases, ensuring correct resolver chain ordering and fallthrough behavior across all three scheme types.

Sequence Diagram(s)

sequenceDiagram
  participant User as User Config
  participant ConfigLayer as Config Layer
  participant Parser as Specifier Parser
  participant Chain as DefaultResolver Chain
  participant Scheme as LocalSchemeResolver
  participant Named as NamedRegistryResolver
  participant Path as LocalPathResolver
  participant HTTP as HTTP Registry
  
  User->>ConfigLayer: namedRegistries: {gh: url, work: url}
  ConfigLayer->>ConfigLayer: substitute_env(), merge_named_registries()
  Parser->>Chain: WantedDependency(gh:`@owner/pkg`)
  Chain->>Scheme: resolve()
  Scheme-->>Chain: Ok(None)
  Chain->>Named: resolve()
  Named->>Parser: parse_named_registry_specifier()
  Parser-->>Named: NamedRegistryPackageSpec
  Named->>HTTP: GET /registry/@owner/pkg
  HTTP-->>Named: packument
  Named-->>Chain: ResolveResult(resolved_via=named-registry)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

  • pnpm/pnpm#11778: Refactors LocalResolver into LocalSchemeResolver and LocalPathResolver in the same file and is a prerequisite for the split resolver chain in this PR.
  • pnpm/pnpm#11730: Both PRs extend Config::current to call WorkspaceSettings::substitute_env::<Sys>() for environment-variable expansion in the configuration cascade.
  • pnpm/pnpm#11773: Both PRs modify the DefaultResolver chain in install_without_lockfile.rs, with this PR inserting NamedRegistryResolver and the split local resolvers.

Suggested labels

area: config dependencies, area: resolution

Poem

A rabbit hops through registries with glee, 🐰
Named aliases map each URL with care—
gh:@scope/pkg`` resolves through the air!
Local schemes win precedence fair,
And metadata caches shared everywhere.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: porting named registries support to the install chain in pacquet, which is the primary objective of this changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch named-registries

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Adds the user-facing `<alias>:` resolver surface so a manifest entry
like `"@acme/private": "gh:^1.0.0"` resolves against GitHub Packages
(or any other registry the user configures under
`namedRegistries:` in `pnpm-workspace.yaml`).

Mirrors upstream b61e268d57:

- `parse_named_registry_specifier_to_registry_package_spec` parses
  `<alias>:[@<owner>/]<name>[@<version>]` and `<alias>:<version>`
  bodies. Rejects scope-without-name with
  `ERR_PNPM_INVALID_NAMED_REGISTRY_PACKAGE_NAME`.
- `merge_named_registries` folds the user map onto pacquet's
  built-in aliases (`gh:` -> GitHub Packages) and validates URLs at
  resolver construction (`ERR_PNPM_INVALID_NAMED_REGISTRY_URL`).
- `NamedRegistryResolver` is a third `Resolver` alongside the npm /
  jsr paths, emitting `resolved_via: "named-registry"`. Auth headers
  flow through the existing per-URL `.npmrc` lookup.
- `Config::named_registries` reads `namedRegistries:` from
  `pnpm-workspace.yaml`, with `${VAR}` substitution on the values
  (matches upstream's `replaceEnvInStringValues`).
- `install_without_lockfile` constructs the merged map once and
  threads it through both the resolver chain (after npm/git, so
  configured aliases cannot hijack built-in schemes) and
  `build_resolution_verifiers` (so tarball-URL prefix routing
  honours user aliases).
@zkochan zkochan force-pushed the named-registries branch from 30791ce to 06dfa6f Compare May 20, 2026 22:15
@codecov-commenter

codecov-commenter commented May 20, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 75.98684% with 73 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.45%. Comparing base (a8a8cbc) to head (07485ff).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...olving-npm-resolver/src/named_registry_resolver.rs 64.35% 36 Missing ⚠️
...cquet/crates/resolving-local-resolver/src/chain.rs 51.42% 34 Missing ⚠️
...resolving-npm-resolver/src/parse_bare_specifier.rs 95.31% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #11785      +/-   ##
==========================================
- Coverage   89.69%   89.45%   -0.25%     
==========================================
  Files         174      175       +1     
  Lines       20781    21116     +335     
==========================================
+ Hits        18640    18889     +249     
- Misses       2141     2227      +86     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

Micro-Benchmark Results

Linux

group                          main                                   pr
-----                          ----                                   --
tarball/download_dependency    1.01      7.6±0.18ms   568.2 KB/sec    1.00      7.6±0.35ms   572.2 KB/sec

zkochan added 2 commits May 21, 2026 00:26
- `resolving-local-resolver/src/chain.rs`: drop the redundant
  explicit `(crate::...)` target on `[resolve_latest_from_local]`
  (clippy's `redundant_explicit_links` lint flagged it under
  `RUSTDOCFLAGS=-D warnings`) and demote the `[contains_path_sep]`
  intra-doc reference to backticks since the helper is private.
- `resolving-npm-resolver/src/parse_bare_specifier.rs`: rename
  `unparseable` to `unparsable` in a doc comment so the workspace
  typos check passes.
Ports upstream's
[`resolving/default-resolver/test/namedRegistry.ts`](https://github.com/pnpm/pnpm/blob/b61e268d57/resolving/default-resolver/test/namedRegistry.ts)
and the two `resolveNamedRegistry.test.ts` cases the previous
commit hadn't covered.

- New `tests/chain.rs` integration tests assert that the explicit
  `link:` / `workspace:` / `file:` schemes win over a colliding
  named-registry alias. These pin the local-scheme / local-path
  split: with combined `LocalResolver`, named-registry would slot
  *after* both halves and a `link` alias could hijack `link:./pkg`.
- `resolves_via_builtin_gh_alias` covers the `gh:` happy path that
  was previously only exercised via a user-supplied `work:` alias.
- `preserves_scoped_pkg_name_when_alias_differs` verifies that the
  resolver records the dependency under the registry's name, not
  the local manifest alias.
- `user_config_overrides_builtin_gh_alias` covers the GHES override
  scenario where a user points `gh` at their enterprise host.

11 resolver tests pass (5 unit + 3 chain integration + 3 happy-path
scenarios) plus the 12 parser cases already in place.
@zkochan zkochan marked this pull request as ready for review May 20, 2026 22:52
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@coderabbitai coderabbitai Bot added area: config dependencies Changes related to configDependencies. area: resolution labels May 20, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
pacquet/crates/resolving-npm-resolver/tests/chain.rs (2)

89-91: ⚡ Quick win

Add debug context before the structural assertions.

The matches! checks are the non-assert_eq! assertions here. If one fails, we currently lose the actual resolution payload, which makes these regressions harder to triage.

As per coding guidelines, "Log before non-assert_eq! assertions, dbg! complex structures, skip logging for simple scalar assert_eq!."

Also applies to: 121-123, 152-154

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pacquet/crates/resolving-npm-resolver/tests/chain.rs` around lines 89 - 91,
Add debug output for the complex `result.resolution` before the structural
`assert!` checks so failures show the payload; specifically, after `let result =
resolver.resolve(&wanted, &opts).await.expect("resolve");` call and before the
`assert!(matches!(result.resolution, LockfileResolution::Directory(_)));` (and
the analogous places around lines 121-123 and 152-154), insert a debug dump of
the resolution (e.g., call dbg!(&result.resolution) or equivalent logging) while
leaving the simple scalar `assert_eq!(result.resolved_via, "local-filesystem");`
unchanged.

37-46: ⚡ Quick win

Make precedence regressions fail without touching the network.

If the chain order regresses, this helper will try to fetch from npm.work.example.com, so the failure becomes timeout/DNS-dependent instead of immediate. Flipping the named-registry leg to offline: true keeps the happy path unchanged and makes precedence failures deterministic.

Proposed tweak
         cache_dir: None,
-        offline: false,
+        offline: true,
         prefer_offline: false,
         ignore_missing_time_field: false,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pacquet/crates/resolving-npm-resolver/tests/chain.rs` around lines 37 - 46,
The test helper currently sets offline: false which causes a network fetch to
npm.work.example.com if chain order regresses; change the named-registry leg's
offline flag to true (in the struct where http_client, auth_headers, meta_cache,
cache_dir, offline, prefer_offline, ignore_missing_time_field are set) so the
helper uses the offline path and makes precedence regressions fail
deterministically without touching the network.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@pacquet/crates/resolving-npm-resolver/tests/chain.rs`:
- Around line 89-91: Add debug output for the complex `result.resolution` before
the structural `assert!` checks so failures show the payload; specifically,
after `let result = resolver.resolve(&wanted, &opts).await.expect("resolve");`
call and before the `assert!(matches!(result.resolution,
LockfileResolution::Directory(_)));` (and the analogous places around lines
121-123 and 152-154), insert a debug dump of the resolution (e.g., call
dbg!(&result.resolution) or equivalent logging) while leaving the simple scalar
`assert_eq!(result.resolved_via, "local-filesystem");` unchanged.
- Around line 37-46: The test helper currently sets offline: false which causes
a network fetch to npm.work.example.com if chain order regresses; change the
named-registry leg's offline flag to true (in the struct where http_client,
auth_headers, meta_cache, cache_dir, offline, prefer_offline,
ignore_missing_time_field are set) so the helper uses the offline path and makes
precedence regressions fail deterministically without touching the network.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1a2ef4c0-94e9-4e3d-bff2-71bb09015765

📥 Commits

Reviewing files that changed from the base of the PR and between a8a8cbc and 07485ff.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (18)
  • pacquet/crates/config/src/lib.rs
  • pacquet/crates/config/src/workspace_yaml.rs
  • pacquet/crates/config/src/workspace_yaml/tests.rs
  • pacquet/crates/package-manager/src/build_resolution_verifiers.rs
  • pacquet/crates/package-manager/src/install_package_from_registry/tests.rs
  • pacquet/crates/package-manager/src/install_without_lockfile.rs
  • pacquet/crates/resolving-local-resolver/src/chain.rs
  • pacquet/crates/resolving-local-resolver/src/lib.rs
  • pacquet/crates/resolving-npm-resolver/Cargo.toml
  • pacquet/crates/resolving-npm-resolver/src/lib.rs
  • pacquet/crates/resolving-npm-resolver/src/named_registry.rs
  • pacquet/crates/resolving-npm-resolver/src/named_registry/tests.rs
  • pacquet/crates/resolving-npm-resolver/src/named_registry_resolver.rs
  • pacquet/crates/resolving-npm-resolver/src/named_registry_resolver/tests.rs
  • pacquet/crates/resolving-npm-resolver/src/npm_resolver.rs
  • pacquet/crates/resolving-npm-resolver/src/parse_bare_specifier.rs
  • pacquet/crates/resolving-npm-resolver/src/parse_bare_specifier/tests.rs
  • pacquet/crates/resolving-npm-resolver/tests/chain.rs
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Code Coverage
  • GitHub Check: Lint and Test (ubuntu-latest)
🧰 Additional context used
📓 Path-based instructions (2)
pacquet/**/*.rs

📄 CodeRabbit inference engine (pacquet/AGENTS.md)

pacquet/**/*.rs: When porting a function that fires pnpm:<channel> events through globalLogger, logger.debug(), or streamParser.write(), mirror the call site, payload, and ordering so the reporter parses pacquet's NDJSON the same way it parses pnpm's.
Declare a newtype wrapper for branded string types. Do not collapse the brand into a plain String or &str.
If upstream always validates before construction, validate in pacquet's wrapper too. The wrapper must construct only via TryFrom<String> and/or FromStr. Do not provide an infallible public constructor.
If upstream never validates, just brand for type-safety. Expose an infallible From<String> (and From<&str> when convenient).
If upstream occasionally constructs without validation, expose from_str_unchecked as an escape hatch alongside the validating constructor.
Match upstream serde behavior for branded types that cross JSON, YAML, or INI boundaries. Use #[serde(try_from = "String")] for deserialization and #[serde(into = "String")] for serialization.
Use #[derive(derive_more::From)] and #[derive(derive_more::Into)] for mechanical conversion impls. Fall back to manual impl only when conversion needs custom logic.
String-literal unions should become enums, not newtype wrappers. Model closed sets of valid string values as enums.
Template literal types should be treated as branded strings with validation discipline from rules 2-5.
Choose owned vs. borrowed parameters to minimize copies. Widen to the most encompassing type (&Path over &PathBuf, &str over &String) when it doesn't force extra copies.
Prefer Arc::clone(&x) / Rc::clone(&x) over x.clone() for reference-counted types, so the cost is visible at the call site.
Follow Rust API Guidelines for naming conventions.
Do not use star imports inside module bodies. Write use super::{Foo, bar} instead of use super::*;. Two forms stay allowed: external-crate preludes like use rayon::prelude::*; and root-of-module re-...

Files:

  • pacquet/crates/resolving-local-resolver/src/lib.rs
  • pacquet/crates/resolving-npm-resolver/src/npm_resolver.rs
  • pacquet/crates/package-manager/src/build_resolution_verifiers.rs
  • pacquet/crates/resolving-npm-resolver/src/named_registry/tests.rs
  • pacquet/crates/resolving-npm-resolver/src/lib.rs
  • pacquet/crates/config/src/workspace_yaml/tests.rs
  • pacquet/crates/resolving-npm-resolver/src/named_registry.rs
  • pacquet/crates/package-manager/src/install_without_lockfile.rs
  • pacquet/crates/config/src/workspace_yaml.rs
  • pacquet/crates/resolving-local-resolver/src/chain.rs
  • pacquet/crates/resolving-npm-resolver/tests/chain.rs
  • pacquet/crates/resolving-npm-resolver/src/parse_bare_specifier/tests.rs
  • pacquet/crates/resolving-npm-resolver/src/named_registry_resolver/tests.rs
  • pacquet/crates/resolving-npm-resolver/src/named_registry_resolver.rs
  • pacquet/crates/resolving-npm-resolver/src/parse_bare_specifier.rs
  • pacquet/crates/package-manager/src/install_package_from_registry/tests.rs
  • pacquet/crates/config/src/lib.rs
pacquet/**/{tests,test}/**/*.rs

📄 CodeRabbit inference engine (pacquet/AGENTS.md)

pacquet/**/{tests,test}/**/*.rs: Prefer real fixtures; reach for the dependency-injection seam only when they can't cover the branch. Most happy paths and error paths should be tested with tempfile::TempDir, the mocked registry, or an integration test that spawns the actual binary.
Use the DI seam — a capability trait on the Host provider, threaded as Sys: <Bounds> — only for branches a real fixture can't reach portably: filesystem error kinds, deterministic time, shared process-global state, or external-service happy paths.
Log before non-assert_eq! assertions, dbg! complex structures, skip logging for simple scalar assert_eq!. Follow the test-logging guidance in CODE_STYLE_GUIDE.md.
Tests must not be tolerant of missing build/runtime environment by silently returning early when a tool isn't found. If the test needs a tool, let it panic when the tool is absent.
Prefer #[cfg_attr(target_os = "windows", ignore = "...")] (or #[cfg(unix)]) over a runtime probe-and-skip helper for platform-locked tools.
Port relevant tests from pnpm when porting behavior. Matching test coverage is the easiest way to prove behavioral parity.
When porting behavior from pnpm, consult plans/TEST_PORTING.md before adding ported tests. Use allow_known_failure! at the not-yet-implemented boundary and update checkboxes as items land.

Files:

  • pacquet/crates/resolving-npm-resolver/tests/chain.rs
🧠 Learnings (1)
📚 Learning: 2026-05-20T19:40:55.051Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11774
File: pacquet/crates/resolving-deps-resolver/src/resolve_peers.rs:0-0
Timestamp: 2026-05-20T19:40:55.051Z
Learning: In the pacquet Rust code, ensure the semver implementation uses the `node-semver` crate (not `nodejs-semver`). `node-semver`’s public API does not include a `satisfies_with_prerelease`-style method; prerelease-tolerant matching should be implemented inline by first calling `Range::satisfies`, and when it rejects a prerelease version, retry matching against a stripped `MAJOR.MINOR.PATCH` base of the prerelease version.

Applied to files:

  • pacquet/crates/resolving-local-resolver/src/lib.rs
  • pacquet/crates/resolving-npm-resolver/src/npm_resolver.rs
  • pacquet/crates/package-manager/src/build_resolution_verifiers.rs
  • pacquet/crates/resolving-npm-resolver/src/named_registry/tests.rs
  • pacquet/crates/resolving-npm-resolver/src/lib.rs
  • pacquet/crates/config/src/workspace_yaml/tests.rs
  • pacquet/crates/resolving-npm-resolver/src/named_registry.rs
  • pacquet/crates/package-manager/src/install_without_lockfile.rs
  • pacquet/crates/config/src/workspace_yaml.rs
  • pacquet/crates/resolving-local-resolver/src/chain.rs
  • pacquet/crates/resolving-npm-resolver/tests/chain.rs
  • pacquet/crates/resolving-npm-resolver/src/parse_bare_specifier/tests.rs
  • pacquet/crates/resolving-npm-resolver/src/named_registry_resolver/tests.rs
  • pacquet/crates/resolving-npm-resolver/src/named_registry_resolver.rs
  • pacquet/crates/resolving-npm-resolver/src/parse_bare_specifier.rs
  • pacquet/crates/package-manager/src/install_package_from_registry/tests.rs
  • pacquet/crates/config/src/lib.rs
🔇 Additional comments (18)
pacquet/crates/config/src/lib.rs (1)

380-391: LGTM!

Also applies to: 1013-1019, 1106-1115, 1133-1139

pacquet/crates/config/src/workspace_yaml.rs (1)

2-3: LGTM!

Also applies to: 125-132, 402-419, 500-502

pacquet/crates/config/src/workspace_yaml/tests.rs (1)

2-2: LGTM!

Also applies to: 109-167

pacquet/crates/resolving-npm-resolver/src/named_registry.rs (1)

18-19: LGTM!

Also applies to: 26-79

pacquet/crates/resolving-npm-resolver/src/named_registry/tests.rs (1)

5-8: LGTM!

Also applies to: 143-184

pacquet/crates/resolving-local-resolver/src/chain.rs (1)

1-13: LGTM!

Also applies to: 26-128, 171-212

pacquet/crates/resolving-local-resolver/src/lib.rs (1)

32-32: LGTM!

pacquet/crates/package-manager/src/install_without_lockfile.rs (1)

21-27: LGTM!

Also applies to: 110-116, 181-249

pacquet/crates/resolving-npm-resolver/src/parse_bare_specifier.rs (1)

15-19: LGTM!

Also applies to: 26-29, 155-286, 295-295

pacquet/crates/resolving-npm-resolver/src/parse_bare_specifier/tests.rs (1)

1-2: LGTM!

Also applies to: 6-10, 217-475

pacquet/crates/resolving-npm-resolver/src/named_registry_resolver.rs (1)

1-215: LGTM!

pacquet/crates/resolving-npm-resolver/src/named_registry_resolver/tests.rs (1)

1-268: LGTM!

pacquet/crates/resolving-npm-resolver/src/lib.rs (1)

29-29: LGTM!

Also applies to: 47-56

pacquet/crates/package-manager/src/build_resolution_verifiers.rs (1)

99-110: LGTM!

pacquet/crates/package-manager/src/install_package_from_registry/tests.rs (1)

42-42: LGTM!

pacquet/crates/resolving-npm-resolver/Cargo.toml (1)

36-37: LGTM!

pacquet/crates/resolving-npm-resolver/src/npm_resolver.rs (2)

330-333: LGTM!


335-335: LGTM!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: config dependencies Changes related to configDependencies. area: resolution

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants