Skip to content
This repository was archived by the owner on May 14, 2026. It is now read-only.

feat: ignoredOptionalDependencies config + lockfile field + outdated-setting check (#434 slice 7)#507

Merged
zkochan merged 2 commits into
mainfrom
feat-ignored-optional-deps
May 13, 2026
Merged

feat: ignoredOptionalDependencies config + lockfile field + outdated-setting check (#434 slice 7)#507
zkochan merged 2 commits into
mainfrom
feat-ignored-optional-deps

Conversation

@zkochan

@zkochan zkochan commented May 13, 2026

Copy link
Copy Markdown
Member

Summary

Last slice of the #434 umbrella (Proper optionalDependencies support). Adds the user-facing ignoredOptionalDependencies setting: a list of dep-name patterns the user wants entirely excluded from resolution + install.

Mirrors three surfaces at pnpm/pnpm@94240bc046:

Changes

  • pacquet-config: Config::ignored_optional_dependencies: Option<Vec<String>> + WorkspaceSettings field + apply_to wiring.
  • pacquet-lockfile:
    • Lockfile::ignored_optional_dependencies: Option<Vec<String>> top-level field with serde round-trip.
    • check_lockfile_settings(lockfile, config_set) — sorts both sides and compares; sibling to satisfies_package_manifest.
    • New StalenessReason::IgnoredOptionalDependenciesChanged { lockfile, config } variant.
    • satisfies_package_manifest extended with is_ignored_optional: &dyn Fn(&str) -> bool so the manifest-side diff skips ignored names — matches what upstream's read-package-hook does at manifest-read time.
  • pacquet-package-manager::install.rs: builds a matcher from Config::ignored_optional_dependencies (reuses pacquet_config::matcher::create_matcher — same glob engine as hoistPattern), calls check_lockfile_settings before satisfies_package_manifest, threads the matcher closure in.
  • pacquet-package-manager::current_lockfile: preserves the wanted lockfile's ignored_optional_dependencies through the slice 6 filter so the recorded set round-trips into the current lockfile.

Test plan

  • pacquet_config::workspace_yaml::tests::parses_ignored_optional_dependencies_from_yaml_and_applies + omitting_ignored_optional_dependencies_keeps_default.
  • pacquet_lockfile::freshness::check_settings_* — both-empty, sorted-match, drift in both directions. Test-the-test verified by removing the sort+compare guard.
  • pacquet_lockfile::freshness::ignored_optional_filtered_out_of_manifest_diff + ignored_optional_without_filter_surfaces_as_drift (polarity).
  • pacquet_lockfile::freshness::ignored_optional_dependencies_round_trips_through_yaml.
  • just ready (1158 tests pass) + taplo format --check + just dylint + RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --workspace.

Out of scope

  • Resolver-side hook for transitive packages — pacquet has no resolver. The filter applies at the freshness check (importer manifest only), which is what the frozen-install path needs.
  • pacquet add removing ignored entries from package.json at install time — not what upstream does either; the hook only filters what the resolver sees.

Closes #503. Closes the #434 umbrella.


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

Summary by CodeRabbit

  • New Features

    • Add workspace setting to declare ignored optional dependencies; config and lockfile capture this setting.
  • Bug Fixes

    • Validate lockfile freshness against ignored-optional-dependencies and preserve that setting when producing the “current” lockfile and during frozen installs.
  • Tests

    • Expanded tests for parsing/serialization, drift detection, ignore-filter behavior, and dev-dependency exemptions.

Review Change Stack

Copilot AI review requested due to automatic review settings May 13, 2026 22:39
@coderabbitai

coderabbitai Bot commented May 13, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ad8846dc-9caa-402d-bcd2-b685cf5b265b

📥 Commits

Reviewing files that changed from the base of the PR and between b5f696b and 34ed43d.

📒 Files selected for processing (12)
  • crates/config/src/lib.rs
  • crates/config/src/workspace_yaml.rs
  • crates/config/src/workspace_yaml/tests.rs
  • crates/lockfile/src/freshness.rs
  • crates/lockfile/src/freshness/tests.rs
  • crates/lockfile/src/lib.rs
  • crates/package-manager/src/current_lockfile.rs
  • crates/package-manager/src/current_lockfile/tests.rs
  • crates/package-manager/src/hoisted_dep_graph.rs
  • crates/package-manager/src/install.rs
  • crates/package-manager/src/install_package_from_registry/tests.rs
  • crates/real-hoist/src/tests.rs
🚧 Files skipped from review as they are similar to previous changes (10)
  • crates/package-manager/src/install_package_from_registry/tests.rs
  • crates/package-manager/src/current_lockfile/tests.rs
  • crates/lockfile/src/lib.rs
  • crates/config/src/workspace_yaml.rs
  • crates/package-manager/src/hoisted_dep_graph.rs
  • crates/real-hoist/src/tests.rs
  • crates/lockfile/src/freshness.rs
  • crates/package-manager/src/install.rs
  • crates/package-manager/src/current_lockfile.rs
  • crates/lockfile/src/freshness/tests.rs
📜 Recent 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). (7)
  • GitHub Check: copilot-pull-request-reviewer
  • GitHub Check: Lint and Test (windows-latest)
  • GitHub Check: Lint and Test (ubuntu-latest)
  • GitHub Check: Lint and Test (macos-latest)
  • GitHub Check: Run benchmark on ubuntu-latest
  • GitHub Check: Run benchmark on ubuntu-latest
  • GitHub Check: Code Coverage
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Preserve existing method chains and pipe-trait chains; do not break them into intermediate let bindings unless there is a concrete justification such as a compilation failure, borrow checker rejection, meaningful performance improvement, or other technical necessity. Refactoring for style alone is not sufficient justification.
Choose owned vs. borrowed parameters to minimize copies; prefer borrowed types (&Path over &PathBuf, &str over &String) when it does not force extra copies.
Prefer Arc::clone(&x) and Rc::clone(&x) over x.clone() for reference-counted types to make the cost visible at the call site.
Do not use star imports inside module bodies. Write use super::{Foo, bar} instead of use super::*; for any glob whose target is a module you control. External-crate preludes (e.g., use rayon::prelude::*;) and root-of-module re-exports (e.g., pub use submodule::*; in lib.rs) are exceptions.
Follow Rust API Guidelines for naming, as documented in https://rust-lang.github.io/api-guidelines/naming.html.
Declare a newtype wrapper for any branded string type being ported from TypeScript pnpm. Do not collapse the brand into a plain String or &str; give the type its own struct so misuse is a type error.
When porting branded string types where upstream TypeScript always validates before construction, validate in the Rust port too. Construct the wrapper only via TryFrom<String> and/or FromStr; do not provide an infallible public constructor that takes an arbitrary string.
For branded string types where upstream TypeScript never validates (used purely for type-safety to prevent confusion between string slots), expose an infallible From<String> and From<&str> constructor in the Rust wrapper.
When upstream TypeScript occasionally constructs a branded type without validation (via bare as assertion), add a from_str_unchecked (or similarly named) constructor on the Rust side. Keep the validating constructor as well; `from_str_u...

Files:

  • crates/config/src/lib.rs
  • crates/config/src/workspace_yaml/tests.rs
🧠 Learnings (3)
📚 Learning: 2026-05-07T23:19:08.272Z
Learnt from: KSXGitHub
Repo: pnpm/pacquet PR: 401
File: tasks/integrated-benchmark/src/work_env.rs:343-344
Timestamp: 2026-05-07T23:19:08.272Z
Learning: When reviewing Rust code in pnpm/pacquet for deprecated API usage, do not automatically treat `serde_saphyr::to_string` as deprecated. In `serde-saphyr` v0.0.25, `serde_saphyr::to_string` has no `#[deprecated]` attribute (the `#[deprecated]` later in `serde-saphyr-0.0.25/src/lib.rs` applies to a different function). Only flag `serde_saphyr::to_string` as deprecated if the resolved dependency version’s source shows `#[deprecated]` on that specific function.

Applied to files:

  • crates/config/src/lib.rs
  • crates/config/src/workspace_yaml/tests.rs
📚 Learning: 2026-05-13T22:52:32.579Z
Learnt from: zkochan
Repo: pnpm/pacquet PR: 506
File: crates/package-manager/src/link_bins.rs:238-241
Timestamp: 2026-05-13T22:52:32.579Z
Learning: In Rust code using `matches!` (or `match`) on a borrowed value (e.g., `meta: &PackageMetadata` and a field access like `meta.resolution`), it is safe to use wildcard/OR patterns such as `LockfileResolution::Binary(_) | LockfileResolution::Variations(_)` when the pattern does not bind the inner payload by value. `_` wildcard patterns (and similar discriminant-only patterns) should not move non-`Copy` data; they only test the variant and avoid ownership transfer. Do not flag these patterns as move-out-of-borrow/ownership compile errors. Only patterns that bind the inner value by value (e.g., `LockfileResolution::Binary(b)`) would require ownership and may trigger move errors when matching on data behind a borrow.

Applied to files:

  • crates/config/src/lib.rs
  • crates/config/src/workspace_yaml/tests.rs
📚 Learning: 2026-05-01T10:01:33.766Z
Learnt from: zkochan
Repo: pnpm/pacquet PR: 349
File: crates/reporter/src/tests.rs:121-121
Timestamp: 2026-05-01T10:01:33.766Z
Learning: In Rust test code, follow the repo’s CODE_STYLE_GUIDE test-logging rule: add logging (e.g., `eprintln!`/`eprintln!(...)`) so that useful diagnostic values are printed when a test fails, unless the assertion is `assert_eq!` (where the differing values are already included). Concretely, if you use assertions like `assert!`, `assert_ne!`, etc., ensure the test logs the relevant actual/expected values (or context) before/around the assertion so failures can be diagnosed without rerunning.

Applied to files:

  • crates/config/src/workspace_yaml/tests.rs
🔇 Additional comments (2)
crates/config/src/lib.rs (1)

515-530: LGTM!

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

611-651: LGTM!


📝 Walkthrough

Walkthrough

Wires ignoredOptionalDependencies from workspace YAML into Config and top-level Lockfile, adds a lockfile-vs-config staleness check, applies an install-time ignore predicate to freshness comparisons, and updates tests/fixtures and current-lockfile preservation.

Changes

ignoredOptionalDependencies configuration and freshness support

Layer / File(s) Summary
Config parsing and workspace YAML
crates/config/src/lib.rs, crates/config/src/workspace_yaml.rs, crates/config/src/workspace_yaml/tests.rs
Config gains ignored_optional_dependencies: Option<Vec<String>>; WorkspaceSettings deserializes ignoredOptionalDependencies from YAML and apply_to copies it into Config. Tests check presence, order, and absence behavior.
Lockfile field definition
crates/lockfile/src/lib.rs
Lockfile adds top-level ignored_optional_dependencies: Option<Vec<String>> with serde(skip_serializing_if = "Option::is_none"), documented as the v9 wire shape field used for drift detection.
Freshness checks and filtering
crates/lockfile/src/freshness.rs
Adds StalenessReason::IgnoredOptionalDependenciesChanged and check_lockfile_settings to compare lockfile vs config sets; extends satisfies_package_manifest and flat_manifest_specs to accept an is_ignored_optional predicate and filter Prod/Optional entries (Dev remains unfiltered) when computing diffs.
Install-time integration and current-lockfile preservation
crates/package-manager/src/install.rs, crates/package-manager/src/current_lockfile.rs
Frozen-lockfile install path now runs check_lockfile_settings (mapped to OutdatedLockfile on mismatch), builds an is_ignored_optional matcher from Config patterns, and passes it to satisfies_package_manifest. filter_lockfile_for_current preserves the lockfile's ignored_optional_dependencies.
Freshness tests and YAML round-trip
crates/lockfile/src/freshness/tests.rs
Updated existing tests to pass the new predicate; added ignoredOptionalDependencies test suite covering acceptance, order-insensitive equality, drift reporting in both directions, filtering correctness (including polarity test), and YAML round-trip for the Lockfile field.
Test fixture updates
crates/package-manager/src/current_lockfile/tests.rs, crates/package-manager/src/hoisted_dep_graph.rs, crates/package-manager/src/install_package_from_registry/tests.rs, crates/real-hoist/src/tests.rs
Test helpers and many Lockfile literals updated to include ignored_optional_dependencies: None and test config initializers set the new field.

Sequence Diagram(s)

sequenceDiagram
  participant Install
  participant Lockfile
  participant Config
  participant CheckSettings
  participant SatisfiesManifest
  participant FlatSpecs
  Install->>Lockfile: read ignored_optional_dependencies
  Install->>Config: read ignored_optional_dependencies
  Install->>CheckSettings: check_lockfile_settings(lockfile, config)
  CheckSettings->>CheckSettings: normalize & compare sets
  CheckSettings-->>Install: IgnoredOptionalDependenciesChanged on mismatch
  Install->>SatisfiesManifest: satisfies_package_manifest(manifest, is_ignored_optional)
  SatisfiesManifest->>FlatSpecs: flat_manifest_specs(manifest, is_ignored_optional)
  FlatSpecs->>FlatSpecs: skip Prod/Optional entries matching predicate
  FlatSpecs-->>SatisfiesManifest: filtered specifier map
  SatisfiesManifest-->>Install: drift reason or success
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

  • pnpm/pacquet#495: Prior slice that preserved settings through current-lockfile filtering; this PR builds on that work.
  • pnpm/pacquet#450: Related changes to frozen-lockfile freshness machinery; both modify crates/lockfile/src/freshness.rs.

Poem

🐰 I nibble YAML, patterns in tow,
I hop through lockfiles, tidy the row,
Optional things quietly ignored,
Freshness checks hum, no drift restored,
A rabbit's work—small, sure, and slow.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding ignoredOptionalDependencies config, lockfile field, and outdated-setting check support.
Description check ✅ Passed The description is comprehensive, covering summary, changes to each crate, test plan with specific test names, out-of-scope clarification, and upstream references per template requirements.
Linked Issues check ✅ Passed The PR implementation fully addresses all objectives from issue #503: Config field addition, Lockfile field with serde, manifest filtering logic, install-time application, outdated-setting check, and current-lockfile preservation are all present and tested.
Out of Scope Changes check ✅ Passed All code changes are within scope of issue #503 and the linked PR objectives; no unrelated changes to other modules or features were introduced.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-ignored-optional-deps

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@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 `@crates/lockfile/src/freshness.rs`:
- Around line 294-315: The predicate that filters out
ignoredOptionalDependencies is being applied to every DependencyGroup (including
Dev) during both the flat-spec pass (where manifest_prod/manifest_optional are
built) and the per-field pass inside the loop; restrict the
is_ignored_optional(name) filter so it only runs for DependencyGroup::Prod and
DependencyGroup::Optional (i.e., apply the filter when field ==
DependencyGroup::Prod || field == DependencyGroup::Optional and likewise when
building manifest_prod/manifest_optional) so devDependencies are not filtered,
and add a regression test that exercises adding/removing a dev-only dependency
that matches ignoredOptionalDependencies to ensure the freshness check now flags
drift correctly.
🪄 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: 9ff0b803-f1ff-4e79-a93d-23f52a4760ac

📥 Commits

Reviewing files that changed from the base of the PR and between 65f9e66 and f8b9607.

📒 Files selected for processing (12)
  • crates/config/src/lib.rs
  • crates/config/src/workspace_yaml.rs
  • crates/config/src/workspace_yaml/tests.rs
  • crates/lockfile/src/freshness.rs
  • crates/lockfile/src/freshness/tests.rs
  • crates/lockfile/src/lib.rs
  • crates/package-manager/src/current_lockfile.rs
  • crates/package-manager/src/current_lockfile/tests.rs
  • crates/package-manager/src/hoisted_dep_graph.rs
  • crates/package-manager/src/install.rs
  • crates/package-manager/src/install_package_from_registry/tests.rs
  • crates/real-hoist/src/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). (7)
  • GitHub Check: copilot-pull-request-reviewer
  • GitHub Check: Lint and Test (macos-latest)
  • GitHub Check: Lint and Test (ubuntu-latest)
  • GitHub Check: Lint and Test (windows-latest)
  • GitHub Check: Code Coverage
  • GitHub Check: Run benchmark on ubuntu-latest
  • GitHub Check: Run benchmark on ubuntu-latest
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Preserve existing method chains and pipe-trait chains; do not break them into intermediate let bindings unless there is a concrete justification such as a compilation failure, borrow checker rejection, meaningful performance improvement, or other technical necessity. Refactoring for style alone is not sufficient justification.
Choose owned vs. borrowed parameters to minimize copies; prefer borrowed types (&Path over &PathBuf, &str over &String) when it does not force extra copies.
Prefer Arc::clone(&x) and Rc::clone(&x) over x.clone() for reference-counted types to make the cost visible at the call site.
Do not use star imports inside module bodies. Write use super::{Foo, bar} instead of use super::*; for any glob whose target is a module you control. External-crate preludes (e.g., use rayon::prelude::*;) and root-of-module re-exports (e.g., pub use submodule::*; in lib.rs) are exceptions.
Follow Rust API Guidelines for naming, as documented in https://rust-lang.github.io/api-guidelines/naming.html.
Declare a newtype wrapper for any branded string type being ported from TypeScript pnpm. Do not collapse the brand into a plain String or &str; give the type its own struct so misuse is a type error.
When porting branded string types where upstream TypeScript always validates before construction, validate in the Rust port too. Construct the wrapper only via TryFrom<String> and/or FromStr; do not provide an infallible public constructor that takes an arbitrary string.
For branded string types where upstream TypeScript never validates (used purely for type-safety to prevent confusion between string slots), expose an infallible From<String> and From<&str> constructor in the Rust wrapper.
When upstream TypeScript occasionally constructs a branded type without validation (via bare as assertion), add a from_str_unchecked (or similarly named) constructor on the Rust side. Keep the validating constructor as well; `from_str_u...

Files:

  • crates/package-manager/src/install_package_from_registry/tests.rs
  • crates/package-manager/src/current_lockfile/tests.rs
  • crates/package-manager/src/hoisted_dep_graph.rs
  • crates/config/src/workspace_yaml.rs
  • crates/lockfile/src/lib.rs
  • crates/package-manager/src/install.rs
  • crates/package-manager/src/current_lockfile.rs
  • crates/config/src/workspace_yaml/tests.rs
  • crates/lockfile/src/freshness.rs
  • crates/real-hoist/src/tests.rs
  • crates/config/src/lib.rs
  • crates/lockfile/src/freshness/tests.rs
🧠 Learnings (5)
📚 Learning: 2026-05-01T10:01:33.766Z
Learnt from: zkochan
Repo: pnpm/pacquet PR: 349
File: crates/reporter/src/tests.rs:121-121
Timestamp: 2026-05-01T10:01:33.766Z
Learning: In Rust test code, follow the repo’s CODE_STYLE_GUIDE test-logging rule: add logging (e.g., `eprintln!`/`eprintln!(...)`) so that useful diagnostic values are printed when a test fails, unless the assertion is `assert_eq!` (where the differing values are already included). Concretely, if you use assertions like `assert!`, `assert_ne!`, etc., ensure the test logs the relevant actual/expected values (or context) before/around the assertion so failures can be diagnosed without rerunning.

Applied to files:

  • crates/package-manager/src/install_package_from_registry/tests.rs
  • crates/package-manager/src/current_lockfile/tests.rs
  • crates/config/src/workspace_yaml/tests.rs
  • crates/real-hoist/src/tests.rs
  • crates/lockfile/src/freshness/tests.rs
📚 Learning: 2026-05-07T23:19:08.272Z
Learnt from: KSXGitHub
Repo: pnpm/pacquet PR: 401
File: tasks/integrated-benchmark/src/work_env.rs:343-344
Timestamp: 2026-05-07T23:19:08.272Z
Learning: When reviewing Rust code in pnpm/pacquet for deprecated API usage, do not automatically treat `serde_saphyr::to_string` as deprecated. In `serde-saphyr` v0.0.25, `serde_saphyr::to_string` has no `#[deprecated]` attribute (the `#[deprecated]` later in `serde-saphyr-0.0.25/src/lib.rs` applies to a different function). Only flag `serde_saphyr::to_string` as deprecated if the resolved dependency version’s source shows `#[deprecated]` on that specific function.

Applied to files:

  • crates/package-manager/src/install_package_from_registry/tests.rs
  • crates/package-manager/src/current_lockfile/tests.rs
  • crates/package-manager/src/hoisted_dep_graph.rs
  • crates/config/src/workspace_yaml.rs
  • crates/lockfile/src/lib.rs
  • crates/package-manager/src/install.rs
  • crates/package-manager/src/current_lockfile.rs
  • crates/config/src/workspace_yaml/tests.rs
  • crates/lockfile/src/freshness.rs
  • crates/real-hoist/src/tests.rs
  • crates/config/src/lib.rs
  • crates/lockfile/src/freshness/tests.rs
📚 Learning: 2026-05-13T19:22:48.951Z
Learnt from: zkochan
Repo: pnpm/pacquet PR: 478
File: crates/package-manager/src/hoisted_dep_graph.rs:51-55
Timestamp: 2026-05-13T19:22:48.951Z
Learning: When reviewing this Rust codebase, avoid introducing/using a newtype like `PkgIdWithPatchHash` in only one module (e.g., `hoisted_dep_graph.rs`) if other related pacquet modules still represent the upstream pnpm “pkg id with patch hash” as a plain `String` (e.g., `virtual_store_layout`). For type consistency, either keep `pkg_id_with_patch_hash` as `String` here, or require a workspace-wide sweep that defines/extracts the `PkgIdWithPatchHash` newtype once and updates all call sites together (otherwise defer the refactor to a coordinated follow-up PR).

Applied to files:

  • crates/package-manager/src/install_package_from_registry/tests.rs
  • crates/package-manager/src/current_lockfile/tests.rs
  • crates/package-manager/src/hoisted_dep_graph.rs
  • crates/package-manager/src/install.rs
  • crates/package-manager/src/current_lockfile.rs
📚 Learning: 2026-05-13T20:09:22.171Z
Learnt from: zkochan
Repo: pnpm/pacquet PR: 486
File: crates/package-manager/src/hoisted_dep_graph.rs:472-476
Timestamp: 2026-05-13T20:09:22.171Z
Learning: In pnpm/pacquet, when generating serialized `hoisted_locations` strings (used for `.modules.yaml`), normalize path separators to forward slashes (e.g., replace `\\` with `/`). This preserves cross-platform portability: the output must not use OS-native separators because upstream pnpm’s `path.relative` can return platform-specific separators, which would break consistency with pnpm/pacquet’s other serialized formats on Windows.

Applied to files:

  • crates/package-manager/src/install_package_from_registry/tests.rs
  • crates/package-manager/src/current_lockfile/tests.rs
  • crates/package-manager/src/hoisted_dep_graph.rs
  • crates/package-manager/src/install.rs
  • crates/package-manager/src/current_lockfile.rs
📚 Learning: 2026-05-01T10:01:33.766Z
Learnt from: zkochan
Repo: pnpm/pacquet PR: 349
File: crates/reporter/src/tests.rs:121-121
Timestamp: 2026-05-01T10:01:33.766Z
Learning: In this repo’s Rust tests (run via `cargo nextest`), stdout/stderr from passing tests is captured and is only shown when a test fails. As a result, `eprintln!` (and similar stderr logging) in these tests should generally not be flagged as CI “noise” on the happy path; CI output should only appear for failing tests.

Applied to files:

  • crates/real-hoist/src/tests.rs
🔇 Additional comments (8)
crates/config/src/lib.rs (1)

492-507: LGTM!

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

56-56: LGTM!

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

66-77: LGTM!

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

37-37: LGTM!

Also applies to: 65-65, 117-117, 169-169, 245-245, 315-315, 353-353, 404-404, 506-506, 588-588, 652-652, 727-727, 775-775, 827-827, 880-880, 930-930, 983-983, 1015-1015

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

59-59: LGTM!

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

86-91: LGTM!

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

863-863: LGTM!

Also applies to: 879-879, 1333-1333, 1370-1370

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

200-208: LGTM!

Also applies to: 389-391

Comment thread crates/lockfile/src/freshness.rs
@codecov

codecov Bot commented May 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.90909% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.60%. Comparing base (72fb61b) to head (34ed43d).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
crates/package-manager/src/install.rs 72.00% 7 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #507      +/-   ##
==========================================
+ Coverage   88.58%   88.60%   +0.01%     
==========================================
  Files         125      125              
  Lines       13575    13643      +68     
==========================================
+ Hits        12026    12088      +62     
- Misses       1549     1555       +6     

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

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

@github-actions

github-actions Bot commented May 13, 2026

Copy link
Copy Markdown

Micro-Benchmark Results

Linux

group                          main                                   pr
-----                          ----                                   --
tarball/download_dependency    1.00     16.6±0.25ms   261.7 KB/sec    1.01     16.7±0.91ms   259.3 KB/sec

@github-actions

Copy link
Copy Markdown

Integrated-Benchmark Report (Linux)

Scenario: Frozen Lockfile

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 2.118 ± 0.068 2.000 2.242 1.00 ± 0.04
pacquet@main 2.112 ± 0.039 2.044 2.149 1.00
pnpm 5.243 ± 0.090 5.149 5.463 2.48 ± 0.06
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 2.11846057316,
      "stddev": 0.06778529723526798,
      "median": 2.1097511651599996,
      "user": 2.7032178799999995,
      "system": 2.05694058,
      "min": 2.0000924736599996,
      "max": 2.24177751066,
      "times": [
        2.24177751066,
        2.1077433806599997,
        2.14092371866,
        2.0000924736599996,
        2.11175894966,
        2.09662391566,
        2.18884373466,
        2.05922901166,
        2.15438695866,
        2.08322607766
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 2.11231005286,
      "stddev": 0.03933720141454545,
      "median": 2.12193385316,
      "user": 2.7008383799999995,
      "system": 2.06047258,
      "min": 2.04437638966,
      "max": 2.1485763796599997,
      "times": [
        2.1469331966599996,
        2.1063183896599997,
        2.1456741036599998,
        2.1485763796599997,
        2.04437638966,
        2.09944506666,
        2.13973509166,
        2.1366376786599997,
        2.10723002766,
        2.04817420466
      ]
    },
    {
      "command": "pnpm",
      "mean": 5.243427424960001,
      "stddev": 0.08976177694525828,
      "median": 5.21307392516,
      "user": 8.39048038,
      "system": 2.59231218,
      "min": 5.14900719366,
      "max": 5.46331753766,
      "times": [
        5.31324026866,
        5.19679286366,
        5.21015602766,
        5.14900719366,
        5.226987981660001,
        5.2133226736600005,
        5.1782236606600005,
        5.46331753766,
        5.21282517666,
        5.27040086566
      ]
    }
  ]
}

Scenario: Frozen Lockfile (Hot Cache)

Command Mean [ms] Min [ms] Max [ms] Relative
pacquet@HEAD 555.8 ± 62.5 517.0 721.2 1.00
pacquet@main 615.6 ± 61.2 551.7 768.0 1.11 ± 0.17
pnpm 2080.8 ± 138.2 1910.5 2364.3 3.74 ± 0.49
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 0.5558121005400001,
      "stddev": 0.06249411680986435,
      "median": 0.53177887394,
      "user": 0.3659983,
      "system": 0.9496117999999999,
      "min": 0.51699886094,
      "max": 0.72118442094,
      "times": [
        0.72118442094,
        0.53648856894,
        0.52188493094,
        0.54213588594,
        0.54631475694,
        0.5270691789399999,
        0.52269691294,
        0.51699886094,
        0.5263044019399999,
        0.59704308694
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 0.61559833144,
      "stddev": 0.06117589153944423,
      "median": 0.6027643444399999,
      "user": 0.37304930000000003,
      "system": 0.9496848,
      "min": 0.55172879194,
      "max": 0.76797151294,
      "times": [
        0.76797151294,
        0.61149114494,
        0.62206280194,
        0.59282798994,
        0.55172879194,
        0.62599410394,
        0.56554233894,
        0.64894817594,
        0.59403754394,
        0.57537890994
      ]
    },
    {
      "command": "pnpm",
      "mean": 2.08075560794,
      "stddev": 0.13821696464840105,
      "median": 2.0418527759400003,
      "user": 2.708259799999999,
      "system": 1.2360919999999997,
      "min": 1.9104854789399999,
      "max": 2.3642618049400004,
      "times": [
        2.26554318194,
        2.0720135759400002,
        2.0640107369400003,
        2.0196948149400002,
        1.97085514194,
        1.9104854789399999,
        2.0099632779400003,
        2.3642618049400004,
        2.1235556729400002,
        2.0071723929400003
      ]
    }
  ]
}

zkochan added a commit that referenced this pull request May 13, 2026
…nal + set-based predicate

CodeRabbit review on PR #507 (major): the filter wrongly applied to
`devDependencies` too. Upstream's
`hooks/read-package-hook/src/createOptionalDependenciesRemover.ts`
iterates `manifest.optionalDependencies` and deletes matches from
`optionalDependencies` AND `dependencies` only — `devDependencies`
is untouched. The previous impl applied the filter to all three
groups in both `flat_manifest_specs` and the per-field check, so a
stale lockfile could incorrectly pass when the manifest added or
removed a matching `devDependency`.

Two fixes:

1. **Group gate** in `flat_manifest_specs` and the per-field check:
   apply the filter only when the group is `Prod` or `Optional`.
   `Dev` walks ignore the closure.

2. **Set-based predicate** at the call site
   (`Install::run`): build the "to drop" set from
   `manifest.optionalDependencies ∩ pattern`, not just from the
   pattern. A name listed only in `dependencies` (not
   `optionalDependencies`) that happens to match the pattern is
   NOT removed by upstream's hook (the hook never iterates that
   name). The set-based predicate captures that nuance.

Both fixes together mirror the hook's exact semantics.

Two new regression tests pin the dev-dependency behavior:
`ignored_optional_does_not_apply_to_dev_dependencies` and
`ignored_optional_dev_only_lockfile_entry_kept`. Test-the-test
verified by dropping the group gate inside `flat_manifest_specs`
— both tests fail.
zkochan added 2 commits May 14, 2026 01:09
…setting check (#434 slice 7)

Last slice of the optional-dependencies umbrella (#434). Adds the
user-facing `ignoredOptionalDependencies` setting: a list of
dep-name patterns the user wants entirely excluded from
resolution + install.

Mirrors pnpm/pnpm@94240bc046's three surfaces:

- **Hook**: `hooks/read-package-hook/src/createOptionalDependenciesRemover.ts`
  builds a matcher and drops matching keys from
  `optionalDependencies` AND `dependencies` (a package may list
  the same dep under both for "optional only when consumed").
- **Lockfile field**: `lockfile/types/src/index.ts:19` —
  `ignoredOptionalDependencies?: string[]` at the top level
  (sibling of `lockfileVersion`/`overrides`, NOT inside
  `settings`).
- **Drift check**: `lockfile/settings-checker/src/getOutdatedLockfileSetting.ts:58-60`
  sorts both arrays and compares; mismatch triggers
  `needsFullResolution`. In pacquet's frozen-only flow this
  surfaces as `OutdatedLockfile`.

## Changes

- **`pacquet-config`**: `Config::ignored_optional_dependencies:
  Option<Vec<String>>` + `WorkspaceSettings` field + `apply_to`
  wiring.
- **`pacquet-lockfile`**: `Lockfile::ignored_optional_dependencies:
  Option<Vec<String>>` top-level field with serde round-trip;
  `check_lockfile_settings(lockfile, config_set)` sorts-and-
  compares; new `StalenessReason::IgnoredOptionalDependenciesChanged
  { lockfile, config }` variant.
- **`pacquet-lockfile::satisfies_package_manifest`** extended
  with an `is_ignored_optional: &dyn Fn(&str) -> bool` parameter.
  Skips matching names in `flat_manifest_specs` and the per-field
  check so a manifest that still lists ignored entries doesn't
  falsely surface as drift against a lockfile the resolver
  correctly built without them.
- **`pacquet-package-manager::install.rs`**: builds a matcher
  from `Config::ignored_optional_dependencies` (reuses
  `pacquet_config::matcher::create_matcher` — same glob engine as
  `hoistPattern`); calls `check_lockfile_settings` before
  `satisfies_package_manifest`; threads the matcher closure into
  the freshness check.
- **`pacquet-package-manager::current_lockfile`**: preserves the
  lockfile's `ignored_optional_dependencies` through the
  slice 6 filter so the recorded set round-trips to the current
  lockfile.

## Tests

- `pacquet_config`: yaml-parse + `apply_to` round-trip + omission
  baseline.
- `pacquet_lockfile::freshness::check_settings_*`: both-sides-
  empty, sorted-match-regardless-of-order, drift in both
  directions. Test-the-test verified by removing the sort+compare
  guard.
- `pacquet_lockfile::freshness::ignored_optional_filtered_*`:
  manifest-side filter passes when the matcher fires; polarity
  test confirms the unfiltered case surfaces as `SpecifiersDiffer`.
- `pacquet_lockfile::freshness::ignored_optional_dependencies_round_trips_through_yaml`:
  serde wire-shape round-trip.

Closes #503. Closes the #434 umbrella.
…nal + set-based predicate

CodeRabbit review on PR #507 (major): the filter wrongly applied to
`devDependencies` too. Upstream's
`hooks/read-package-hook/src/createOptionalDependenciesRemover.ts`
iterates `manifest.optionalDependencies` and deletes matches from
`optionalDependencies` AND `dependencies` only — `devDependencies`
is untouched. The previous impl applied the filter to all three
groups in both `flat_manifest_specs` and the per-field check, so a
stale lockfile could incorrectly pass when the manifest added or
removed a matching `devDependency`.

Two fixes:

1. **Group gate** in `flat_manifest_specs` and the per-field check:
   apply the filter only when the group is `Prod` or `Optional`.
   `Dev` walks ignore the closure.

2. **Set-based predicate** at the call site
   (`Install::run`): build the "to drop" set from
   `manifest.optionalDependencies ∩ pattern`, not just from the
   pattern. A name listed only in `dependencies` (not
   `optionalDependencies`) that happens to match the pattern is
   NOT removed by upstream's hook (the hook never iterates that
   name). The set-based predicate captures that nuance.

Both fixes together mirror the hook's exact semantics.

Two new regression tests pin the dev-dependency behavior:
`ignored_optional_does_not_apply_to_dev_dependencies` and
`ignored_optional_dev_only_lockfile_entry_kept`. Test-the-test
verified by dropping the group gate inside `flat_manifest_specs`
— both tests fail.
Copilot AI review requested due to automatic review settings May 13, 2026 23:11
@zkochan zkochan force-pushed the feat-ignored-optional-deps branch from b5f696b to 34ed43d Compare May 13, 2026 23:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@zkochan zkochan merged commit 1a15676 into main May 13, 2026
14 of 15 checks passed
@zkochan zkochan deleted the feat-ignored-optional-deps branch May 13, 2026 23:15
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ignoredOptionalDependencies config + lockfile field + outdated-setting check (#434 slice 7)

2 participants