Skip to content

feat(package-manager): port packageExtensions setting#12027

Merged
zkochan merged 1 commit into
mainfrom
pkg-ext
May 28, 2026
Merged

feat(package-manager): port packageExtensions setting#12027
zkochan merged 1 commit into
mainfrom
pkg-ext

Conversation

@zkochan

@zkochan zkochan commented May 28, 2026

Copy link
Copy Markdown
Member

Ports the packageExtensions setting from pnpm to pacquet, end-to-end. Refs #12009.

What lands

Config + yaml plumbing (pacquet-config):

  • PackageExtension / PeerDependencyMeta structs (mirror pnpm's Pick<BaseManifest, 'dependencies' | 'optionalDependencies' | 'peerDependencies' | 'peerDependenciesMeta'>).
  • WorkspaceSettings::package_extensions with yaml deserialization, clear_workspace_only_fields entry, and empty-map collapse (matches the overrides shape).
  • Config::package_extensions field.
  • PNPM_CONFIG_PACKAGE_EXTENSIONS env var.

Hook (pacquet-package-manager::PackageExtender):

  • Port of pnpm's createPackageExtender: groups extensions by package name once, then applies semver range filter and merges fields onto the manifest. Manifest fields win over extension fields on conflict (matches upstream's { ...extension, ...manifest } spread order).
  • apply_to_arc deep-clones the inner Value only when an extension matches — unrelated manifests keep sharing the resolver's cached Arc.

Resolver wiring (pacquet-resolving-deps-resolver):

  • New ManifestHook type, threaded through ResolveDependencyTreeOptions / ResolveImporterOptions / TreeCtx.
  • Applied in resolve_node right after Resolver::resolve returns and before the result enters the wanted-dep cache — same site as pnpm's ctx.readPackageHook(pkg) call.
  • install_with_fresh_lockfile builds the hook from Config.package_extensions once per install.

Workspace state / drift check (optimistic_repeat_install):

  • current_settings now emits package_extensions so .pnpm-workspace-state-v1.json round-trips through pnpm.
  • settings_match compares the field (treating empty map as absent for cross-implementation parity).
  • Removed from the "deliberately not compared" list in settings_match.

Lockfile checksum + frozen-install drift gate (pacquet-graph-hasher, pacquet-lockfile):

  • New hash_object_nullable_with_prefix in pacquet-graph-hasher — port of pnpm's hashObjectNullableWithPrefix, byte-for-byte identical (tests pin against the known sha256-48AVoXIXcTKcnHt8qVKp5vNw4gyOB5VfztHwtYBRcAQ= output upstream's test asserts).
  • New Lockfile::package_extensions_checksum top-level field (matches upstream's wire shape).
  • install_with_fresh_lockfile computes and writes the checksum on every fresh install; current_lockfile clones it into the materialized lockfile.
  • check_lockfile_settings gained a package_extensions_checksum argument and a new StalenessReason::PackageExtensionsChecksumChanged variant. The check fires after overrides and before ignoredOptionalDependencies — matches upstream's ordering at getOutdatedLockfileSetting.ts:53-55 so the first-drift error reads the same on both sides.

Test plan

  • 10 unit tests for PackageExtender (merge semantics, range filter, scoped names, Arc no-op vs clone-on-match, order-invariance).
  • 2 yaml-parsing tests (parses_package_extensions_from_yaml_and_applies, empty_package_extensions_map_collapses_to_none).
  • Workspace-state drift test returns_skipped_when_package_extensions_drift — ports checkDepsStatus.test.ts:85.
  • Integration test fresh_install_applies_package_extensions_to_dependency_manifest — asserts a packageExtensions-added peerDependencies entry lands in the lockfile's packages block AND that packageExtensionsChecksum is recorded.
  • 2 checksum-parity tests (hash_object_nullable_with_prefix_known_value, hash_object_nullable_with_prefix_sorts) — port the upstream hashObjectNullableWithPrefix jest tests; pinning the prefixed sha256-… output keeps pacquet's checksum byte-identical to pnpm's.
  • 5 freshness-gate drift tests (check_settings_returns_ok_when_no_package_extensions_checksum_on_either_side, …matches, …value_change, …when_lockfile_has_checksum_but_config_does_not, …when_config_has_checksum_but_lockfile_does_not).
  • Frozen-install integration test frozen_lockfile_errors_when_package_extensions_drift_from_lockfile — drift surfaces as OutdatedLockfile::PackageExtensionsChecksumChanged.
  • cargo nextest run -p pacquet-config -p pacquet-resolving-deps-resolver -p pacquet-package-manager -p pacquet-graph-hasher -p pacquet-lockfile — 859/859 pass.
  • cargo clippy --locked --workspace --all-targets -- --deny warnings clean.
  • just fmt.

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

Summary by CodeRabbit

  • New Features

    • Workspace packageExtensions support to extend package manifests during resolution.
    • packageExtensions can be provided via environment and applied as a manifest hook.
    • Generated lockfiles record a packageExtensions checksum.
  • Bug Fixes / Reliability

    • Frozen installs fail on packageExtensions checksum drift.
    • Fast-path optimistic repeat-install now verifies packageExtensions for reuse.
  • Tests

    • Added tests for parsing, application, checksum invariants, serialization, and freshness cases.
  • Documentation

    • Porting checklist updated to cover packageExtensions lockfile/write and drift tests.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR implements end-to-end pnpm-style packageExtensions support: config/YAML/env parsing, data types, PackageExtender, resolver manifest_hook, nullable-prefixed hashing, lockfile checksum wiring, freshness/frozen checks, optimistic fast-path tracking, and tests/fixtures.

Changes

packageExtensions

Layer / File(s) Summary
Config & env parsing
pacquet/crates/config/src/{env_overlay.rs,lib.rs,workspace_yaml.rs}, pacquet/crates/config/src/workspace_yaml/tests.rs
Adds package_extensions to WorkspaceSettings and Config, defines PackageExtension/PeerDependencyMeta shapes, supports PACKAGE_EXTENSIONS env JSON, clears workspace-only field, and collapses empty maps to None on apply.
Hashing helper
pacquet/crates/graph-hasher/src/{object_hasher.rs,lib.rs,tests.rs}
Adds hash_object_nullable_with_prefix that maps null/empty-object -> None and otherwise returns Some("sha256-"+hash), with tests for deterministic output and ordering invariance.
PackageExtender implementation
pacquet/crates/package-manager/src/package_extender.rs, .../package_extender/tests.rs, pacquet/crates/package-manager/src/lib.rs
Implements selector parsing/grouping, semver-range matching, merge semantics (extension keys inserted first, manifest wins), Arc-aware apply, into_manifest_hook returning Option hook, and many tests.
Resolver manifest_hook plumbing
pacquet/crates/resolving-deps-resolver/src/{resolve_dependency_tree.rs,resolve_importer.rs,lib.rs}, tests
Adds ManifestHook type, threads hook through ResolveImporter/TreeCtx/resolve_node, applies hook on cache-miss before caching resolved manifests, and updates Debug impls and tests to pass manifest_hook: None.
Install (fresh) integration & checksum
pacquet/crates/package-manager/src/{install.rs,install_with_fresh_lockfile.rs}, tests
Precomputes per-install PackageExtender hook, threads it into ResolveImporterOptions, computes package_extensions checksum (JSON-serialize + nullable-aware hasher), writes packageExtensionsChecksum into fresh pnpm-lock.yaml, and adds unit/integration tests.
Graph → Lockfile wiring
pacquet/crates/package-manager/src/{dependencies_graph_to_lockfile.rs,current_lockfile.rs}, tests
Adds GraphToLockfileOptions.package_extensions_checksum, writes it into produced Lockfile, and preserves the checksum in filtered current lockfiles; updates many test literals.
Lockfile freshness & tests
pacquet/crates/lockfile/src/{lib.rs,freshness.rs,freshness/tests.rs}
Adds top-level Lockfile::package_extensions_checksum, StalenessReason::PackageExtensionsChecksumChanged, extends check_lockfile_settings to accept and compare the checksum, and adds tests for match/mismatch scenarios.
Optimistic repeat-install & workspace-state
pacquet/crates/package-manager/src/optimistic_repeat_install.rs, tests, pacquet/crates/workspace-state/src/tests.rs
Includes package_extensions in fast-path equality (None ≡ empty/null), serializes package_extensions into workspace-state, adds round-trip test, and adds optimistic-repeat-install drift test.
Fixtures, docs & small rewrites
pacquet/crates/* tests, pacquet/plans/TEST_PORTING.md, pacquet/crates/package-manager/Cargo.toml
Updates Lockfile literals to include package_extensions_checksum: None, adjusts tests, refactors a Cargo.toml block formatting, and updates TEST_PORTING.md checklist.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

  • KSXGitHub

Poem

🐰 I nibble keys and parse their strings,
I stitch peer deps where the resolver brings,
A checksum hums across the lockfile sea,
Extensions applied, the installs agree,
Hooray — manifests bloom, neat as can be!

🚥 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 PR title 'feat(package-manager): port packageExtensions setting' directly and clearly describes the main change: porting the packageExtensions feature to the package-manager module.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pkg-ext

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.

@github-actions

github-actions Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Micro-Benchmark Results

Linux

group                          main                                   pr
-----                          ----                                   --
tarball/download_dependency    1.02      8.1±0.20ms   536.5 KB/sec    1.00      7.9±0.29ms   548.0 KB/sec

@codecov-commenter

codecov-commenter commented May 28, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.55957% with 40 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.48%. Comparing base (a0eb2ad) to head (e5a1134).

Files with missing lines Patch % Lines
...es/resolving-deps-resolver/src/resolve_importer.rs 16.66% 20 Missing ⚠️
...s/package-manager/src/optimistic_repeat_install.rs 68.18% 7 Missing ⚠️
...lving-deps-resolver/src/resolve_dependency_tree.rs 69.56% 7 Missing ⚠️
...uet/crates/package-manager/src/package_extender.rs 95.83% 5 Missing ⚠️
pacquet/crates/graph-hasher/src/object_hasher.rs 90.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #12027      +/-   ##
==========================================
- Coverage   88.50%   88.48%   -0.03%     
==========================================
  Files         230      231       +1     
  Lines       29225    29498     +273     
==========================================
+ Hits        25865    26100     +235     
- Misses       3360     3398      +38     

☔ 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.

@zkochan zkochan force-pushed the pkg-ext branch 2 times, most recently from 1f5741a to 4073080 Compare May 28, 2026 16:35
@zkochan zkochan marked this pull request as ready for review May 28, 2026 16:37
@github-actions

github-actions Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Integrated-Benchmark Report (Linux)

Scenario: Isolated linker: fresh restore, cold cache + cold store

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 2.157 ± 0.303 1.975 2.991 1.03 ± 0.15
pacquet@main 2.088 ± 0.050 2.004 2.170 1.00
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 2.15682573214,
      "stddev": 0.3033867900413,
      "median": 2.0407965263400003,
      "user": 2.62040528,
      "system": 3.3834996399999993,
      "min": 1.9751595913400002,
      "max": 2.9912526063400002,
      "times": [
        2.01374425434,
        2.01228282834,
        2.02495968334,
        2.05663336934,
        2.07255226334,
        2.0209836713400002,
        2.22254954334,
        2.9912526063400002,
        2.17813951034,
        1.9751595913400002
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 2.0878488802399997,
      "stddev": 0.049952952158503025,
      "median": 2.07364959384,
      "user": 2.5842082799999995,
      "system": 3.3664518400000008,
      "min": 2.00447640534,
      "max": 2.17028574134,
      "times": [
        2.15905621734,
        2.00447640534,
        2.05318807334,
        2.0929105153400003,
        2.07206905134,
        2.07523013634,
        2.06322388234,
        2.06830966034,
        2.11973911934,
        2.17028574134
      ]
    }
  ]
}

Scenario: Isolated linker: fresh restore, hot cache + hot store

Command Mean [ms] Min [ms] Max [ms] Relative
pacquet@HEAD 639.0 ± 11.1 621.7 658.5 1.00
pacquet@main 662.2 ± 20.0 639.4 707.1 1.04 ± 0.04
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 0.6389767469199998,
      "stddev": 0.011079426643169181,
      "median": 0.6370742762200001,
      "user": 0.34562203999999996,
      "system": 1.32461756,
      "min": 0.62174762722,
      "max": 0.65850547822,
      "times": [
        0.65850547822,
        0.65262817322,
        0.63516807022,
        0.64375220922,
        0.6345650192200001,
        0.62631540622,
        0.6367564022200001,
        0.6373921502200001,
        0.62174762722,
        0.64293693322
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 0.6622128418200001,
      "stddev": 0.019953226471699663,
      "median": 0.65752955822,
      "user": 0.35036784,
      "system": 1.33437866,
      "min": 0.63942457822,
      "max": 0.70713723722,
      "times": [
        0.66384001222,
        0.65933804322,
        0.6471120652200001,
        0.67537396922,
        0.65142127622,
        0.6557210732200001,
        0.67676718922,
        0.64599297422,
        0.70713723722,
        0.63942457822
      ]
    }
  ]
}

Scenario: Isolated linker: fresh install, cold cache + cold store

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 2.253 ± 0.035 2.192 2.312 1.00
pacquet@main 2.276 ± 0.021 2.244 2.322 1.01 ± 0.02
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 2.2534594746,
      "stddev": 0.03485976614913816,
      "median": 2.2420406303,
      "user": 3.7128850800000004,
      "system": 3.0164347400000002,
      "min": 2.1922497663000002,
      "max": 2.3124016473,
      "times": [
        2.2873856433,
        2.2742235173,
        2.2384382643,
        2.2389888443,
        2.2391994383,
        2.2800767923,
        2.3124016473,
        2.1922497663000002,
        2.2448818223,
        2.2267490103000003
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 2.2759379622,
      "stddev": 0.02109063415013645,
      "median": 2.2764394643,
      "user": 3.7461075800000003,
      "system": 3.0407588400000005,
      "min": 2.2441578853,
      "max": 2.3217088073,
      "times": [
        2.2783633823000002,
        2.2702464783000003,
        2.2551621903,
        2.2441578853,
        2.2813942753,
        2.2748597893,
        2.2638276103,
        2.2780191393,
        2.3217088073,
        2.2916400643
      ]
    }
  ]
}

Scenario: Isolated linker: fresh install, hot cache + hot store

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 1.478 ± 0.036 1.436 1.548 1.00
pacquet@main 1.493 ± 0.050 1.450 1.621 1.01 ± 0.04
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 1.4776285452599998,
      "stddev": 0.035734416162806246,
      "median": 1.46780461646,
      "user": 1.7182248400000002,
      "system": 1.7610809,
      "min": 1.43581147246,
      "max": 1.54825339046,
      "times": [
        1.47252463646,
        1.4708819604599999,
        1.46472727246,
        1.53341682246,
        1.4799180114600001,
        1.46070457446,
        1.43581147246,
        1.46180726346,
        1.44824004846,
        1.54825339046
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 1.49344279006,
      "stddev": 0.05021683018450585,
      "median": 1.4851785894599998,
      "user": 1.7103150399999998,
      "system": 1.7712651999999998,
      "min": 1.44975993346,
      "max": 1.62138190346,
      "times": [
        1.46044028946,
        1.44975993346,
        1.5286477654600001,
        1.62138190346,
        1.48701308146,
        1.45808045246,
        1.46783925646,
        1.48525027346,
        1.49090803946,
        1.48510690546
      ]
    }
  ]
}

@github-actions

github-actions Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

🐰 Bencher Report

Branchpr/12027
Testbedpacquet
Click to view all benchmark results
BenchmarkLatencyBenchmark Result
milliseconds (ms)
(Result Δ%)
Upper Boundary
milliseconds (ms)
(Limit %)
isolated-linker.fresh-install.cold-cache.cold-store📈 view plot
🚷 view threshold
2,253.46 ms
(-2.06%)Baseline: 2,300.81 ms
2,760.97 ms
(81.62%)
isolated-linker.fresh-install.hot-cache.hot-store📈 view plot
🚷 view threshold
1,477.63 ms
(+2.92%)Baseline: 1,435.65 ms
1,722.78 ms
(85.77%)
isolated-linker.fresh-restore.cold-cache.cold-store📈 view plot
🚷 view threshold
2,156.83 ms
(+4.83%)Baseline: 2,057.37 ms
2,468.85 ms
(87.36%)
isolated-linker.fresh-restore.hot-cache.hot-store📈 view plot
🚷 view threshold
638.98 ms
(-5.66%)Baseline: 677.34 ms
812.81 ms
(78.61%)
🐰 View full continuous benchmarking report in Bencher

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Review Summary by Qodo

Port packageExtensions setting end-to-end with manifest hook and checksum validation

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Port packageExtensions setting end-to-end from pnpm to pacquet
• Add PackageExtender hook to merge extensions onto resolved manifests
• Compute and validate packageExtensionsChecksum for lockfile drift detection
• Thread ManifestHook through resolver to apply extensions during resolution
• Add workspace-state drift check for packageExtensions configuration changes
Diagram
flowchart LR
  Config["Config.package_extensions"]
  Extender["PackageExtender<br/>groups by name"]
  Hook["ManifestHook<br/>Arc closure"]
  Resolver["Resolver<br/>resolve_node"]
  Manifest["Manifest<br/>with extensions"]
  Checksum["packageExtensionsChecksum<br/>sha256 hash"]
  Lockfile["Lockfile<br/>with checksum"]
  DriftCheck["Drift check<br/>on frozen install"]
  
  Config -->|new| Extender
  Extender -->|into_manifest_hook| Hook
  Hook -->|threaded to| Resolver
  Resolver -->|apply_to_arc| Manifest
  Config -->|compute hash| Checksum
  Manifest -->|recorded in| Lockfile
  Checksum -->|recorded in| Lockfile
  Lockfile -->|compared by| DriftCheck

Loading

Grey Divider

File Changes

1. pacquet/crates/package-manager/src/package_extender.rs ✨ Enhancement +207/-0

New PackageExtender hook for manifest extension merging

pacquet/crates/package-manager/src/package_extender.rs


2. pacquet/crates/package-manager/src/package_extender/tests.rs 🧪 Tests +197/-0

Unit tests for PackageExtender merge and range semantics

pacquet/crates/package-manager/src/package_extender/tests.rs


3. pacquet/crates/config/src/workspace_yaml.rs ✨ Enhancement +60/-0

Add PackageExtension and PeerDependencyMeta yaml structs

pacquet/crates/config/src/workspace_yaml.rs


View more (29)
4. pacquet/crates/config/src/workspace_yaml/tests.rs 🧪 Tests +64/-0

Tests for packageExtensions yaml parsing and empty map collapse

pacquet/crates/config/src/workspace_yaml/tests.rs


5. pacquet/crates/config/src/lib.rs ✨ Enhancement +19/-2

Export PackageExtension types and add Config field

pacquet/crates/config/src/lib.rs


6. pacquet/crates/config/src/env_overlay.rs ✨ Enhancement +1/-0

Add PNPM_CONFIG_PACKAGE_EXTENSIONS env var support

pacquet/crates/config/src/env_overlay.rs


7. pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs ✨ Enhancement +64/-4

Thread ManifestHook through TreeCtx for per-resolve dispatch

pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs


8. pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs ✨ Enhancement +27/-2

Add manifest_hook field to ResolveImporterOptions

pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs


9. pacquet/crates/resolving-deps-resolver/src/lib.rs ✨ Enhancement +1/-1

Export ManifestHook type for public API

pacquet/crates/resolving-deps-resolver/src/lib.rs


10. pacquet/crates/resolving-deps-resolver/src/tests.rs 🧪 Tests +30/-0

Update all test calls to include manifest_hook field

pacquet/crates/resolving-deps-resolver/src/tests.rs


11. pacquet/crates/resolving-deps-resolver/src/resolve_importer/tests.rs 🧪 Tests +1/-0

Update default_opts to include manifest_hook None

pacquet/crates/resolving-deps-resolver/src/resolve_importer/tests.rs


12. pacquet/crates/graph-hasher/src/object_hasher.rs ✨ Enhancement +25/-0

Add hash_object_nullable_with_prefix for checksum computation

pacquet/crates/graph-hasher/src/object_hasher.rs


13. pacquet/crates/graph-hasher/src/lib.rs ✨ Enhancement +4/-1

Export hash_object_nullable_with_prefix function

pacquet/crates/graph-hasher/src/lib.rs


14. pacquet/crates/graph-hasher/src/tests.rs 🧪 Tests +30/-1

Tests for hash_object_nullable_with_prefix known values

pacquet/crates/graph-hasher/src/tests.rs


15. pacquet/crates/lockfile/src/lib.rs ✨ Enhancement +16/-0

Add package_extensions_checksum field to Lockfile struct

pacquet/crates/lockfile/src/lib.rs


16. pacquet/crates/lockfile/src/freshness.rs ✨ Enhancement +25/-0

Add PackageExtensionsChecksumChanged staleness reason and check

pacquet/crates/lockfile/src/freshness.rs


17. pacquet/crates/lockfile/src/freshness/tests.rs 🧪 Tests +108/-12

Tests for packageExtensionsChecksum drift detection

pacquet/crates/lockfile/src/freshness/tests.rs


18. pacquet/crates/package-manager/src/dependencies_graph_to_lockfile.rs ✨ Enhancement +9/-0

Add package_extensions_checksum to GraphToLockfileOptions

pacquet/crates/package-manager/src/dependencies_graph_to_lockfile.rs


19. pacquet/crates/package-manager/src/dependencies_graph_to_lockfile/tests.rs 🧪 Tests +6/-0

Update test fixtures with package_extensions_checksum field

pacquet/crates/package-manager/src/dependencies_graph_to_lockfile/tests.rs


20. pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs ✨ Enhancement +27/-0

Build PackageExtender hook and compute checksum at install time

pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs


21. pacquet/crates/package-manager/src/install.rs ✨ Enhancement +11/-3

Compute and pass checksum to lockfile freshness check

pacquet/crates/package-manager/src/install.rs


22. pacquet/crates/package-manager/src/current_lockfile.rs ✨ Enhancement +1/-0

Clone package_extensions_checksum to materialized lockfile

pacquet/crates/package-manager/src/current_lockfile.rs


23. pacquet/crates/package-manager/src/current_lockfile/tests.rs 🧪 Tests +1/-0

Update test fixture with package_extensions_checksum field

pacquet/crates/package-manager/src/current_lockfile/tests.rs


24. pacquet/crates/package-manager/src/optimistic_repeat_install.rs ✨ Enhancement +32/-1

Add package_extensions drift check to workspace state comparison

pacquet/crates/package-manager/src/optimistic_repeat_install.rs


25. pacquet/crates/package-manager/src/optimistic_repeat_install/tests.rs 🧪 Tests +56/-2

Test packageExtensions drift invalidates cached state

pacquet/crates/package-manager/src/optimistic_repeat_install/tests.rs


26. pacquet/crates/package-manager/src/install/tests.rs 🧪 Tests +185/-0

Integration tests for packageExtensions resolution and drift

pacquet/crates/package-manager/src/install/tests.rs


27. pacquet/crates/package-manager/src/hoisted_dep_graph.rs 🧪 Tests +5/-0

Update test fixtures with package_extensions_checksum field

pacquet/crates/package-manager/src/hoisted_dep_graph.rs


28. pacquet/crates/package-manager/src/install_package_from_registry/tests.rs 🧪 Tests +1/-0

Update test config with package_extensions field

pacquet/crates/package-manager/src/install_package_from_registry/tests.rs


29. pacquet/crates/real-hoist/src/tests.rs 🧪 Tests +19/-0

Update lockfile fixtures with package_extensions_checksum field

pacquet/crates/real-hoist/src/tests.rs


30. pacquet/crates/package-manager/src/lib.rs ✨ Enhancement +2/-0

Add package_extender module and export public API

pacquet/crates/package-manager/src/lib.rs


31. pacquet/crates/package-manager/Cargo.toml Dependencies +38/-37

Add pacquet-resolving-parse-wanted-dependency dependency

pacquet/crates/package-manager/Cargo.toml


32. pacquet/plans/TEST_PORTING.md 📝 Documentation +3/-2

Update test porting status for packageExtensions

pacquet/plans/TEST_PORTING.md


Grey Divider

Qodo Logo

@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.

Actionable comments posted: 2

🤖 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.

Inline comments:
In `@pacquet/crates/package-manager/src/install/tests.rs`:
- Around line 5629-5633: The doc comment above the test is stale: it states
Pacquet doesn't record packageExtensionsChecksum while the test actually asserts
that packageExtensionsChecksum is written to the lockfile; update or remove that
note so the comment matches the test contract. Locate the comment text
mentioning `packageExtensionsChecksum` and the lockfile in the test block (near
the test that asserts the checksum appears in the package's lockfile metadata)
and either remove the sentence claiming it isn't recorded or rewrite it to state
that Pacquet now records `packageExtensionsChecksum` and the test verifies that
behavior.

In `@pacquet/crates/package-manager/src/package_extender.rs`:
- Around line 124-129: The current logic clones the manifest whenever the
package name exists in self.by_pkg_name, even if none of that package's
extension selectors actually match the requested version; instead, lookup the
entries from self.by_pkg_name.get(name) and scan them for any selector/range
match first, and only if at least one entry would apply then perform
Value::clone(&manifest), call self.apply(&mut cloned), and return
Arc::new(cloned); otherwise return the original manifest unchanged. Use the
existing selector-match/check logic used elsewhere (the same predicate that
apply would use) so you avoid unnecessary allocations while keeping the
apply(...) and by_pkg_name symbols intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8b585afe-a6b2-46dc-ba37-763e14e149f0

📥 Commits

Reviewing files that changed from the base of the PR and between 7ecaf3d and 4073080.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (32)
  • pacquet/crates/config/src/env_overlay.rs
  • pacquet/crates/config/src/lib.rs
  • pacquet/crates/config/src/workspace_yaml.rs
  • pacquet/crates/config/src/workspace_yaml/tests.rs
  • pacquet/crates/graph-hasher/src/lib.rs
  • pacquet/crates/graph-hasher/src/object_hasher.rs
  • pacquet/crates/graph-hasher/src/tests.rs
  • pacquet/crates/lockfile/src/freshness.rs
  • pacquet/crates/lockfile/src/freshness/tests.rs
  • pacquet/crates/lockfile/src/lib.rs
  • pacquet/crates/package-manager/Cargo.toml
  • pacquet/crates/package-manager/src/current_lockfile.rs
  • pacquet/crates/package-manager/src/current_lockfile/tests.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile/tests.rs
  • pacquet/crates/package-manager/src/hoisted_dep_graph.rs
  • pacquet/crates/package-manager/src/install.rs
  • pacquet/crates/package-manager/src/install/tests.rs
  • pacquet/crates/package-manager/src/install_package_from_registry/tests.rs
  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
  • pacquet/crates/package-manager/src/lib.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install/tests.rs
  • pacquet/crates/package-manager/src/package_extender.rs
  • pacquet/crates/package-manager/src/package_extender/tests.rs
  • pacquet/crates/real-hoist/src/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/lib.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/tests.rs
  • pacquet/plans/TEST_PORTING.md
📜 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). (4)
  • GitHub Check: Lint and Test (macos-latest)
  • GitHub Check: Code Coverage
  • GitHub Check: Analyze (javascript)
  • GitHub Check: Compile & Lint
🧰 Additional context used
📓 Path-based instructions (1)
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/package-manager/src/lib.rs
  • pacquet/crates/graph-hasher/src/lib.rs
  • pacquet/crates/package-manager/src/current_lockfile.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/lib.rs
  • pacquet/crates/graph-hasher/src/object_hasher.rs
  • pacquet/crates/package-manager/src/current_lockfile/tests.rs
  • pacquet/crates/config/src/workspace_yaml/tests.rs
  • pacquet/crates/graph-hasher/src/tests.rs
  • pacquet/crates/lockfile/src/lib.rs
  • pacquet/crates/package-manager/src/install_package_from_registry/tests.rs
  • pacquet/crates/config/src/lib.rs
  • pacquet/crates/package-manager/src/install.rs
  • pacquet/crates/package-manager/src/install/tests.rs
  • pacquet/crates/config/src/env_overlay.rs
  • pacquet/crates/lockfile/src/freshness.rs
  • pacquet/crates/package-manager/src/package_extender.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs
  • pacquet/crates/package-manager/src/package_extender/tests.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile.rs
  • pacquet/crates/config/src/workspace_yaml.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install.rs
  • pacquet/crates/package-manager/src/hoisted_dep_graph.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs
  • pacquet/crates/lockfile/src/freshness/tests.rs
  • pacquet/crates/real-hoist/src/tests.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install/tests.rs
  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
🧠 Learnings (6)
📚 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/package-manager/src/lib.rs
  • pacquet/crates/graph-hasher/src/lib.rs
  • pacquet/crates/package-manager/src/current_lockfile.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/lib.rs
  • pacquet/crates/graph-hasher/src/object_hasher.rs
  • pacquet/crates/package-manager/src/current_lockfile/tests.rs
  • pacquet/crates/config/src/workspace_yaml/tests.rs
  • pacquet/crates/graph-hasher/src/tests.rs
  • pacquet/crates/lockfile/src/lib.rs
  • pacquet/crates/package-manager/src/install_package_from_registry/tests.rs
  • pacquet/crates/config/src/lib.rs
  • pacquet/crates/package-manager/src/install.rs
  • pacquet/crates/package-manager/src/install/tests.rs
  • pacquet/crates/config/src/env_overlay.rs
  • pacquet/crates/lockfile/src/freshness.rs
  • pacquet/crates/package-manager/src/package_extender.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs
  • pacquet/crates/package-manager/src/package_extender/tests.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile.rs
  • pacquet/crates/config/src/workspace_yaml.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install.rs
  • pacquet/crates/package-manager/src/hoisted_dep_graph.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs
  • pacquet/crates/lockfile/src/freshness/tests.rs
  • pacquet/crates/real-hoist/src/tests.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install/tests.rs
  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
📚 Learning: 2026-05-22T00:08:44.646Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11837
File: pacquet/crates/resolving-npm-resolver/src/pick_package.rs:33-51
Timestamp: 2026-05-22T00:08:44.646Z
Learning: In the pnpm/pnpm repo’s pacquet Rust crates, do not flag Unicode ellipsis characters (U+2026, `…`) in Rust doc comments (`///` / `/** */`) as a lint violation. The pacquet crate’s `dylint.toml` only enables `perfectionist::derive_ordering`, and the Dylint `unicode-ellipsis` rule is not enabled for this project—so `…` in doc comments is an intentional, repo-consistent style.

Applied to files:

  • pacquet/crates/package-manager/src/lib.rs
  • pacquet/crates/graph-hasher/src/lib.rs
  • pacquet/crates/package-manager/src/current_lockfile.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/lib.rs
  • pacquet/crates/graph-hasher/src/object_hasher.rs
  • pacquet/crates/package-manager/src/current_lockfile/tests.rs
  • pacquet/crates/config/src/workspace_yaml/tests.rs
  • pacquet/crates/graph-hasher/src/tests.rs
  • pacquet/crates/lockfile/src/lib.rs
  • pacquet/crates/package-manager/src/install_package_from_registry/tests.rs
  • pacquet/crates/config/src/lib.rs
  • pacquet/crates/package-manager/src/install.rs
  • pacquet/crates/package-manager/src/install/tests.rs
  • pacquet/crates/config/src/env_overlay.rs
  • pacquet/crates/lockfile/src/freshness.rs
  • pacquet/crates/package-manager/src/package_extender.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs
  • pacquet/crates/package-manager/src/package_extender/tests.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile.rs
  • pacquet/crates/config/src/workspace_yaml.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install.rs
  • pacquet/crates/package-manager/src/hoisted_dep_graph.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs
  • pacquet/crates/lockfile/src/freshness/tests.rs
  • pacquet/crates/real-hoist/src/tests.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install/tests.rs
  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
📚 Learning: 2026-05-20T23:07:58.444Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11784
File: pacquet/crates/resolving-deps-resolver/src/hoist_peers.rs:120-133
Timestamp: 2026-05-20T23:07:58.444Z
Learning: When reviewing code in this pacquet Rust port, follow the upstream pnpm compatibility rule: only match pnpm’s behavior exactly. Do not propose review changes that intentionally deviate from pnpm’s documented/observed behavior, even if pnpm appears buggy. If you identify a real bug in pnpm behavior, the review should prioritize fixing it upstream in pnpm first, and avoid implementing a pnpm-behavior workaround here unless the same fix has already landed upstream.

Applied to files:

  • pacquet/crates/package-manager/src/lib.rs
  • pacquet/crates/graph-hasher/src/lib.rs
  • pacquet/crates/package-manager/src/current_lockfile.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/lib.rs
  • pacquet/crates/graph-hasher/src/object_hasher.rs
  • pacquet/crates/package-manager/src/current_lockfile/tests.rs
  • pacquet/crates/config/src/workspace_yaml/tests.rs
  • pacquet/crates/graph-hasher/src/tests.rs
  • pacquet/crates/lockfile/src/lib.rs
  • pacquet/crates/package-manager/src/install_package_from_registry/tests.rs
  • pacquet/crates/config/src/lib.rs
  • pacquet/crates/package-manager/src/install.rs
  • pacquet/crates/package-manager/src/install/tests.rs
  • pacquet/crates/config/src/env_overlay.rs
  • pacquet/crates/lockfile/src/freshness.rs
  • pacquet/crates/package-manager/src/package_extender.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs
  • pacquet/crates/package-manager/src/package_extender/tests.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile.rs
  • pacquet/crates/config/src/workspace_yaml.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install.rs
  • pacquet/crates/package-manager/src/hoisted_dep_graph.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs
  • pacquet/crates/lockfile/src/freshness/tests.rs
  • pacquet/crates/real-hoist/src/tests.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install/tests.rs
  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
📚 Learning: 2026-05-24T21:10:50.292Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11915
File: pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs:553-617
Timestamp: 2026-05-24T21:10:50.292Z
Learning: When implementing dependency “revisit”/optional-folding in pacquet, mirror pnpm’s `resolveDependencies.ts` behavior: on revisit, update the `optional` flag only for the directly visited package, and do not automatically change `optional` for transitive descendants. pnpm corrects stale optional flags later via `copyDependencySubGraph` (a BFS in `lockfile/pruner/src/index.ts`/`copyDependencySubGraph`). Until pacquet has an equivalent pruner/copy-subgraph step, be aware that the raw `node.optional` can flow through to the lockfile/virtual store via `dependencies_graph_to_lockfile.rs` → `create_virtual_store.rs` → `installability.rs`, so review changes that affect optional flag propagation for this mismatch.

Applied to files:

  • pacquet/crates/resolving-deps-resolver/src/resolve_importer/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/lib.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs
  • pacquet/crates/resolving-deps-resolver/src/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs
📚 Learning: 2026-05-24T21:10:50.292Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11915
File: pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs:553-617
Timestamp: 2026-05-24T21:10:50.292Z
Learning: In pacquet’s Rust resolver layer, the behavior of `ResolvedPackage.optional` “AND-folding”/revisit updates is intentionally aligned with pnpm: when a package is directly revisited, only that package’s `optional` flag is updated; transitive descendants are not re-walked/rescored in this stage. pnpm later corrects any stale optional flags downstream via its pruner/BFS logic (`copyDependencySubGraph` + `nonOptional` reachability stamping in the lockfile pruner). Since pacquet does not yet have that pruner equivalent, review should NOT flag the downstream/stale optional propagation as a bug in pacquet PRs; treat the resolver-layer optional propagation gap as expected parity until the pruning/stamping step is ported.

Applied to files:

  • pacquet/crates/resolving-deps-resolver/src/resolve_importer/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/lib.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs
  • pacquet/crates/resolving-deps-resolver/src/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs
📚 Learning: 2026-05-20T23:23:40.606Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11788
File: pacquet/crates/lockfile/src/snapshot_dep_ref.rs:52-56
Timestamp: 2026-05-20T23:23:40.606Z
Learning: In the pacquet lockfile crate, the link target is intentionally represented as a raw `Link(String)` (not a `LinkTarget` newtype) for both `SnapshotDepRef::Link` and `ImporterDepVersion::Link`. pnpm upstream preserves `link:` targets verbatim with only a prefix check (e.g., `refToRelative`), so do not suggest introducing a `LinkTarget` newtype in a bug-fix/targeted PR. If type branding is ever desired, it should be done together for both variants in a separate dedicated refactor PR rather than as part of a small fix.

Applied to files:

  • pacquet/crates/lockfile/src/lib.rs
  • pacquet/crates/lockfile/src/freshness.rs
  • pacquet/crates/lockfile/src/freshness/tests.rs
🔇 Additional comments (32)
pacquet/crates/package-manager/src/lib.rs (1)

27-27: LGTM!

Also applies to: 62-62

pacquet/crates/graph-hasher/src/lib.rs (1)

26-29: LGTM!

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

88-88: LGTM!

pacquet/crates/resolving-deps-resolver/src/resolve_importer/tests.rs (1)

95-95: LGTM!

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

71-71: LGTM!

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

59-59: LGTM!

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

873-935: LGTM!

pacquet/crates/graph-hasher/src/object_hasher.rs (1)

44-47: ⚡ Quick win

Preserve pnpm nullable/hash semantics for empty arrays in hash_object_nullable_with_prefix

is_nullish currently treats only Value::Null and empty Value::Object as nullish; pnpm’s hashObjectNullableWithPrefix short-circuits on “empty” inputs such that an empty array ([]) follows the same nullable/undefined path, so Value::Array([]) should also be treated as nullish (return None).

🔧 Proposed fix
     let is_nullish = match value {
         Value::Null => true,
+        Value::Array(arr) => arr.is_empty(),
         Value::Object(map) => map.is_empty(),
         _ => false,
     };
pacquet/crates/graph-hasher/src/tests.rs (1)

1-4: LGTM!

Also applies to: 155-179

pacquet/crates/lockfile/src/lib.rs (1)

79-94: LGTM!

pacquet/plans/TEST_PORTING.md (1)

380-381: LGTM!

Also applies to: 751-753

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

78-78: LGTM!

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

38-39: LGTM!

Also applies to: 882-897

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

1061-1066: ⚡ Quick win

Do not treat this .ok() as hiding a real drift-detection failure
Config::package_extensions is serialized from pacquet/crates/config/src/workspace_yaml.rs’s PackageExtension / PeerDependencyMeta, both of which derive serde::Serialize and only contain Option<BTreeMap<String, String>> and Option<bool> (no custom Serialize impls and no float/non-finite cases). With this type shape, serde_json::to_value(extensions) has no meaningful fallible path, so mapping its Err to None won’t incorrectly mark stale lockfile settings as fresh.

			> Likely an incorrect or invalid review comment.
pacquet/crates/package-manager/Cargo.toml (1)

14-51: LGTM!

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

167-167: LGTM!

pacquet/crates/lockfile/src/freshness.rs (1)

103-114: LGTM!

Also applies to: 208-241

pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs (1)

99-103: LGTM!

Also applies to: 105-120, 165-171

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

17-197: LGTM!

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

76-83: LGTM!

Also applies to: 110-111, 131-132

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

323-337: LGTM!

Also applies to: 339-374, 477-477, 685-691

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

210-213: LGTM!

Also applies to: 249-271, 303-306

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

1023-1023: LGTM!

Also applies to: 1040-1040, 1495-1495, 1533-1533, 1673-1673

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

43-43: LGTM!

Also applies to: 209-209, 229-229, 874-874, 996-996, 1147-1147

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

100-103: ⚡ Quick win

Add prerelease-tolerant range matching for pnpm parity

Range::satisfies (node-semver-style behavior) rejects prerelease versions by default, so the current if !range.satisfies(version) { continue; } will skip extensions for prerelease package versions.
Align this callsite with pnpm’s exact prerelease-matching rules (the earlier “strip to MAJOR.MINOR.PATCH and retry” behavior needs to match pnpm precisely, since node-semver’s default satisfies behavior doesn’t provide an equivalent toggle here).

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

119-119: LGTM!

Also applies to: 187-187, 271-271, 311-311, 354-354, 432-432, 473-473, 515-515, 559-559, 599-599, 650-650, 699-699, 751-751, 874-874, 952-952, 1000-1000, 1096-1096, 1190-1190, 1296-1296, 1364-1364, 1473-1473, 1580-1580, 1674-1674, 1721-1721, 1754-1754, 1803-1803, 1872-1872, 1926-1926, 1990-1990, 2042-2042

pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs (1)

57-67: LGTM!

Also applies to: 69-77, 79-91, 190-192, 298-302, 319-319, 335-341, 488-488, 492-492, 497-509

pacquet/crates/lockfile/src/freshness/tests.rs (1)

594-595: LGTM!

Also applies to: 611-611, 624-624, 644-644, 667-667, 670-670, 688-688, 702-702, 720-720, 738-738, 747-841, 860-860

pacquet/crates/real-hoist/src/tests.rs (1)

37-37: LGTM!

Also applies to: 66-66, 117-117, 170-170, 248-248, 319-319, 358-358, 411-411, 515-515, 598-598, 663-663, 739-739, 788-788, 841-841, 895-895, 946-946, 1000-1000, 1037-1037, 1081-1081

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

428-482: LGTM!

pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs (2)

532-541: LGTM!

Also applies to: 596-596


1063-1063: LGTM!

Also applies to: 1067-1079

Comment thread pacquet/crates/package-manager/src/install/tests.rs Outdated
Comment thread pacquet/crates/package-manager/src/package_extender.rs Outdated

@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.

Actionable comments posted: 0

🧹 Nitpick comments (2)
pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs (1)

1135-1142: ⚡ Quick win

Shorten these test doc comments to avoid duplicating test assertions.

The behavior is already fully captured by the test names and assertions; keep only concise intent where needed.

As per coding guidelines, "Tests are documentation. Do not duplicate them in prose."

Also applies to: 1159-1163

🤖 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/package-manager/src/install_with_fresh_lockfile.rs` around
lines 1135 - 1142, The long doc comments above the test about
packageExtensionsChecksum duplicate the test name and assertions; shorten them
to a concise one-line intent (e.g., "Ensure packageExtensions checksum is stable
regardless of Config::package_extensions declaration order") and similarly
replace the other verbose block at 1159-1163 with a brief intent sentence;
update the comment block(s) surrounding the test in
install_with_fresh_lockfile.rs rather than changing test logic or names.
pacquet/crates/workspace-state/src/tests.rs (1)

90-97: ⚡ Quick win

Trim this test doc comment to the non-obvious rationale only.

This block re-describes behavior already asserted in the test body; keep only the “why” and let the test name/assertions document behavior.

As per coding guidelines, "Tests are documentation. Do not duplicate them in prose."

🤖 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/workspace-state/src/tests.rs` around lines 90 - 97, The doc
comment above the test is too verbose and duplicates the test's assertions; edit
the comment in pacquet/crates/workspace-state/src/tests.rs to remove the prose
that re-states behavior (the round-trip description and wire-shape example) and
keep only the non-obvious rationale: that packageExtensions must preserve the
on-disk shape because optimistic_repeat_install::settings_match reads
WorkspaceStateSettings and the drift gate requires byte-comparable output with
pnpm's writer; reference packageExtensions and WorkspaceStateSettings in the
trimmed comment so the intent remains clear.
🤖 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/package-manager/src/install_with_fresh_lockfile.rs`:
- Around line 1135-1142: The long doc comments above the test about
packageExtensionsChecksum duplicate the test name and assertions; shorten them
to a concise one-line intent (e.g., "Ensure packageExtensions checksum is stable
regardless of Config::package_extensions declaration order") and similarly
replace the other verbose block at 1159-1163 with a brief intent sentence;
update the comment block(s) surrounding the test in
install_with_fresh_lockfile.rs rather than changing test logic or names.

In `@pacquet/crates/workspace-state/src/tests.rs`:
- Around line 90-97: The doc comment above the test is too verbose and
duplicates the test's assertions; edit the comment in
pacquet/crates/workspace-state/src/tests.rs to remove the prose that re-states
behavior (the round-trip description and wire-shape example) and keep only the
non-obvious rationale: that packageExtensions must preserve the on-disk shape
because optimistic_repeat_install::settings_match reads WorkspaceStateSettings
and the drift gate requires byte-comparable output with pnpm's writer; reference
packageExtensions and WorkspaceStateSettings in the trimmed comment so the
intent remains clear.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7cc4e871-83be-454a-b9ef-32a013c70a2b

📥 Commits

Reviewing files that changed from the base of the PR and between 4073080 and 089ba63.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (33)
  • pacquet/crates/config/src/env_overlay.rs
  • pacquet/crates/config/src/lib.rs
  • pacquet/crates/config/src/workspace_yaml.rs
  • pacquet/crates/config/src/workspace_yaml/tests.rs
  • pacquet/crates/graph-hasher/src/lib.rs
  • pacquet/crates/graph-hasher/src/object_hasher.rs
  • pacquet/crates/graph-hasher/src/tests.rs
  • pacquet/crates/lockfile/src/freshness.rs
  • pacquet/crates/lockfile/src/freshness/tests.rs
  • pacquet/crates/lockfile/src/lib.rs
  • pacquet/crates/package-manager/Cargo.toml
  • pacquet/crates/package-manager/src/current_lockfile.rs
  • pacquet/crates/package-manager/src/current_lockfile/tests.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile/tests.rs
  • pacquet/crates/package-manager/src/hoisted_dep_graph.rs
  • pacquet/crates/package-manager/src/install.rs
  • pacquet/crates/package-manager/src/install/tests.rs
  • pacquet/crates/package-manager/src/install_package_from_registry/tests.rs
  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
  • pacquet/crates/package-manager/src/lib.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install/tests.rs
  • pacquet/crates/package-manager/src/package_extender.rs
  • pacquet/crates/package-manager/src/package_extender/tests.rs
  • pacquet/crates/real-hoist/src/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/lib.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/tests.rs
  • pacquet/crates/workspace-state/src/tests.rs
  • pacquet/plans/TEST_PORTING.md
✅ Files skipped from review due to trivial changes (7)
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer/tests.rs
  • pacquet/crates/package-manager/Cargo.toml
  • pacquet/crates/package-manager/src/hoisted_dep_graph.rs
  • pacquet/crates/package-manager/src/current_lockfile/tests.rs
  • pacquet/plans/TEST_PORTING.md
  • pacquet/crates/real-hoist/src/tests.rs
  • pacquet/crates/config/src/workspace_yaml/tests.rs
🚧 Files skipped from review as they are similar to previous changes (22)
  • pacquet/crates/package-manager/src/install_package_from_registry/tests.rs
  • pacquet/crates/graph-hasher/src/lib.rs
  • pacquet/crates/package-manager/src/lib.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs
  • pacquet/crates/config/src/env_overlay.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install.rs
  • pacquet/crates/resolving-deps-resolver/src/lib.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install/tests.rs
  • pacquet/crates/graph-hasher/src/tests.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile/tests.rs
  • pacquet/crates/config/src/lib.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile.rs
  • pacquet/crates/package-manager/src/current_lockfile.rs
  • pacquet/crates/package-manager/src/install.rs
  • pacquet/crates/graph-hasher/src/object_hasher.rs
  • pacquet/crates/lockfile/src/lib.rs
  • pacquet/crates/lockfile/src/freshness.rs
  • pacquet/crates/lockfile/src/freshness/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs
  • pacquet/crates/package-manager/src/package_extender.rs
  • pacquet/crates/resolving-deps-resolver/src/tests.rs
  • pacquet/crates/package-manager/src/install/tests.rs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
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/workspace-state/src/tests.rs
  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
  • pacquet/crates/config/src/workspace_yaml.rs
  • pacquet/crates/package-manager/src/package_extender/tests.rs
🧠 Learnings (3)
📚 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/workspace-state/src/tests.rs
  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
  • pacquet/crates/config/src/workspace_yaml.rs
  • pacquet/crates/package-manager/src/package_extender/tests.rs
📚 Learning: 2026-05-22T00:08:44.646Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11837
File: pacquet/crates/resolving-npm-resolver/src/pick_package.rs:33-51
Timestamp: 2026-05-22T00:08:44.646Z
Learning: In the pnpm/pnpm repo’s pacquet Rust crates, do not flag Unicode ellipsis characters (U+2026, `…`) in Rust doc comments (`///` / `/** */`) as a lint violation. The pacquet crate’s `dylint.toml` only enables `perfectionist::derive_ordering`, and the Dylint `unicode-ellipsis` rule is not enabled for this project—so `…` in doc comments is an intentional, repo-consistent style.

Applied to files:

  • pacquet/crates/workspace-state/src/tests.rs
  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
  • pacquet/crates/config/src/workspace_yaml.rs
  • pacquet/crates/package-manager/src/package_extender/tests.rs
📚 Learning: 2026-05-20T23:07:58.444Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11784
File: pacquet/crates/resolving-deps-resolver/src/hoist_peers.rs:120-133
Timestamp: 2026-05-20T23:07:58.444Z
Learning: When reviewing code in this pacquet Rust port, follow the upstream pnpm compatibility rule: only match pnpm’s behavior exactly. Do not propose review changes that intentionally deviate from pnpm’s documented/observed behavior, even if pnpm appears buggy. If you identify a real bug in pnpm behavior, the review should prioritize fixing it upstream in pnpm first, and avoid implementing a pnpm-behavior workaround here unless the same fix has already landed upstream.

Applied to files:

  • pacquet/crates/workspace-state/src/tests.rs
  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
  • pacquet/crates/config/src/workspace_yaml.rs
  • pacquet/crates/package-manager/src/package_extender/tests.rs
🔇 Additional comments (4)
pacquet/crates/workspace-state/src/tests.rs (1)

98-123: LGTM!

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

532-542: LGTM!

Also applies to: 596-596, 1063-1063, 1067-1079

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

323-337: LGTM!

Also applies to: 339-374, 477-477, 685-691

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

1-222: LGTM!

@zkochan zkochan force-pushed the pkg-ext branch 2 times, most recently from 4033560 to 335c025 Compare May 28, 2026 17:35

@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.

Actionable comments posted: 3

🤖 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.

Inline comments:
In `@pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs`:
- Around line 1081-1085: compute_package_extensions_checksum currently
serializes and hashes an empty map as Some(hash) instead of returning None;
change the function to check whether config.package_extensions is an empty map
(after serde_json::to_value) and return None when it's empty, otherwise proceed
to hash with pacquet_graph_hasher::hash_object_nullable_with_prefix; apply the
same empty-map guard to the analogous checksum helper later in the file and add
a unit test that asserts an explicit empty map yields None placed next to the
existing absent-map test.

In `@pacquet/crates/package-manager/src/install.rs`:
- Around line 1061-1070: The current code treats any Some(package_extensions)
(including an empty object) as present and hashes it; change the logic so
package_extensions is considered absent if it's empty before hashing: compute
serde_json::to_value(extensions).ok(), then if the resulting Value is an Object
or Array check emptiness (treat empty Object/Array as None) and only call
pacquet_graph_hasher::hash_object_nullable_with_prefix when the serde Value is
non-empty; keep the final name package_extensions_checksum and pass its
as_deref() into pacquet_lockfile::check_lockfile_settings as before.

In `@pacquet/crates/package-manager/src/package_extender.rs`:
- Around line 48-55: The struct field `range: Option<Range>` currently conflates
"no selector range" and "parse failure", causing parse errors to behave as
match-all; change the representation so parse failures are preserved (e.g. make
`range` an Option<Result<Range, _>> or a small enum like {Unspecified,
Parsed(Range), ParseError}), set it to ParseError when semver parsing fails in
the parser (the code at the current parse site that collapses failures to None),
and then update the selector-matching logic (the code paths that inspect `range`
around the current match blocks at ~100-106 and ~135-138) to treat ParseError as
"no match" while keeping Unspecified as the unconditional-apply case. Ensure all
places that previously assumed Option<Range> are adjusted to handle the new
variant.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c091aa8c-9238-4def-aefd-206f665a0751

📥 Commits

Reviewing files that changed from the base of the PR and between 089ba63 and 4033560.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (33)
  • pacquet/crates/config/src/env_overlay.rs
  • pacquet/crates/config/src/lib.rs
  • pacquet/crates/config/src/workspace_yaml.rs
  • pacquet/crates/config/src/workspace_yaml/tests.rs
  • pacquet/crates/graph-hasher/src/lib.rs
  • pacquet/crates/graph-hasher/src/object_hasher.rs
  • pacquet/crates/graph-hasher/src/tests.rs
  • pacquet/crates/lockfile/src/freshness.rs
  • pacquet/crates/lockfile/src/freshness/tests.rs
  • pacquet/crates/lockfile/src/lib.rs
  • pacquet/crates/package-manager/Cargo.toml
  • pacquet/crates/package-manager/src/current_lockfile.rs
  • pacquet/crates/package-manager/src/current_lockfile/tests.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile/tests.rs
  • pacquet/crates/package-manager/src/hoisted_dep_graph.rs
  • pacquet/crates/package-manager/src/install.rs
  • pacquet/crates/package-manager/src/install/tests.rs
  • pacquet/crates/package-manager/src/install_package_from_registry/tests.rs
  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
  • pacquet/crates/package-manager/src/lib.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install/tests.rs
  • pacquet/crates/package-manager/src/package_extender.rs
  • pacquet/crates/package-manager/src/package_extender/tests.rs
  • pacquet/crates/real-hoist/src/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/lib.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/tests.rs
  • pacquet/crates/workspace-state/src/tests.rs
  • pacquet/plans/TEST_PORTING.md
✅ Files skipped from review due to trivial changes (3)
  • pacquet/crates/package-manager/Cargo.toml
  • pacquet/crates/package-manager/src/hoisted_dep_graph.rs
  • pacquet/plans/TEST_PORTING.md
🚧 Files skipped from review as they are similar to previous changes (23)
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/lib.rs
  • pacquet/crates/graph-hasher/src/object_hasher.rs
  • pacquet/crates/package-manager/src/install_package_from_registry/tests.rs
  • pacquet/crates/package-manager/src/current_lockfile/tests.rs
  • pacquet/crates/config/src/workspace_yaml/tests.rs
  • pacquet/crates/workspace-state/src/tests.rs
  • pacquet/crates/lockfile/src/lib.rs
  • pacquet/crates/package-manager/src/install/tests.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile.rs
  • pacquet/crates/lockfile/src/freshness.rs
  • pacquet/crates/package-manager/src/current_lockfile.rs
  • pacquet/crates/graph-hasher/src/lib.rs
  • pacquet/crates/package-manager/src/package_extender/tests.rs
  • pacquet/crates/real-hoist/src/tests.rs
  • pacquet/crates/graph-hasher/src/tests.rs
  • pacquet/crates/config/src/workspace_yaml.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install.rs
  • pacquet/crates/lockfile/src/freshness/tests.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile/tests.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
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/package-manager/src/lib.rs
  • pacquet/crates/config/src/env_overlay.rs
  • pacquet/crates/package-manager/src/install.rs
  • pacquet/crates/config/src/lib.rs
  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs
  • pacquet/crates/package-manager/src/package_extender.rs
🧠 Learnings (5)
📚 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/package-manager/src/lib.rs
  • pacquet/crates/config/src/env_overlay.rs
  • pacquet/crates/package-manager/src/install.rs
  • pacquet/crates/config/src/lib.rs
  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs
  • pacquet/crates/package-manager/src/package_extender.rs
📚 Learning: 2026-05-22T00:08:44.646Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11837
File: pacquet/crates/resolving-npm-resolver/src/pick_package.rs:33-51
Timestamp: 2026-05-22T00:08:44.646Z
Learning: In the pnpm/pnpm repo’s pacquet Rust crates, do not flag Unicode ellipsis characters (U+2026, `…`) in Rust doc comments (`///` / `/** */`) as a lint violation. The pacquet crate’s `dylint.toml` only enables `perfectionist::derive_ordering`, and the Dylint `unicode-ellipsis` rule is not enabled for this project—so `…` in doc comments is an intentional, repo-consistent style.

Applied to files:

  • pacquet/crates/package-manager/src/lib.rs
  • pacquet/crates/config/src/env_overlay.rs
  • pacquet/crates/package-manager/src/install.rs
  • pacquet/crates/config/src/lib.rs
  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs
  • pacquet/crates/package-manager/src/package_extender.rs
📚 Learning: 2026-05-20T23:07:58.444Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11784
File: pacquet/crates/resolving-deps-resolver/src/hoist_peers.rs:120-133
Timestamp: 2026-05-20T23:07:58.444Z
Learning: When reviewing code in this pacquet Rust port, follow the upstream pnpm compatibility rule: only match pnpm’s behavior exactly. Do not propose review changes that intentionally deviate from pnpm’s documented/observed behavior, even if pnpm appears buggy. If you identify a real bug in pnpm behavior, the review should prioritize fixing it upstream in pnpm first, and avoid implementing a pnpm-behavior workaround here unless the same fix has already landed upstream.

Applied to files:

  • pacquet/crates/package-manager/src/lib.rs
  • pacquet/crates/config/src/env_overlay.rs
  • pacquet/crates/package-manager/src/install.rs
  • pacquet/crates/config/src/lib.rs
  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs
  • pacquet/crates/package-manager/src/package_extender.rs
📚 Learning: 2026-05-24T21:10:50.292Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11915
File: pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs:553-617
Timestamp: 2026-05-24T21:10:50.292Z
Learning: When implementing dependency “revisit”/optional-folding in pacquet, mirror pnpm’s `resolveDependencies.ts` behavior: on revisit, update the `optional` flag only for the directly visited package, and do not automatically change `optional` for transitive descendants. pnpm corrects stale optional flags later via `copyDependencySubGraph` (a BFS in `lockfile/pruner/src/index.ts`/`copyDependencySubGraph`). Until pacquet has an equivalent pruner/copy-subgraph step, be aware that the raw `node.optional` can flow through to the lockfile/virtual store via `dependencies_graph_to_lockfile.rs` → `create_virtual_store.rs` → `installability.rs`, so review changes that affect optional flag propagation for this mismatch.

Applied to files:

  • pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs
📚 Learning: 2026-05-24T21:10:50.292Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11915
File: pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs:553-617
Timestamp: 2026-05-24T21:10:50.292Z
Learning: In pacquet’s Rust resolver layer, the behavior of `ResolvedPackage.optional` “AND-folding”/revisit updates is intentionally aligned with pnpm: when a package is directly revisited, only that package’s `optional` flag is updated; transitive descendants are not re-walked/rescored in this stage. pnpm later corrects any stale optional flags downstream via its pruner/BFS logic (`copyDependencySubGraph` + `nonOptional` reachability stamping in the lockfile pruner). Since pacquet does not yet have that pruner equivalent, review should NOT flag the downstream/stale optional propagation as a bug in pacquet PRs; treat the resolver-layer optional propagation gap as expected parity until the pruning/stamping step is ported.

Applied to files:

  • pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs
🔇 Additional comments (4)
pacquet/crates/package-manager/src/lib.rs (1)

27-27: LGTM!

Also applies to: 62-62

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

141-141: LGTM!

Also applies to: 168-168

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

38-39: LGTM!

Also applies to: 531-538, 891-906

pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs (1)

99-123: LGTM!

Also applies to: 125-145, 189-205, 247-248, 327-329

Comment thread pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
Comment thread pacquet/crates/package-manager/src/install.rs
Comment thread pacquet/crates/package-manager/src/package_extender.rs Outdated

@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.

Actionable comments posted: 0

🧹 Nitpick comments (1)
pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs (1)

1142-1149: ⚡ Quick win

Use regular comments (or remove) instead of narrative /// on tests.

These /// blocks restate test behavior and read like body narration. Prefer concise // intent comments (or no comment) to keep tests self-documenting per repo rules.

♻️ Proposed change
-    /// Ports `installing/.../packageExtensions.ts:103-153`
-    /// `packageExtensionsChecksum does not change regardless of keys
-    /// order` — two `Config::package_extensions` populated with the
-    /// same selectors and entries in a different declared order must
-    /// produce the same `sha256-…` lockfile checksum. Without the
-    /// sorted-keys hash, the order-sensitive `IndexMap` iteration
-    /// would flap the checksum and force a redundant full resolution
-    /// on every reorder.
+    // Keep parity with upstream: outer key order must not affect checksum.
@@
-    /// Empty / absent extensions round-trip to `None`, matching pnpm's
-    /// `hashObjectNullableWithPrefix(undefined) === undefined`
-    /// short-circuit. Without this, an absent `packageExtensions` and
-    /// a configured-but-empty one would write different lockfile
-    /// fields and the drift gate would fire on no-op installs.
+    // Match pnpm behavior: absent extensions should not emit a checksum.

As per coding guidelines: "Tests are documentation. Do not duplicate them in prose." and "Doc comments (///, //!) document the contract ... They are not a re-narration of the body."

Also applies to: 1166-1170

🤖 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/package-manager/src/install_with_fresh_lockfile.rs` around
lines 1142 - 1149, Replace the narrative doc-comment blocks (lines starting with
"///") that describe the test behavior—such as the block referencing
"packageExtensionsChecksum does not change regardless of keys order" and the
similar block at the other occurrence—with regular non-doc comments (//) or
remove them; locate the test that mentions packageExtensionsChecksum and change
the leading triple-slash comments to ordinary single-line comments (or delete)
so the test remains self-documenting and follows repo guidelines.
🤖 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/package-manager/src/install_with_fresh_lockfile.rs`:
- Around line 1142-1149: Replace the narrative doc-comment blocks (lines
starting with "///") that describe the test behavior—such as the block
referencing "packageExtensionsChecksum does not change regardless of keys order"
and the similar block at the other occurrence—with regular non-doc comments (//)
or remove them; locate the test that mentions packageExtensionsChecksum and
change the leading triple-slash comments to ordinary single-line comments (or
delete) so the test remains self-documenting and follows repo guidelines.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 27656d3c-df34-483e-a198-425f76b2d50c

📥 Commits

Reviewing files that changed from the base of the PR and between 4033560 and 335c025.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (33)
  • pacquet/crates/config/src/env_overlay.rs
  • pacquet/crates/config/src/lib.rs
  • pacquet/crates/config/src/workspace_yaml.rs
  • pacquet/crates/config/src/workspace_yaml/tests.rs
  • pacquet/crates/graph-hasher/src/lib.rs
  • pacquet/crates/graph-hasher/src/object_hasher.rs
  • pacquet/crates/graph-hasher/src/tests.rs
  • pacquet/crates/lockfile/src/freshness.rs
  • pacquet/crates/lockfile/src/freshness/tests.rs
  • pacquet/crates/lockfile/src/lib.rs
  • pacquet/crates/package-manager/Cargo.toml
  • pacquet/crates/package-manager/src/current_lockfile.rs
  • pacquet/crates/package-manager/src/current_lockfile/tests.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile/tests.rs
  • pacquet/crates/package-manager/src/hoisted_dep_graph.rs
  • pacquet/crates/package-manager/src/install.rs
  • pacquet/crates/package-manager/src/install/tests.rs
  • pacquet/crates/package-manager/src/install_package_from_registry/tests.rs
  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
  • pacquet/crates/package-manager/src/lib.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install/tests.rs
  • pacquet/crates/package-manager/src/package_extender.rs
  • pacquet/crates/package-manager/src/package_extender/tests.rs
  • pacquet/crates/real-hoist/src/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/lib.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/tests.rs
  • pacquet/crates/workspace-state/src/tests.rs
  • pacquet/plans/TEST_PORTING.md
✅ Files skipped from review due to trivial changes (5)
  • pacquet/crates/package-manager/src/hoisted_dep_graph.rs
  • pacquet/crates/package-manager/Cargo.toml
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile/tests.rs
  • pacquet/plans/TEST_PORTING.md
  • pacquet/crates/real-hoist/src/tests.rs
🚧 Files skipped from review as they are similar to previous changes (27)
  • pacquet/crates/package-manager/src/current_lockfile.rs
  • pacquet/crates/package-manager/src/current_lockfile/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer/tests.rs
  • pacquet/crates/graph-hasher/src/object_hasher.rs
  • pacquet/crates/config/src/env_overlay.rs
  • pacquet/crates/config/src/lib.rs
  • pacquet/crates/graph-hasher/src/tests.rs
  • pacquet/crates/lockfile/src/lib.rs
  • pacquet/crates/resolving-deps-resolver/src/lib.rs
  • pacquet/crates/config/src/workspace_yaml/tests.rs
  • pacquet/crates/package-manager/src/lib.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs
  • pacquet/crates/resolving-deps-resolver/src/tests.rs
  • pacquet/crates/package-manager/src/install_package_from_registry/tests.rs
  • pacquet/crates/lockfile/src/freshness.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install.rs
  • pacquet/crates/package-manager/src/package_extender/tests.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install/tests.rs
  • pacquet/crates/graph-hasher/src/lib.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile.rs
  • pacquet/crates/package-manager/src/install/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs
  • pacquet/crates/package-manager/src/install.rs
  • pacquet/crates/lockfile/src/freshness/tests.rs
  • pacquet/crates/workspace-state/src/tests.rs
  • pacquet/crates/package-manager/src/package_extender.rs
  • pacquet/crates/config/src/workspace_yaml.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). (5)
  • GitHub Check: Run benchmark on ubuntu-latest
  • GitHub Check: Code Coverage
  • GitHub Check: Lint and Test (macos-latest)
  • GitHub Check: Lint and Test (windows-latest)
  • GitHub Check: Compile & Lint
🧰 Additional context used
📓 Path-based instructions (1)
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/package-manager/src/install_with_fresh_lockfile.rs
🧠 Learnings (3)
📚 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/package-manager/src/install_with_fresh_lockfile.rs
📚 Learning: 2026-05-22T00:08:44.646Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11837
File: pacquet/crates/resolving-npm-resolver/src/pick_package.rs:33-51
Timestamp: 2026-05-22T00:08:44.646Z
Learning: In the pnpm/pnpm repo’s pacquet Rust crates, do not flag Unicode ellipsis characters (U+2026, `…`) in Rust doc comments (`///` / `/** */`) as a lint violation. The pacquet crate’s `dylint.toml` only enables `perfectionist::derive_ordering`, and the Dylint `unicode-ellipsis` rule is not enabled for this project—so `…` in doc comments is an intentional, repo-consistent style.

Applied to files:

  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
📚 Learning: 2026-05-20T23:07:58.444Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11784
File: pacquet/crates/resolving-deps-resolver/src/hoist_peers.rs:120-133
Timestamp: 2026-05-20T23:07:58.444Z
Learning: When reviewing code in this pacquet Rust port, follow the upstream pnpm compatibility rule: only match pnpm’s behavior exactly. Do not propose review changes that intentionally deviate from pnpm’s documented/observed behavior, even if pnpm appears buggy. If you identify a real bug in pnpm behavior, the review should prioritize fixing it upstream in pnpm first, and avoid implementing a pnpm-behavior workaround here unless the same fix has already landed upstream.

Applied to files:

  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
🔇 Additional comments (1)
pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs (1)

532-541: LGTM!

Also applies to: 598-607, 1070-1086, 1117-1140, 1150-1164, 1171-1176

@zkochan zkochan force-pushed the pkg-ext branch 2 times, most recently from 8d39f52 to 8e87f7c Compare May 28, 2026 18:06

@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.

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@pacquet/crates/package-manager/src/package_extender.rs`:
- Around line 79-84: The code currently maps parse failures from
parsed.bare_specifier.parse() to RangeFilter::Unparsable and entry_matches
treats that as always false, which silently drops malformed name@range
selectors; change the behavior so that parse errors do not convert to a silent
non-match—either store the original raw range string (e.g., add a
RangeFilter::Raw(String) variant) or, when matching in entry_matches, pass the
raw range_str into the semver satisfier call (semver::satisfies / equivalent) so
any TypeError from node-semver/semver surfaces instead of being swallowed;
update RangeFilter and entry_matches to use the raw range on parse failure (or
re-throw/propgate the parse error) and ensure parsed.bare_specifier handling
preserves the original string for that fallback.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e915362b-d338-48a2-8c53-bc14cc2f2b4b

📥 Commits

Reviewing files that changed from the base of the PR and between 335c025 and 8d39f52.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (33)
  • pacquet/crates/config/src/env_overlay.rs
  • pacquet/crates/config/src/lib.rs
  • pacquet/crates/config/src/workspace_yaml.rs
  • pacquet/crates/config/src/workspace_yaml/tests.rs
  • pacquet/crates/graph-hasher/src/lib.rs
  • pacquet/crates/graph-hasher/src/object_hasher.rs
  • pacquet/crates/graph-hasher/src/tests.rs
  • pacquet/crates/lockfile/src/freshness.rs
  • pacquet/crates/lockfile/src/freshness/tests.rs
  • pacquet/crates/lockfile/src/lib.rs
  • pacquet/crates/package-manager/Cargo.toml
  • pacquet/crates/package-manager/src/current_lockfile.rs
  • pacquet/crates/package-manager/src/current_lockfile/tests.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile/tests.rs
  • pacquet/crates/package-manager/src/hoisted_dep_graph.rs
  • pacquet/crates/package-manager/src/install.rs
  • pacquet/crates/package-manager/src/install/tests.rs
  • pacquet/crates/package-manager/src/install_package_from_registry/tests.rs
  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
  • pacquet/crates/package-manager/src/lib.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install/tests.rs
  • pacquet/crates/package-manager/src/package_extender.rs
  • pacquet/crates/package-manager/src/package_extender/tests.rs
  • pacquet/crates/real-hoist/src/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/lib.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/tests.rs
  • pacquet/crates/workspace-state/src/tests.rs
  • pacquet/plans/TEST_PORTING.md
✅ Files skipped from review due to trivial changes (6)
  • pacquet/crates/package-manager/src/install_package_from_registry/tests.rs
  • pacquet/crates/package-manager/Cargo.toml
  • pacquet/crates/config/src/workspace_yaml/tests.rs
  • pacquet/plans/TEST_PORTING.md
  • pacquet/crates/resolving-deps-resolver/src/tests.rs
  • pacquet/crates/real-hoist/src/tests.rs
🚧 Files skipped from review as they are similar to previous changes (22)
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer/tests.rs
  • pacquet/crates/package-manager/src/current_lockfile.rs
  • pacquet/crates/graph-hasher/src/object_hasher.rs
  • pacquet/crates/graph-hasher/src/lib.rs
  • pacquet/crates/package-manager/src/hoisted_dep_graph.rs
  • pacquet/crates/config/src/env_overlay.rs
  • pacquet/crates/package-manager/src/lib.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile.rs
  • pacquet/crates/workspace-state/src/tests.rs
  • pacquet/crates/lockfile/src/freshness.rs
  • pacquet/crates/package-manager/src/dependencies_graph_to_lockfile/tests.rs
  • pacquet/crates/package-manager/src/current_lockfile/tests.rs
  • pacquet/crates/package-manager/src/install.rs
  • pacquet/crates/config/src/workspace_yaml.rs
  • pacquet/crates/config/src/lib.rs
  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
  • pacquet/crates/lockfile/src/freshness/tests.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_importer.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install.rs
  • pacquet/crates/package-manager/src/install/tests.rs
  • pacquet/crates/graph-hasher/src/tests.rs
  • pacquet/crates/package-manager/src/optimistic_repeat_install/tests.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). (5)
  • GitHub Check: Lint and Test (windows-latest)
  • GitHub Check: Cargo Deny
  • GitHub Check: Code Coverage
  • GitHub Check: Analyze (javascript)
  • GitHub Check: Compile & Lint
🧰 Additional context used
📓 Path-based instructions (1)
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-deps-resolver/src/lib.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs
  • pacquet/crates/package-manager/src/package_extender.rs
  • pacquet/crates/package-manager/src/package_extender/tests.rs
  • pacquet/crates/lockfile/src/lib.rs
🧠 Learnings (6)
📚 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-deps-resolver/src/lib.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs
  • pacquet/crates/package-manager/src/package_extender.rs
  • pacquet/crates/package-manager/src/package_extender/tests.rs
  • pacquet/crates/lockfile/src/lib.rs
📚 Learning: 2026-05-22T00:08:44.646Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11837
File: pacquet/crates/resolving-npm-resolver/src/pick_package.rs:33-51
Timestamp: 2026-05-22T00:08:44.646Z
Learning: In the pnpm/pnpm repo’s pacquet Rust crates, do not flag Unicode ellipsis characters (U+2026, `…`) in Rust doc comments (`///` / `/** */`) as a lint violation. The pacquet crate’s `dylint.toml` only enables `perfectionist::derive_ordering`, and the Dylint `unicode-ellipsis` rule is not enabled for this project—so `…` in doc comments is an intentional, repo-consistent style.

Applied to files:

  • pacquet/crates/resolving-deps-resolver/src/lib.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs
  • pacquet/crates/package-manager/src/package_extender.rs
  • pacquet/crates/package-manager/src/package_extender/tests.rs
  • pacquet/crates/lockfile/src/lib.rs
📚 Learning: 2026-05-20T23:07:58.444Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11784
File: pacquet/crates/resolving-deps-resolver/src/hoist_peers.rs:120-133
Timestamp: 2026-05-20T23:07:58.444Z
Learning: When reviewing code in this pacquet Rust port, follow the upstream pnpm compatibility rule: only match pnpm’s behavior exactly. Do not propose review changes that intentionally deviate from pnpm’s documented/observed behavior, even if pnpm appears buggy. If you identify a real bug in pnpm behavior, the review should prioritize fixing it upstream in pnpm first, and avoid implementing a pnpm-behavior workaround here unless the same fix has already landed upstream.

Applied to files:

  • pacquet/crates/resolving-deps-resolver/src/lib.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs
  • pacquet/crates/package-manager/src/package_extender.rs
  • pacquet/crates/package-manager/src/package_extender/tests.rs
  • pacquet/crates/lockfile/src/lib.rs
📚 Learning: 2026-05-24T21:10:50.292Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11915
File: pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs:553-617
Timestamp: 2026-05-24T21:10:50.292Z
Learning: When implementing dependency “revisit”/optional-folding in pacquet, mirror pnpm’s `resolveDependencies.ts` behavior: on revisit, update the `optional` flag only for the directly visited package, and do not automatically change `optional` for transitive descendants. pnpm corrects stale optional flags later via `copyDependencySubGraph` (a BFS in `lockfile/pruner/src/index.ts`/`copyDependencySubGraph`). Until pacquet has an equivalent pruner/copy-subgraph step, be aware that the raw `node.optional` can flow through to the lockfile/virtual store via `dependencies_graph_to_lockfile.rs` → `create_virtual_store.rs` → `installability.rs`, so review changes that affect optional flag propagation for this mismatch.

Applied to files:

  • pacquet/crates/resolving-deps-resolver/src/lib.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs
📚 Learning: 2026-05-24T21:10:50.292Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11915
File: pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs:553-617
Timestamp: 2026-05-24T21:10:50.292Z
Learning: In pacquet’s Rust resolver layer, the behavior of `ResolvedPackage.optional` “AND-folding”/revisit updates is intentionally aligned with pnpm: when a package is directly revisited, only that package’s `optional` flag is updated; transitive descendants are not re-walked/rescored in this stage. pnpm later corrects any stale optional flags downstream via its pruner/BFS logic (`copyDependencySubGraph` + `nonOptional` reachability stamping in the lockfile pruner). Since pacquet does not yet have that pruner equivalent, review should NOT flag the downstream/stale optional propagation as a bug in pacquet PRs; treat the resolver-layer optional propagation gap as expected parity until the pruning/stamping step is ported.

Applied to files:

  • pacquet/crates/resolving-deps-resolver/src/lib.rs
  • pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs
📚 Learning: 2026-05-20T23:23:40.606Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11788
File: pacquet/crates/lockfile/src/snapshot_dep_ref.rs:52-56
Timestamp: 2026-05-20T23:23:40.606Z
Learning: In the pacquet lockfile crate, the link target is intentionally represented as a raw `Link(String)` (not a `LinkTarget` newtype) for both `SnapshotDepRef::Link` and `ImporterDepVersion::Link`. pnpm upstream preserves `link:` targets verbatim with only a prefix check (e.g., `refToRelative`), so do not suggest introducing a `LinkTarget` newtype in a bug-fix/targeted PR. If type branding is ever desired, it should be done together for both variants in a separate dedicated refactor PR rather than as part of a small fix.

Applied to files:

  • pacquet/crates/lockfile/src/lib.rs
🔇 Additional comments (3)
pacquet/crates/resolving-deps-resolver/src/lib.rs (1)

70-73: LGTM!

pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs (1)

57-67: LGTM!

Also applies to: 69-77, 79-90, 190-193, 298-301, 319-320, 335-341, 488-509

pacquet/crates/lockfile/src/lib.rs (1)

79-94: LGTM!

Comment thread pacquet/crates/package-manager/src/package_extender.rs
@zkochan zkochan force-pushed the pkg-ext branch 2 times, most recently from 8ea67ec to f561789 Compare May 28, 2026 19:03
Adds the `packageExtensions` setting end-to-end on the pacquet side:
yaml plumbing, `Config` field, `PNPM_CONFIG_PACKAGE_EXTENSIONS` env
var, a `PackageExtender` hook applied during resolution, and the
workspace-state drift check. Mirrors pnpm's `createPackageExtender`
([1]) merge semantics — manifest fields win over extension fields on
conflict.

The lockfile-side `packageExtensionsChecksum` field (and its
frozen-install drift gate) is intentionally out of scope; see
#12009.

[1]: https://github.com/pnpm/pnpm/blob/39101f5e37/hooks/read-package-hook/src/createPackageExtender.ts

Refs: #12009

---
Written by an agent (Claude Code, claude-opus-4-7).
@zkochan zkochan merged commit 2cca6ab into main May 28, 2026
26 of 28 checks passed
@zkochan zkochan deleted the pkg-ext branch May 28, 2026 19:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants