feat(install): warn when an override matches no dependency#12741
feat(install): warn when an override matches no dependency#12741aqeelat wants to merge 9 commits into
Conversation
PR Summary by QodoWarn on unused pnpm overrides during install (TS + pacquet parity)
AI Description
Diagram
High-Level Assessment
Files changed (24)
|
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds install-time detection for override selectors that match no dependency, emits a dedicated unused-override log at resolution completion, and renders it as a grouped warning in the default reporters. The change also threads applied-selector tracking through override application and adds coverage for install, reporter, and lockfile-scanning paths. ChangesUnused overrides warning
Estimated code review effort: 4 (Complex) | ~60 minutes Assessment against linked issues
Out-of-scope changesNo out-of-scope code changes identified. Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Code Review by Qodo
Context used✅ Tickets:
🎫 Warn when override doesn't match anything 1. Unused overrides buffer ignores prefix
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #12741 +/- ##
==========================================
+ Coverage 85.55% 85.58% +0.02%
==========================================
Files 413 413
Lines 63908 64053 +145
==========================================
+ Hits 54679 54821 +142
- Misses 9229 9232 +3 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
pnpm11/hooks/read-package-hook/src/createVersionsOverrider.ts (1)
41-41: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winKeep
overrideDepsOfPkgwithin the argument-count guideline.Adding
onAppliedmakes this helper a four-argument function; fold it into the existing options object.Proposed refactor
- overrideDepsOfPkg({ manifest, dir }, versionOverridesWithParent, genericVersionOverrides, onApplied) + overrideDepsOfPkg({ manifest, dir, onApplied }, versionOverridesWithParent, genericVersionOverrides)function overrideDepsOfPkg ( - { manifest, dir }: { manifest: PackageManifest, dir: string | undefined }, + { manifest, dir, onApplied }: { + manifest: PackageManifest + dir: string | undefined + onApplied?: (override: VersionOverrideBase) => void + }, versionOverrides: VersionOverrideWithParent[], - genericVersionOverrides: VersionOverride[], - onApplied?: (override: VersionOverrideBase) => void + genericVersionOverrides: VersionOverride[] ): void {As per coding guidelines, TypeScript functions should keep to no more than two or three arguments by using an options object when needed.
Also applies to: 78-83
🤖 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 `@pnpm11/hooks/read-package-hook/src/createVersionsOverrider.ts` at line 41, The helper call in createVersionsOverrider is passing too many arguments to overrideDepsOfPkg, violating the argument-count guideline. Refactor overrideDepsOfPkg to accept onApplied inside the existing options object instead of as a separate fourth parameter, then update the call sites in createVersionsOverrider and the related usage around the other referenced block to match the new signature.Source: Coding 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.
Inline comments:
In `@pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs`:
- Around line 1217-1244: Gate the unused-override reporting in
install_with_fresh_lockfile on full re-resolution only: the current use of
versions_overrider.applied_selectors() in the unused selector loop can
misclassify overrides when reused wanted_lockfile subtrees are present. Update
the conditional around the UnusedOverride emission so it only runs when the
install path truly re-resolves the tree, and keep the existing selector
comparison logic inside that guarded block.
---
Nitpick comments:
In `@pnpm11/hooks/read-package-hook/src/createVersionsOverrider.ts`:
- Line 41: The helper call in createVersionsOverrider is passing too many
arguments to overrideDepsOfPkg, violating the argument-count guideline. Refactor
overrideDepsOfPkg to accept onApplied inside the existing options object instead
of as a separate fourth parameter, then update the call sites in
createVersionsOverrider and the related usage around the other referenced block
to match the new signature.
🪄 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: debad984-0556-45b2-91cc-1f1cb937e0f6
📒 Files selected for processing (24)
.changeset/warn-unused-overrides.mdpacquet/crates/default-reporter/src/state.rspacquet/crates/default-reporter/tests/render.rspacquet/crates/package-manager/src/install_with_fresh_lockfile.rspacquet/crates/package-manager/src/overrides.rspacquet/crates/package-manager/src/overrides/tests.rspacquet/crates/reporter/src/lib.rspacquet/crates/reporter/src/tests.rspnpm11/cli/default-reporter/src/index.tspnpm11/cli/default-reporter/src/reporterForClient/index.tspnpm11/cli/default-reporter/src/reporterForClient/reportUnusedOverrides.tspnpm11/cli/default-reporter/test/reportingUnusedOverrides.tspnpm11/core/core-loggers/src/all.tspnpm11/core/core-loggers/src/index.tspnpm11/core/core-loggers/src/unusedOverrideLogger.tspnpm11/hooks/read-package-hook/src/createReadPackageHook.tspnpm11/hooks/read-package-hook/src/createVersionsOverrider.tspnpm11/hooks/read-package-hook/test/createVersionOverrider.test.tspnpm11/installing/deps-installer/src/install/extendInstallOptions.tspnpm11/installing/deps-installer/src/install/index.tspnpm11/installing/deps-installer/src/parseWantedDependencies.tspnpm11/installing/deps-installer/test/install/overrides.tspnpm11/pnpm/test/install/pnpmRegistry.tspnpm11/pnpm/test/install/unusedOverrides.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (9)
- GitHub Check: Rust CI / Test / macos
- GitHub Check: Rust CI / Test / ubuntu
- GitHub Check: Rust CI / Test / windows
- GitHub Check: Rust CI / Dylint
- GitHub Check: TS CI / Build pnpr / ubuntu-latest
- GitHub Check: TS CI / Build pnpr / 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 (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: In TypeScript code, preferinterfacefor defining object shapes.
In TypeScript code, use camelCase for variable names.
In TypeScript code, preferasync/awaitover promise chains, usePromise.allorPromise.anyfor independent work, and hoist invariant work out of loops.
In TypeScript code, prefer functions over classes, declare functions after use when relying on hoisting, and keep functions to no more than two or three arguments by using an options object when needed.
In TypeScript code, use the import order: standard libraries first, then external dependencies in alphabetical order, then relative imports.
In TypeScript code, throwPnpmErrorfor user-reachable errors, use plainErrorfor programmer/type-guard/unreachable errors, never swallow errors, catch only the specific expected error code, and include contextual information such as the offending path in error messages.
In TypeScript code, keep configuration flowing through@pnpm/configand command options, avoid hardcoding configurable values, have command handlers return data for the CLI to print, and avoid wrapper functions that add no behavior.
In TypeScript code, use async filesystem APIs andutil.types.isNativeError()in Jest instead ofinstanceof Errorwhen checking caught errors across realms.
In TypeScript tests, do not useinstanceof Errorfor caught-error checks; useutil.types.isNativeError()instead because Jest can run tests across VM realms.
Files:
pnpm11/core/core-loggers/src/all.tspnpm11/cli/default-reporter/test/reportingUnusedOverrides.tspnpm11/core/core-loggers/src/unusedOverrideLogger.tspnpm11/core/core-loggers/src/index.tspnpm11/cli/default-reporter/src/reporterForClient/reportUnusedOverrides.tspnpm11/installing/deps-installer/src/parseWantedDependencies.tspnpm11/hooks/read-package-hook/test/createVersionOverrider.test.tspnpm11/installing/deps-installer/src/install/extendInstallOptions.tspnpm11/pnpm/test/install/unusedOverrides.tspnpm11/cli/default-reporter/src/reporterForClient/index.tspnpm11/installing/deps-installer/test/install/overrides.tspnpm11/cli/default-reporter/src/index.tspnpm11/hooks/read-package-hook/src/createReadPackageHook.tspnpm11/pnpm/test/install/pnpmRegistry.tspnpm11/hooks/read-package-hook/src/createVersionsOverrider.tspnpm11/installing/deps-installer/src/install/index.ts
pacquet/crates/**/*.rs
📄 CodeRabbit inference engine (pacquet/AGENTS.md)
pacquet/crates/**/*.rs: When porting a function that emits pnpm-style log events throughglobalLogger,logger.debug(...), orstreamParser.write(...), mirror the upstream call site, payload, and ordering so the default reporter parses pacquet NDJSON the same way.
Prefer real fixtures; use the dependency-injection seam onHost/Sysonly for branches that portable fixtures cannot reach, such as filesystem error kinds, deterministic time, shared process-global state, or external-service happy paths.
When porting a branded string type, declare a distinct Rust newtype wrapper instead of collapsing it toStringor&str.
If upstream always validates a branded string before construction, make the Rust wrapper constructible only viaTryFrom<String>and/orFromStr; do not provide an infallible public constructor.
If upstream never validates a branded string, expose infallibleFrom<String>(andFrom<&str>when convenient) for the Rust wrapper.
If upstream sometimes constructs a branded string without validation, add afrom_str_unchecked-style constructor alongside the validating constructor.
For branded types that cross JSON, YAML, or INI boundaries, wire serde through the validator with#[serde(try_from = "String")]on deserialization and#[serde(into = "String")]on serialization.
Usederive_more::From/derive_more::Intofor mechanical wrapper conversions; hand-write conversion impls only when validation or normalization requires custom logic.
Model upstream string-literal unions as Rustenums rather than newtype wrappers.
Treat TypeScript template-literal types as branded strings and apply the same newtype/validation rules.
Do not use star imports inside module bodies; prefer explicit imports such asuse super::{Foo, bar}.
Use doc comments for rustdoc-visible API contracts, and use regular//comments only for non-obvious implementation rationale.
Do not duplicate tests in prose: if a behavior is already covered by a test, keep the doc...
Files:
pacquet/crates/package-manager/src/overrides/tests.rspacquet/crates/reporter/src/lib.rspacquet/crates/reporter/src/tests.rspacquet/crates/package-manager/src/install_with_fresh_lockfile.rspacquet/crates/default-reporter/tests/render.rspacquet/crates/default-reporter/src/state.rspacquet/crates/package-manager/src/overrides.rs
🧠 Learnings (14)
📚 Learning: 2026-05-14T09:04:00.133Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11622
File: resolving/npm-resolver/test/publishedBy.test.ts:350-354
Timestamp: 2026-05-14T09:04:00.133Z
Learning: In the pnpm/pnpm repository, ESLint is the authoritative style linter. Do not raise review findings for missing trailing commas in multiline function calls (e.g., `fs.writeFileSync(...)`) when this repo’s ESLint configuration does not report them and lint passes. Prefer deferring to the ESLint results for this specific trailing-comma rule rather than enforcing it manually in code review.
Applied to files:
pnpm11/core/core-loggers/src/all.tspnpm11/cli/default-reporter/test/reportingUnusedOverrides.tspnpm11/core/core-loggers/src/unusedOverrideLogger.tspnpm11/core/core-loggers/src/index.tspnpm11/cli/default-reporter/src/reporterForClient/reportUnusedOverrides.tspnpm11/installing/deps-installer/src/parseWantedDependencies.tspnpm11/hooks/read-package-hook/test/createVersionOverrider.test.tspnpm11/installing/deps-installer/src/install/extendInstallOptions.tspnpm11/pnpm/test/install/unusedOverrides.tspnpm11/cli/default-reporter/src/reporterForClient/index.tspnpm11/installing/deps-installer/test/install/overrides.tspnpm11/cli/default-reporter/src/index.tspnpm11/hooks/read-package-hook/src/createReadPackageHook.tspnpm11/pnpm/test/install/pnpmRegistry.tspnpm11/hooks/read-package-hook/src/createVersionsOverrider.tspnpm11/installing/deps-installer/src/install/index.ts
📚 Learning: 2026-06-05T13:47:26.046Z
Learnt from: vsumner
Repo: pnpm/pnpm PR: 12190
File: installing/deps-installer/src/install/index.ts:2337-2343
Timestamp: 2026-06-05T13:47:26.046Z
Learning: In the pnpm/pnpm codebase, `PnpmError` automatically prefixes `err.code` with `ERR_PNPM_` when you pass a code that does not already start with `ERR_PNPM_` (it normalizes `this.code` via `code.startsWith('ERR_PNPM_') ? code : `ERR_PNPM_${code}``). Therefore, during code review you should NOT flag `new PnpmError(...)` call sites for passing a bare error code (e.g., `new PnpmError('FROZEN_STORE_INCOMPATIBLE_WITH_PNPR', ...)`); the resulting `err.code` will still be `ERR_PNPM_FROZEN_STORE_INCOMPATIBLE_WITH_PNPR`.
Applied to files:
pnpm11/core/core-loggers/src/all.tspnpm11/cli/default-reporter/test/reportingUnusedOverrides.tspnpm11/core/core-loggers/src/unusedOverrideLogger.tspnpm11/core/core-loggers/src/index.tspnpm11/cli/default-reporter/src/reporterForClient/reportUnusedOverrides.tspnpm11/installing/deps-installer/src/parseWantedDependencies.tspnpm11/hooks/read-package-hook/test/createVersionOverrider.test.tspnpm11/installing/deps-installer/src/install/extendInstallOptions.tspnpm11/pnpm/test/install/unusedOverrides.tspnpm11/cli/default-reporter/src/reporterForClient/index.tspnpm11/installing/deps-installer/test/install/overrides.tspnpm11/cli/default-reporter/src/index.tspnpm11/hooks/read-package-hook/src/createReadPackageHook.tspnpm11/pnpm/test/install/pnpmRegistry.tspnpm11/hooks/read-package-hook/src/createVersionsOverrider.tspnpm11/installing/deps-installer/src/install/index.ts
📚 Learning: 2026-06-05T13:47:05.929Z
Learnt from: vsumner
Repo: pnpm/pnpm PR: 12190
File: installing/deps-installer/test/install/frozenStore.ts:2-17
Timestamp: 2026-06-05T13:47:05.929Z
Learning: In the pnpm/pnpm repository, the shared Jest preset keeps `injectGlobals` at its default (`true`), so `test` and `expect` are available as Jest globals. Therefore, reviewers should not flag (or treat as TypeScript/compilation errors) missing `import { test, expect } from 'jest/globals'` when a test file uses `test`/`expect` without importing them. Importing from `jest/globals` may still be used for consistency with sibling files, but it is not required for execution in this repo unless a Jest preset is explicitly configured with `injectGlobals: false`.
Applied to files:
pnpm11/cli/default-reporter/test/reportingUnusedOverrides.tspnpm11/hooks/read-package-hook/test/createVersionOverrider.test.tspnpm11/pnpm/test/install/unusedOverrides.tspnpm11/installing/deps-installer/test/install/overrides.tspnpm11/pnpm/test/install/pnpmRegistry.ts
📚 Learning: 2026-05-26T21:01:06.666Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11966
File: .changeset/require-tarball-integrity.md:6-6
Timestamp: 2026-05-26T21:01:06.666Z
Learning: In pnpm lockfile-related release notes/docs (especially changeset markdown), preserve URL hostnames exactly as they appear in pnpm-lock.yaml tarball resolution entries—keep hosts like `codeload.github.com`, `bitbucket.org`, and `gitlab.com` in lowercase. Do not “correct” them to title-case/preserve brand capitalization (e.g., LanguageTool rules like `GITHUB` capitalization) because these are literal URL fragments, not platform brand names.
Applied to files:
.changeset/warn-unused-overrides.md
📚 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/overrides/tests.rspacquet/crates/reporter/src/lib.rspacquet/crates/reporter/src/tests.rspacquet/crates/package-manager/src/install_with_fresh_lockfile.rspacquet/crates/default-reporter/tests/render.rspacquet/crates/default-reporter/src/state.rspacquet/crates/package-manager/src/overrides.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/overrides/tests.rspacquet/crates/reporter/src/lib.rspacquet/crates/reporter/src/tests.rspacquet/crates/package-manager/src/install_with_fresh_lockfile.rspacquet/crates/default-reporter/tests/render.rspacquet/crates/default-reporter/src/state.rspacquet/crates/package-manager/src/overrides.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/overrides/tests.rspacquet/crates/reporter/src/lib.rspacquet/crates/reporter/src/tests.rspacquet/crates/package-manager/src/install_with_fresh_lockfile.rspacquet/crates/default-reporter/tests/render.rspacquet/crates/default-reporter/src/state.rspacquet/crates/package-manager/src/overrides.rs
📚 Learning: 2026-06-06T18:58:37.156Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 12243
File: pacquet/crates/package-manager/src/install_package_by_snapshot.rs:319-322
Timestamp: 2026-06-06T18:58:37.156Z
Learning: When reviewing Rust code, do not assume `matches!(expr, Pattern(_))` will move out of `expr` if `Pattern(_)` contains no by-value bindings. `matches!` desugars to a `match` that auto-borrows the scrutinee for discrimination, so even if `expr` is a non-`Copy` value behind a shared reference (e.g., `&T`), the macro should not move-out of the borrowed data purely due to `matches!`. Treat `matches!(&expr, Pattern(_))` as a readability/clarity improvement, not a correctness requirement. Only flag potential move-out-of-borrow risks when the pattern includes by-value bindings that would require moving the matched value.
Applied to files:
pacquet/crates/package-manager/src/overrides/tests.rspacquet/crates/reporter/src/lib.rspacquet/crates/reporter/src/tests.rspacquet/crates/package-manager/src/install_with_fresh_lockfile.rspacquet/crates/default-reporter/tests/render.rspacquet/crates/default-reporter/src/state.rspacquet/crates/package-manager/src/overrides.rs
📚 Learning: 2026-06-12T20:41:57.558Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 12364
File: pacquet/crates/package-manager/src/install/tests.rs:5717-5717
Timestamp: 2026-06-12T20:41:57.558Z
Learning: In the pnpm/pnpm Rust workspace (toolchain Rust 1.95.0), keep using `std::time::Duration::from_mins(...)` and `std::time::Duration::from_hours(...)` as-is. Do not flag these as invalid or suggest replacing them with `Duration::from_secs(...)`, because Clippy’s `clippy::duration_suboptimal_units` lint is denied by `-D warnings` in CI, and changing to seconds may trigger CI failures.
Applied to files:
pacquet/crates/package-manager/src/overrides/tests.rspacquet/crates/reporter/src/lib.rspacquet/crates/reporter/src/tests.rspacquet/crates/package-manager/src/install_with_fresh_lockfile.rspacquet/crates/default-reporter/tests/render.rspacquet/crates/default-reporter/src/state.rspacquet/crates/package-manager/src/overrides.rs
📚 Learning: 2026-06-29T10:07:32.128Z
Learnt from: KSXGitHub
Repo: pnpm/pnpm PR: 12719
File: pacquet/crates/cli/tests/update.rs:56-56
Timestamp: 2026-06-29T10:07:32.128Z
Learning: When reviewing Rust code, do not flag `format!`, `write!`, or `writeln!` calls as having “missing format arguments” if the format string is a raw string literal (e.g., `r#"...{name}..."#`, `br#"...{spec}..."#`) that uses Rust’s captured-format-argument syntax (`{name}`, `{spec}`, etc.). Raw string literals still support captured-format placeholders, so the code is valid as long as those captured names correspond to in-scope variables and compiles.
Applied to files:
pacquet/crates/package-manager/src/overrides/tests.rspacquet/crates/reporter/src/lib.rspacquet/crates/reporter/src/tests.rspacquet/crates/package-manager/src/install_with_fresh_lockfile.rspacquet/crates/default-reporter/tests/render.rspacquet/crates/default-reporter/src/state.rspacquet/crates/package-manager/src/overrides.rs
📚 Learning: 2026-06-12T07:04:03.703Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 12227
File: pacquet/crates/package-manager/src/install.rs:708-719
Timestamp: 2026-06-12T07:04:03.703Z
Learning: In this pnpm-based repo’s package-manager Rust code, `ThrottledClient` uses a two-class semaphore scheduling policy (“reserved half” for tarball/download work and a higher-priority “latency class” for lockfile-verification metadata fetches). Therefore, when reviewing, do NOT flag reuse of the same `Arc<ThrottledClient>` for both downloads and verification as a budget/contention issue—this reuse is intentional and handled by the internal two-class “reserved permits” design. Creating a separate client instance would break the EMFILE guard behavior that relies on shared throttling.
Applied to files:
pacquet/crates/package-manager/src/overrides/tests.rspacquet/crates/package-manager/src/install_with_fresh_lockfile.rspacquet/crates/package-manager/src/overrides.rs
📚 Learning: 2026-06-16T00:37:05.024Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 12436
File: pacquet/crates/package-manager/src/install.rs:254-270
Timestamp: 2026-06-16T00:37:05.024Z
Learning: When reviewing the pacquet Rust port (package-manager crate), ensure any diagnostic `help`/message text included in error variants (e.g., `ERR_PNPM_*` codes) matches pnpm upstream error message strings verbatim. This includes user-facing strings and any pnpm-specific presentation (error code naming, `pnpm:*` log channels, pnpm user-agent expectations, etc.). If the help text references a command that is not yet implemented in pacquet (e.g., `approve-builds`), do not flag that as a review issue—porting/implementing the missing command is tracked separately; keep the user-facing message consistent with pnpm.
Applied to files:
pacquet/crates/package-manager/src/overrides/tests.rspacquet/crates/package-manager/src/install_with_fresh_lockfile.rspacquet/crates/package-manager/src/overrides.rs
📚 Learning: 2026-06-30T00:46:30.632Z
Learnt from: KSXGitHub
Repo: pnpm/pnpm PR: 12737
File: pacquet/crates/package-manager/src/dependencies_graph_to_lockfile.rs:354-354
Timestamp: 2026-06-30T00:46:30.632Z
Learning: When implementing permalink-stability / reachability checks for GitHub commit SHAs in the pnpm codebase, do not determine whether a pinned SHA is reachable from `origin/main` by running `git merge-base --is-ancestor <sha> origin/main` on a shallow `--depth=1` clone. A shallow clone can omit older `origin/main` commits, causing false “unreachable” results. For ancestry/merge-base checks, fetch full history (or otherwise ensure the local commit graph contains the relevant `origin/main` history needed for the ancestry test) before concluding that a pinned SHA is off-`main`.
Applied to files:
pacquet/crates/package-manager/src/overrides/tests.rspacquet/crates/package-manager/src/install_with_fresh_lockfile.rspacquet/crates/package-manager/src/overrides.rs
📚 Learning: 2026-06-19T14:23:52.915Z
Learnt from: KSXGitHub
Repo: pnpm/pnpm PR: 12510
File: pacquet/crates/real-hoist/src/tests.rs:1098-1098
Timestamp: 2026-06-19T14:23:52.915Z
Learning: In the pnpm/pnpm Pacquet Rust codebase, treat `dbg!` calls that appear immediately before an `assert!` (but not `assert_eq!`)—especially when checking complex values like `HashMap<..., VecDeque<...>>`—as intentional, per `CODE_STYLE_GUIDE.md` ("When or when not to log during tests?"). Do not flag these `dbg!` statements as debugging noise or recommend removing them. This is meant to ensure that if the test fails, the actual logged value is visible; `just test` runs tests via `cargo nextest run`, which keeps output from passing tests and only surfaces it on failures.
Applied to files:
pacquet/crates/package-manager/src/overrides/tests.rspacquet/crates/reporter/src/tests.rs
🔇 Additional comments (24)
pnpm11/cli/default-reporter/src/reporterForClient/index.ts (1)
61-61: LGTM!Also applies to: 116-119
pnpm11/pnpm/test/install/pnpmRegistry.ts (1)
97-125: LGTM!.changeset/warn-unused-overrides.md (1)
1-19: LGTM!pnpm11/pnpm/test/install/unusedOverrides.ts (1)
84-97: 🎯 Functional CorrectnessNo change needed:
packageConfigs.<project>.overridesis supported inpnpm-workspace.yaml.> Likely an incorrect or invalid review comment.pnpm11/cli/default-reporter/src/reporterForClient/reportUnusedOverrides.ts (1)
18-29: 🎯 Functional CorrectnessNo dedupe needed here.
parseOverrides()starts fromRecord<string, string>, so the same selector cannot appear twice inopts.parsedOverridesor reach this reporter.> Likely an incorrect or invalid review comment.pnpm11/cli/default-reporter/src/index.ts (1)
169-169: LGTM!Also applies to: 245-247, 291-291
pacquet/crates/default-reporter/src/state.rs (1)
237-243: LGTM!Also applies to: 297-297, 335-337, 436-439, 452-472
pnpm11/cli/default-reporter/test/reportingUnusedOverrides.ts (1)
15-102: LGTM!pacquet/crates/default-reporter/tests/render.rs (1)
334-399: LGTM!pnpm11/installing/deps-installer/test/install/overrides.ts (1)
315-317: 🎯 Functional CorrectnessNo duplicate
const callsdeclaration here.> Likely an incorrect or invalid review comment.pnpm11/core/core-loggers/src/unusedOverrideLogger.ts (1)
1-14: LGTM!pnpm11/core/core-loggers/src/index.ts (1)
25-25: LGTM!Also applies to: 55-55
pacquet/crates/reporter/src/lib.rs (1)
159-168: LGTM!Also applies to: 660-669
pacquet/crates/reporter/src/tests.rs (1)
700-721: LGTM!pacquet/crates/package-manager/src/overrides.rs (1)
40-46: LGTM!Also applies to: 96-96, 105-113, 222-223, 261-262, 306-315
pacquet/crates/package-manager/src/overrides/tests.rs (1)
332-397: LGTM!pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs (1)
1245-1249: LGTM!pnpm11/core/core-loggers/src/all.ts (1)
25-25: LGTM!pnpm11/hooks/read-package-hook/src/createVersionsOverrider.ts (1)
3-27: LGTM!Also applies to: 55-76, 97-124, 154-157
pnpm11/hooks/read-package-hook/src/createReadPackageHook.ts (1)
1-1: LGTM!Also applies to: 48-56, 76-76
pnpm11/hooks/read-package-hook/test/createVersionOverrider.test.ts (1)
770-805: LGTM!Also applies to: 807-830
pnpm11/installing/deps-installer/src/install/extendInstallOptions.ts (1)
397-397: LGTM!Also applies to: 412-424
pnpm11/installing/deps-installer/src/parseWantedDependencies.ts (1)
17-17: LGTM!Also applies to: 63-63
pnpm11/installing/deps-installer/src/install/index.ts (1)
22-22: LGTM!Also applies to: 187-191, 332-332, 888-888, 1659-1678, 2079-2082
|
Code review by qodo was updated up to the latest commit 851747d |
Integrated-Benchmark Report (Linux)Commit: Each scenario reports direct installs and pnpr installs. Bencher consumes pacquet@HEAD and pnpr@HEAD. Scenario: Isolated linker: fresh restore, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 4.16868080266,
"stddev": 0.133943565602734,
"median": 4.14392184696,
"user": 3.89087612,
"system": 3.5588280800000005,
"min": 3.95207117096,
"max": 4.4048922049599994,
"times": [
4.4048922049599994,
4.020061140959999,
3.95207117096,
4.25095996796,
4.310122177959999,
4.1534007619599995,
4.10490600896,
4.131914504959999,
4.22403715596,
4.13444293196
]
},
{
"command": "pacquet@main",
"mean": 4.11925980016,
"stddev": 0.13983223972364447,
"median": 4.06852027146,
"user": 3.93612612,
"system": 3.5214041799999998,
"min": 3.9590824709600003,
"max": 4.35538326896,
"times": [
4.35538326896,
4.06803436196,
4.281362608959999,
4.06900618096,
4.270561798959999,
4.048864507959999,
3.9590824709600003,
4.16006653096,
4.00176964996,
3.9784666219600004
]
},
{
"command": "pnpr@HEAD",
"mean": 2.15567304506,
"stddev": 0.09132443142811597,
"median": 2.15660872496,
"user": 2.65027262,
"system": 3.07506358,
"min": 2.04194707696,
"max": 2.36035574896,
"times": [
2.10351079496,
2.15588412496,
2.15733332496,
2.1125198539600003,
2.06038045296,
2.36035574896,
2.16428964196,
2.23572926296,
2.16478016796,
2.04194707696
]
},
{
"command": "pnpr@main",
"mean": 2.08692160666,
"stddev": 0.09174410591007412,
"median": 2.07483364046,
"user": 2.74851052,
"system": 3.0520321800000003,
"min": 1.97208841696,
"max": 2.3108907269600003,
"times": [
2.10925918596,
2.02003329396,
2.11072013496,
2.07139590496,
2.3108907269600003,
2.11799644796,
2.06212439996,
2.0164361789600003,
1.97208841696,
2.07827137596
]
}
]
}Scenario: Isolated linker: fresh restore, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 0.6407822328,
"stddev": 0.013381232246980856,
"median": 0.6329463115,
"user": 0.37099956,
"system": 1.3295766199999999,
"min": 0.6274353570000001,
"max": 0.665098917,
"times": [
0.631167932,
0.658114378,
0.649763799,
0.6482422600000001,
0.633330876,
0.632561747,
0.6315650030000001,
0.630542059,
0.665098917,
0.6274353570000001
]
},
{
"command": "pacquet@main",
"mean": 0.6556018987999999,
"stddev": 0.014799383581493991,
"median": 0.6570514645000001,
"user": 0.39643766,
"system": 1.32741652,
"min": 0.63728965,
"max": 0.6749176990000001,
"times": [
0.661780008,
0.6630476790000001,
0.652322921,
0.6683535650000001,
0.674685725,
0.638867563,
0.6433178940000001,
0.63728965,
0.6414362840000001,
0.6749176990000001
]
},
{
"command": "pnpr@HEAD",
"mean": 0.7097334686000002,
"stddev": 0.047175419793648914,
"median": 0.6967262275,
"user": 0.38887205999999996,
"system": 1.36557152,
"min": 0.6792370910000001,
"max": 0.839312218,
"times": [
0.7165084610000001,
0.7072100250000001,
0.696402403,
0.681596602,
0.697050052,
0.6792370910000001,
0.707542627,
0.689200009,
0.839312218,
0.683275198
]
},
{
"command": "pnpr@main",
"mean": 0.7053561844,
"stddev": 0.019509896808298365,
"median": 0.6962420840000001,
"user": 0.40601376,
"system": 1.35054092,
"min": 0.6792978540000001,
"max": 0.735370717,
"times": [
0.728808293,
0.6939755470000001,
0.696104107,
0.7107639530000001,
0.6928485400000001,
0.690369813,
0.6792978540000001,
0.696380061,
0.7296429590000001,
0.735370717
]
}
]
}Scenario: Isolated linker: fresh install, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 4.293949263780001,
"stddev": 0.04871058718646163,
"median": 4.28951135298,
"user": 3.77709756,
"system": 3.4390345399999993,
"min": 4.23259565498,
"max": 4.40375629998,
"times": [
4.40375629998,
4.29642900698,
4.23259565498,
4.275451980980001,
4.29226605498,
4.29390791398,
4.33580834998,
4.28616480398,
4.28675665098,
4.23635592098
]
},
{
"command": "pacquet@main",
"mean": 4.27026965258,
"stddev": 0.054380512164978224,
"median": 4.28322527348,
"user": 3.8345341599999996,
"system": 3.41553754,
"min": 4.17094705298,
"max": 4.32904667998,
"times": [
4.27620621298,
4.2902443339800005,
4.2507426529800005,
4.32134936298,
4.32904667998,
4.31428060998,
4.17094705298,
4.30620226898,
4.25307595798,
4.190601392980001
]
},
{
"command": "pnpr@HEAD",
"mean": 2.19836771718,
"stddev": 0.14210232023070543,
"median": 2.15935166648,
"user": 2.5026945599999992,
"system": 2.9390676399999998,
"min": 2.0376892519800003,
"max": 2.43284071398,
"times": [
2.0376892519800003,
2.1201758069800003,
2.13463997798,
2.18406335498,
2.08696491998,
2.2193128529800004,
2.3759569329800003,
2.04408842598,
2.34794493398,
2.43284071398
]
},
{
"command": "pnpr@main",
"mean": 2.26235772468,
"stddev": 0.1565452637247315,
"median": 2.2593846619800004,
"user": 2.5396353599999997,
"system": 2.9491378399999997,
"min": 2.0328211779800003,
"max": 2.50657754698,
"times": [
2.50657754698,
2.2961625269800003,
2.22260679698,
2.40025212798,
2.41805125098,
2.1249908159800004,
2.0328211779800003,
2.35703498098,
2.11369247898,
2.1513875429800002
]
}
]
}Scenario: Isolated linker: fresh install, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 1.38528570524,
"stddev": 0.02195531174656638,
"median": 1.38187767364,
"user": 1.3471852599999998,
"system": 1.7385469000000005,
"min": 1.36041071014,
"max": 1.41883803114,
"times": [
1.3816396481400002,
1.37999506614,
1.39977273914,
1.39051428714,
1.41830015514,
1.38211569914,
1.41883803114,
1.36041071014,
1.36053894514,
1.36073177114
]
},
{
"command": "pacquet@main",
"mean": 1.44497100424,
"stddev": 0.09266218047578534,
"median": 1.41449759514,
"user": 1.4306212600000001,
"system": 1.7396957,
"min": 1.40436367414,
"max": 1.7072796221400002,
"times": [
1.40611205414,
1.7072796221400002,
1.40436367414,
1.43221762214,
1.43154982014,
1.4087846881400001,
1.41488428114,
1.4190613811400001,
1.41134599014,
1.4141109091400001
]
},
{
"command": "pnpr@HEAD",
"mean": 0.6902997656400001,
"stddev": 0.04104560865170669,
"median": 0.68008793314,
"user": 0.36034675999999993,
"system": 1.3146198999999998,
"min": 0.6612679921400001,
"max": 0.80288716114,
"times": [
0.6642187111400001,
0.6612679921400001,
0.67706763114,
0.80288716114,
0.67122325514,
0.7004626021400001,
0.67915441214,
0.6827517201400001,
0.6829427171400001,
0.68102145414
]
},
{
"command": "pnpr@main",
"mean": 0.70105155054,
"stddev": 0.006636792097374372,
"median": 0.6993794561400001,
"user": 0.37132405999999996,
"system": 1.3195823,
"min": 0.69270388114,
"max": 0.71357842814,
"times": [
0.69876388314,
0.7057012231400001,
0.7080430901400001,
0.6983465761400001,
0.6949546001400001,
0.6999950291400001,
0.71357842814,
0.70378200814,
0.69270388114,
0.6946467861400001
]
}
]
}Scenario: Isolated linker: fresh install, cold cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 3.1038544881600005,
"stddev": 0.032276522575814495,
"median": 3.1118689120600003,
"user": 1.8802692,
"system": 2.0068132599999995,
"min": 3.03916239006,
"max": 3.14995106906,
"times": [
3.10857239106,
3.12146997906,
3.12683108006,
3.08954554606,
3.11516543306,
3.06740234506,
3.12543702506,
3.09500762306,
3.14995106906,
3.03916239006
]
},
{
"command": "pacquet@main",
"mean": 3.09958016736,
"stddev": 0.05251111006294535,
"median": 3.08400828656,
"user": 1.8782501999999996,
"system": 2.00457676,
"min": 3.05970867206,
"max": 3.23546876806,
"times": [
3.06026981706,
3.12758561806,
3.09565704806,
3.08775788706,
3.08025868606,
3.05970867206,
3.0764305100600002,
3.10887451206,
3.23546876806,
3.06379015506
]
},
{
"command": "pnpr@HEAD",
"mean": 0.6726700583599999,
"stddev": 0.007933273368760124,
"median": 0.67528988606,
"user": 0.3503283,
"system": 1.2967594599999999,
"min": 0.65560933706,
"max": 0.68209884806,
"times": [
0.67833154706,
0.67782088906,
0.67734619806,
0.6757666530599999,
0.67089609206,
0.65560933706,
0.67481311906,
0.68209884806,
0.67051512306,
0.66350277706
]
},
{
"command": "pnpr@main",
"mean": 0.66877106296,
"stddev": 0.005888958424990845,
"median": 0.66834904856,
"user": 0.35915909999999995,
"system": 1.2909376599999998,
"min": 0.66051598206,
"max": 0.68188730506,
"times": [
0.66943100806,
0.66051598206,
0.66801943306,
0.67371862206,
0.66806688206,
0.66601623706,
0.66257852906,
0.66884541606,
0.66863121506,
0.68188730506
]
}
]
}Scenario: Isolated linker: fresh restore, cold cache + cold store + cold pnpr
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 7.009224038779999,
"stddev": 0.21106622753079357,
"median": 7.010334437379999,
"user": 4.05151686,
"system": 3.81722786,
"min": 6.65607629188,
"max": 7.35875186688,
"times": [
7.35875186688,
6.89827836088,
6.89331987788,
7.0435282708799996,
7.0077820078799995,
6.65607629188,
7.26543028588,
7.1506369758799995,
6.8055495828799994,
7.01288686688
]
},
{
"command": "pacquet@main",
"mean": 6.9621410808799995,
"stddev": 0.19151735348320487,
"median": 6.93441826388,
"user": 4.104301659999999,
"system": 3.8288200599999995,
"min": 6.66861324988,
"max": 7.35378279388,
"times": [
6.95489732188,
6.66861324988,
6.91393920588,
7.06675405688,
7.1014373618799995,
6.86491808388,
7.0548835168799995,
6.8221452958799995,
7.35378279388,
6.820039921879999
]
},
{
"command": "pnpr@HEAD",
"mean": 4.92817907598,
"stddev": 0.08055977294949718,
"median": 4.91522161038,
"user": 2.76667846,
"system": 3.2878508600000003,
"min": 4.79281696188,
"max": 5.07694778788,
"times": [
4.91972589488,
5.00341662488,
4.91071732588,
4.94521095488,
4.79281696188,
4.99243625088,
4.89979580688,
5.07694778788,
4.87215012388,
4.86857302788
]
},
{
"command": "pnpr@main",
"mean": 5.019213050179999,
"stddev": 0.1220644989563262,
"median": 5.016426818879999,
"user": 2.83147276,
"system": 3.29032646,
"min": 4.87483077588,
"max": 5.189391307879999,
"times": [
4.87483077588,
5.09439130888,
4.98003972488,
4.92919502988,
5.16889305388,
4.90057788788,
4.87704253188,
5.189391307879999,
5.12495496788,
5.05281391288
]
}
]
} |
|
| Branch | pr/12741 |
| Testbed | pnpr |
⚠️ WARNING: No Threshold found!Without a Threshold, no Alerts will ever be generated.
Click here to create a new Threshold
For more information, see the Threshold documentation.
To only post results if a Threshold exists, set the--ci-only-thresholdsflag.
Click to view all benchmark results
| Benchmark | Latency | milliseconds (ms) |
|---|---|---|
| isolated-linker.fresh-install.cold-cache.cold-store | 📈 view plot | 2,198.37 ms |
| isolated-linker.fresh-install.cold-cache.hot-store | 📈 view plot | 672.67 ms |
| isolated-linker.fresh-install.hot-cache.hot-store | 📈 view plot | 690.30 ms |
| isolated-linker.fresh-restore.cold-cache.cold-store | 📈 view plot | 2,155.67 ms |
| isolated-linker.fresh-restore.cold-cache.cold-store.cold-pnpr | 📈 view plot | 4,928.18 ms |
| isolated-linker.fresh-restore.hot-cache.hot-store | 📈 view plot | 709.73 ms |
851747d to
0436da5
Compare
| // When a pnpr server is configured, use server-side resolution | ||
| // instead of the normal resolution flow. | ||
| // instead of the normal resolution flow. The pnpr protocol resolves | ||
| // with overrides but does not report which selectors matched, so | ||
| // the unused-override warning is silently skipped on this path — | ||
| // an acceptable tradeoff to preserve pnpr's performance benefit. | ||
| if (opts.pnprServer) { | ||
| return installViaPnprServer(manifest, rootDir, opts) | ||
| } |
There was a problem hiding this comment.
1. Pnprserver skips unused-override warning 📎 Requirement gap ≡ Correctness
When pnprServer is configured, pnpm install returns via the pnpr-server path and the new unused-override warning is explicitly skipped. This violates the requirement to warn on unused overrides during pnpm i, allowing silent misconfiguration in this mode.
Agent Prompt
## Issue description
The unused-override warning is skipped when `opts.pnprServer` is set, so `pnpm install` can run with unused `overrides` without emitting any warning.
## Issue Context
The PR introduces unused override detection and reporting, but the pnpr-server install path currently bypasses it (and a test asserts this behavior).
## Fix Focus Areas
- pnpm11/installing/deps-installer/src/install/index.ts[186-193]
- pnpm11/pnpm/test/install/pnpmRegistry.ts[97-122]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
|
Code review by qodo was updated up to the latest commit 0436da5 |
Looking for bugs?Check back in a few minutes. Qodo's review agents are on it. |
There was a problem hiding this comment.
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 `@pnpm11/installing/deps-installer/src/install/index.ts`:
- Around line 187-190: The pnpr install path in install/index.ts is skipping
unusedOverride reporting entirely, so the new warning is never emitted when
pnprServer handles resolution. Update the pnpr flow used by the install logic
(the branches around the pnprServer resolution path) so matched selectors are
available for unusedOverride, either by propagating the selector matches back
from pnpr or by falling back to the client-side resolver when warning
correctness is required. Make sure the same fix covers the other pnpr-related
branch referenced in the diff as well.
In `@pnpm11/pnpm/test/install/pnpmRegistry.ts`:
- Around line 97-122: The test in pnpmRegistry.ts does not actually cover an
unused override, so it cannot verify the skipped warning behavior. Update the
setup in test('pnpm install with overrides uses pnpr server and skips
unused-override warning', ...) so the overrides entry does not match any
installed dependency, then keep the execPnpm call with pnprServer and assert the
install still succeeds without emitting the unused-override warning. Use the
existing helpers prepareProject, writeYamlFileSync, and execPnpm to keep the
test focused on the pnpr server path and the warning behavior.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: aad27078-6c1d-41b7-b5d6-057d030c1627
📒 Files selected for processing (25)
.changeset/warn-unused-overrides.mdpacquet/crates/default-reporter/src/state.rspacquet/crates/default-reporter/tests/render.rspacquet/crates/package-manager/src/install_with_fresh_lockfile.rspacquet/crates/package-manager/src/overrides.rspacquet/crates/package-manager/src/overrides/tests.rspacquet/crates/reporter/src/lib.rspacquet/crates/reporter/src/tests.rspnpm11/cli/default-reporter/src/index.tspnpm11/cli/default-reporter/src/reporterForClient/index.tspnpm11/cli/default-reporter/src/reporterForClient/reportUnusedOverrides.tspnpm11/cli/default-reporter/test/reportingUnusedOverrides.tspnpm11/core/core-loggers/src/all.tspnpm11/core/core-loggers/src/index.tspnpm11/core/core-loggers/src/unusedOverrideLogger.tspnpm11/hooks/read-package-hook/src/createReadPackageHook.tspnpm11/hooks/read-package-hook/src/createVersionsOverrider.tspnpm11/hooks/read-package-hook/test/createVersionOverrider.test.tspnpm11/installing/deps-installer/src/install/extendInstallOptions.tspnpm11/installing/deps-installer/src/install/index.tspnpm11/installing/deps-installer/src/parseWantedDependencies.tspnpm11/installing/deps-installer/test/install/overrides.tspnpm11/pnpm/test/install/pnpmRegistry.tspnpm11/pnpm/test/install/unusedOverrides.tspnpm11/pnpm/test/utils/execPnpm.ts
✅ Files skipped from review due to trivial changes (3)
- pnpm11/core/core-loggers/src/all.ts
- pnpm11/core/core-loggers/src/index.ts
- .changeset/warn-unused-overrides.md
🚧 Files skipped from review as they are similar to previous changes (19)
- pnpm11/core/core-loggers/src/unusedOverrideLogger.ts
- pnpm11/installing/deps-installer/src/parseWantedDependencies.ts
- pnpm11/hooks/read-package-hook/src/createReadPackageHook.ts
- pnpm11/cli/default-reporter/src/index.ts
- pnpm11/pnpm/test/install/unusedOverrides.ts
- pnpm11/cli/default-reporter/src/reporterForClient/reportUnusedOverrides.ts
- pacquet/crates/reporter/src/tests.rs
- pnpm11/cli/default-reporter/test/reportingUnusedOverrides.ts
- pnpm11/hooks/read-package-hook/test/createVersionOverrider.test.ts
- pnpm11/cli/default-reporter/src/reporterForClient/index.ts
- pnpm11/installing/deps-installer/src/install/extendInstallOptions.ts
- pacquet/crates/package-manager/src/overrides.rs
- pacquet/crates/reporter/src/lib.rs
- pacquet/crates/package-manager/src/overrides/tests.rs
- pacquet/crates/default-reporter/src/state.rs
- pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
- pacquet/crates/default-reporter/tests/render.rs
- pnpm11/installing/deps-installer/test/install/overrides.ts
- pnpm11/hooks/read-package-hook/src/createVersionsOverrider.ts
There was a problem hiding this comment.
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 `@pnpm11/installing/deps-installer/src/install/index.ts`:
- Around line 2697-2699: Update depEntryMatches() so selector matching does not
compare the post-override resolved version against the selector range for
pnpr-style scoped selectors like foo@^1: 2.0.0; instead use the matched selector
metadata from pnpr, the original requested range, or a fallback that recognizes
range-scoped selectors before deciding whether the override is unused. Make the
fix in the selector/range handling around depEntryMatches() and the related
override warning path it feeds, then add a regression test covering foo@^1
resolving to 2.0.0 so the warning fails without the fix.
- Around line 2640-2644: The parent-version filtering in the install flow is too
permissive: in the package scan inside the install logic, an entry from
nameVerFromPkgSnapshot() with no version is currently allowed through even when
parentRange is set, which can hide an unused-override warning. Update the
conditional around the parentName/parentRange checks so unknown versions are not
treated as matching a range; only continue past the selector when the version is
present and semver.satisfies() returns true, and otherwise let the warning path
run.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 1879f977-4967-4ada-bc91-46e1acf88c73
📒 Files selected for processing (2)
pnpm11/installing/deps-installer/src/install/index.tspnpm11/pnpm/test/install/pnpmRegistry.ts
| function depEntryMatches ( | ||
| deps: Record<string, string> | undefined, | ||
| targetName: string, | ||
| targetRange: string | undefined | ||
| ): boolean { | ||
| if (deps == null) return false | ||
| const version = deps[targetName] | ||
| if (version == null) return false | ||
| if (targetRange == null) return true | ||
| if (!semver.valid(version)) return true | ||
| return semver.satisfies(version, targetRange) |
There was a problem hiding this comment.
2. Range match false negatives 🐞 Bug ≡ Correctness
In pnpr-server mode, depEntryMatches() treats any non-semver lockfile version string as a match when an override selector has a version range, so version-scoped overrides (e.g. foo@^1) can be considered applied even when the resolved version does not satisfy the range, suppressing the unused-override warning. This is common because lockfile versions can include peer suffixes like "3.4.1(ajv@6.10.2)" which semver.valid() rejects.
Agent Prompt
### Issue description
`depEntryMatches()` currently returns `true` for any non-semver `version` string when `targetRange` is set. In pnpm lockfiles, resolved versions frequently include peer suffixes (e.g. `3.4.1(ajv@6.10.2)`), plus other non-semver forms (`link:`, `npm:` aliases). This causes version-scoped override selectors to be treated as “applied” just because the dependency key exists, which suppresses the new unused-override warning.
### Issue Context
The pnpr-server path relies on `findAppliedOverrideSelectorsFromLockfile()` to infer which override selectors matched since server-side resolution does not report selectors.
### Fix Focus Areas
- pnpm11/installing/deps-installer/src/install/index.ts[2689-2700]
### Suggested fix
- When `targetRange` is set:
- Extract a comparable semver from the lockfile value rather than treating non-semver as a match.
- For peer suffixes, a simple and accurate approach is `const base = version.split('(')[0]` and then `semver.valid(base)`.
- Return `false` (not a match) for non-semver protocols like `link:`, `file:`, `npm:` unless you have a deliberate, proven mapping that matches pnpm’s override-selector semantics.
- Keep the existing fast-path `targetRange == null => true` for name-only selectors.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| for (const [depPath, snapshot] of Object.entries(lockfile.packages ?? {}) as Array<[DepPath, PackageSnapshot]>) { | ||
| const { name, version } = nameVerFromPkgSnapshot(depPath, snapshot) | ||
| if (name !== parentName) continue | ||
| if (parentRange != null && version != null && !semver.satisfies(version, parentRange)) continue | ||
| if ( |
There was a problem hiding this comment.
3. Unhandled invalid selector ranges 🐞 Bug ☼ Reliability
The pnpr-server lockfile scan calls semver.satisfies() with selector bareSpecifiers that are not validated as semver ranges; selectors like foo@latest can reach this code and may throw, crashing installs (repo-controlled DoS). The local override matcher guards semver operations with semver.validRange(), but this scan does not.
Agent Prompt
### Issue description
`findAppliedOverrideSelectorsFromLockfile()` and `depEntryMatches()` call `semver.satisfies(version, range)` on `range` values derived from override selectors. Override selector parsing does not semver-validate `bareSpecifier`, so non-semver strings (e.g. `latest`) can reach this code and may crash the install.
### Issue Context
- `parseWantedDependency()` returns the raw substring after `@` as `bareSpecifier` without semver validation.
- The normal override-application logic (`isIntersectingRange`) explicitly checks `semver.validRange()` before using semver range logic.
### Fix Focus Areas
- pnpm11/installing/deps-installer/src/install/index.ts[2640-2644]
- pnpm11/installing/deps-installer/src/install/index.ts[2689-2700]
### Suggested fix
- Before calling `semver.satisfies(...)`:
- If `range` is not a valid semver range (`semver.validRange(range) == null`), treat it as non-matching (return `false` / skip) to align with `isIntersectingRange()` behavior.
- Optionally wrap `semver.satisfies` in a try/catch as a belt-and-suspenders guard, logging debug if needed.
- Apply this guard for both:
- `parentRange` check in the parent-scoped branch, and
- `targetRange` checks in `depEntryMatches()`.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
|
Code review by qodo was updated up to the latest commit c9c467f |
| if (override.parentPkg != null) { | ||
| const parentName = override.parentPkg.name | ||
| const parentRange = override.parentPkg.bareSpecifier | ||
| for (const [depPath, snapshot] of Object.entries(lockfile.packages ?? {}) as Array<[DepPath, PackageSnapshot]>) { | ||
| const { name, version } = nameVerFromPkgSnapshot(depPath, snapshot) | ||
| if (name !== parentName) continue | ||
| if (parentRange != null && version != null && !semver.satisfies(version, parentRange)) continue | ||
| if ( | ||
| depEntryMatches(snapshot.dependencies, targetName, targetRange) || | ||
| depEntryMatches(snapshot.optionalDependencies, targetName, targetRange) || | ||
| depEntryMatches(snapshot.peerDependencies, targetName, targetRange) | ||
| ) { | ||
| applied.add(override.selector) | ||
| break | ||
| } | ||
| } | ||
| } else { |
There was a problem hiding this comment.
1. Pnpr parent override false warnings 🐞 Bug ≡ Correctness
In pnpr-server mode, findAppliedOverrideSelectorsFromLockfile() considers parent-scoped overrides matched only when the parent name is found in package snapshots, so parent-scoped overrides that apply to workspace project/importer manifests (e.g. my-app>dep) can be incorrectly logged as unused.
Agent Prompt
### Issue description
`findAppliedOverrideSelectorsFromLockfile()` checks `lockfile.packages` for parent-scoped overrides, but does not account for the case where the parent package is a workspace project (an importer manifest). Since lockfile importer snapshots do not include package `name`/`version`, the current scan cannot detect that `parentPkg` matches a project manifest, and may emit false `pnpm:unusedOverride` logs.
### Issue Context
- Parent-scoped overrides in the local resolver path match against `manifest.name`/`manifest.version` (including project manifests).
- The pnpr-server path tries to reconstruct “applied overrides” by scanning the resolved lockfile.
### Fix Focus Areas
- pnpm11/installing/deps-installer/src/install/index.ts[2616-2676]
- pnpm11/lockfile/types/src/index.ts[68-78]
- pnpm11/hooks/read-package-hook/src/createVersionsOverrider.ts[34-45]
### Suggested fix
- Extend the pnpr-server scan to also treat workspace project manifests as possible `parentPkg` matches:
- In `installViaPnprServer()`, you already have `manifest` and optionally `allInstallProjects`.
- For each project whose `manifest.name` matches `override.parentPkg.name` (and version satisfies `parentPkg.bareSpecifier` when present), look up that project’s importer snapshot (`lockfile.importers[importerId]`) and check whether it contains the target dependency key (and range logic as appropriate).
- Keep the existing `lockfile.packages` scan for parent packages that are real resolved packages.
- Consider adjusting the helper signature to accept `{ importerId, manifest }[]` so parent-scoped overrides can be validated against importers without guessing parent identity from the lockfile.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
|
Code review by qodo was updated up to the latest commit c9c467f |
There was a problem hiding this comment.
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 `@pnpm11/installing/deps-installer/test/install/unusedOverrideScan.ts`:
- Around line 7-156: The current tests only cover the lockfile helper and miss
the install-time warning flow, so add an install e2e regression test around the
unused override warning path. Exercise the install command and verify that
pnpm:unusedOverride is emitted only for truly unused overrides, is buffered
until resolution_done, and appears in the grouped WARN output. Use the existing
unusedOverrideScan-related behavior as a guide, and place the test where it
covers the user-visible install logging rather than just the helper internals.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 1e9f66f3-0666-4cef-9890-b40d01270cf8
📒 Files selected for processing (2)
pnpm11/installing/deps-installer/src/install/index.tspnpm11/installing/deps-installer/test/install/unusedOverrideScan.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- pnpm11/installing/deps-installer/src/install/index.ts
|
Code review by qodo was updated up to the latest commit 2779a89 |
1 similar comment
|
Code review by qodo was updated up to the latest commit 2779a89 |
2779a89 to
1b5e4b0
Compare
|
Code review by qodo was updated up to the latest commit 1b5e4b0 |
|
Code review by qodo was updated up to the latest commit 1b5e4b0 |
|
Code review by qodo was updated up to the latest commit b744003 |
| let selectors = std::mem::take(&mut self.pending_unused_overrides); | ||
| let sanitized: Vec<String> = | ||
| selectors.iter().map(|s| sanitize_override_selector(s)).collect(); | ||
| let head = if sanitized.len() == 1 { | ||
| "1 override matched no dependency".to_string() | ||
| } else { | ||
| format!("{} overrides matched no dependency", sanitized.len()) | ||
| }; | ||
| self.push_warning(&format!("{}: {}", head, sanitized.join(", "))); |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
pnpm11/installing/deps-installer/src/install/findAppliedOverrides.ts (1)
39-105: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicated dependency-group scanning between parent-scoped and generic branches.
The parent-scoped branch (importer/package loops at Lines 44-67 and 70-82) and the generic branch (Lines 84-93, 95-104) repeat nearly identical
depEntryMatcheschecks acrossdependencies/devDependencies/optionalDependencies/peerDependencies. Extracting a small shared helper (e.g.matchesAnyGroup(entry, targetName, groups)) would reduce duplication and lower the risk of the two branches silently diverging on a future edit.♻️ Sketch of a shared helper
+function matchesDependencyGroups ( + groups: Array<Record<string, string> | undefined>, + targetName: string +): boolean { + return groups.some((deps) => depEntryMatches(deps, targetName)) +}Then both branches can call
matchesDependencyGroups([importer.dependencies, importer.devDependencies, importer.optionalDependencies], targetName)etc., instead of repeating the three-way||chain.🤖 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 `@pnpm11/installing/deps-installer/src/install/findAppliedOverrides.ts` around lines 39 - 105, The parent-scoped and generic paths in findAppliedOverrides duplicate the same depEntryMatches logic across dependency groups, making the two branches easy to drift apart. Extract a small shared helper near findAppliedOverrides (for example, one that checks a list of dependency maps against targetName) and use it in both the importer/package snapshot loops and the generic importer/package snapshot loops so the matching rules stay identical and easier to maintain.
🤖 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 `@pnpm11/installing/deps-installer/src/install/index.ts`:
- Around line 1662-1681: The unused-override check in the install flow is
comparing the lockfile importer count against the mutated projects list, which
can misidentify partial installs as full reanalysis. Update the condition in the
`index.ts` install logic to use `ctx.projects` instead of `projects`, matching
the `tryFrozenInstall` gate and the selected workspace set after
`pruneLockfileImporters` has trimmed `ctx.wantedLockfile.importers`.
---
Nitpick comments:
In `@pnpm11/installing/deps-installer/src/install/findAppliedOverrides.ts`:
- Around line 39-105: The parent-scoped and generic paths in
findAppliedOverrides duplicate the same depEntryMatches logic across dependency
groups, making the two branches easy to drift apart. Extract a small shared
helper near findAppliedOverrides (for example, one that checks a list of
dependency maps against targetName) and use it in both the importer/package
snapshot loops and the generic importer/package snapshot loops so the matching
rules stay identical and easier to maintain.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 3de42aa5-2f6d-4bc2-a811-e342068ff9bd
📒 Files selected for processing (27)
.changeset/warn-unused-overrides.mdpacquet/crates/default-reporter/src/state.rspacquet/crates/default-reporter/tests/render.rspacquet/crates/package-manager/src/install_with_fresh_lockfile.rspacquet/crates/package-manager/src/overrides.rspacquet/crates/package-manager/src/overrides/tests.rspacquet/crates/reporter/src/lib.rspacquet/crates/reporter/src/tests.rspnpm11/cli/default-reporter/src/index.tspnpm11/cli/default-reporter/src/reporterForClient/index.tspnpm11/cli/default-reporter/src/reporterForClient/reportUnusedOverrides.tspnpm11/cli/default-reporter/test/reportingUnusedOverrides.tspnpm11/core/core-loggers/src/all.tspnpm11/core/core-loggers/src/index.tspnpm11/core/core-loggers/src/unusedOverrideLogger.tspnpm11/hooks/read-package-hook/src/createReadPackageHook.tspnpm11/hooks/read-package-hook/src/createVersionsOverrider.tspnpm11/hooks/read-package-hook/test/createVersionOverrider.test.tspnpm11/installing/deps-installer/src/install/extendInstallOptions.tspnpm11/installing/deps-installer/src/install/findAppliedOverrides.tspnpm11/installing/deps-installer/src/install/index.tspnpm11/installing/deps-installer/src/parseWantedDependencies.tspnpm11/installing/deps-installer/test/install/overrides.tspnpm11/installing/deps-installer/test/install/unusedOverrideScan.tspnpm11/pnpm/test/install/pnpmRegistry.tspnpm11/pnpm/test/install/unusedOverrides.tspnpm11/pnpm/test/utils/execPnpm.ts
✅ Files skipped from review due to trivial changes (1)
- .changeset/warn-unused-overrides.md
🚧 Files skipped from review as they are similar to previous changes (22)
- pnpm11/installing/deps-installer/src/parseWantedDependencies.ts
- pnpm11/core/core-loggers/src/index.ts
- pnpm11/core/core-loggers/src/all.ts
- pnpm11/pnpm/test/install/unusedOverrides.ts
- pacquet/crates/reporter/src/lib.rs
- pnpm11/cli/default-reporter/src/reporterForClient/index.ts
- pnpm11/installing/deps-installer/src/install/extendInstallOptions.ts
- pnpm11/core/core-loggers/src/unusedOverrideLogger.ts
- pnpm11/installing/deps-installer/test/install/overrides.ts
- pacquet/crates/default-reporter/tests/render.rs
- pnpm11/cli/default-reporter/src/index.ts
- pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
- pnpm11/pnpm/test/install/pnpmRegistry.ts
- pnpm11/cli/default-reporter/test/reportingUnusedOverrides.ts
- pnpm11/installing/deps-installer/test/install/unusedOverrideScan.ts
- pnpm11/hooks/read-package-hook/src/createReadPackageHook.ts
- pacquet/crates/default-reporter/src/state.rs
- pacquet/crates/package-manager/src/overrides/tests.rs
- pnpm11/cli/default-reporter/src/reporterForClient/reportUnusedOverrides.ts
- pacquet/crates/reporter/src/tests.rs
- pacquet/crates/package-manager/src/overrides.rs
- pnpm11/hooks/read-package-hook/src/createVersionsOverrider.ts
|
Code review by qodo was updated up to the latest commit b744003 |
Emits a pnpm:unusedOverride log for each override selector that matched no resolved package during a fresh install. The default reporter buffers these until the resolution_done stage and renders a single grouped WARN line (e.g. '[WARN] 2 overrides matched no dependency: foo, parent>child'). Closes pnpm#10315. TypeScript (pnpm): - New pnpm:unusedOverride logger channel (core-loggers). - createVersionsOverrider accepts onApplied callback; createReadPackageHook threads it as onOverrideApplied. Backward-compat VersionOverrideWithoutRawSelector alias kept for external consumers. - Installer diffs parsed overrides against applied set after full resolution; gate mirrors verifyPatches (forceFullResolution || empty lockfile). - Direct-add path (parseWantedDependencies) records applied overrides too. - Default reporter sanitizes control characters from selectors at render time; raw selectors stay intact in structured events. - pnpr-server path preserved with overrides (warning silently skipped since pnpr doesn't report matched selectors). - ramda isEmpty used for emptiness gate instead of Object.keys().length. Pacquet (Rust port): - VersionsOverrider records applied selectors via Arc<Mutex<HashSet>>; record_applied checks contains before cloning. - install_with_fresh_lockfile gates the diff on lockfile_for_reuse.is_none() (subtree reuse bypasses manifest_hook, so applied_selectors is incomplete). - UnusedOverride events emitted sorted before ResolutionDone. - Default reporter buffers and renders at resolution_done; control characters stripped at render; sort at emission only (no double sort). - Wire-shape test and render tests for the new event channel.
…ckfile scan The pnpr protocol resolves with overrides but does not report which selectors matched. After pnpr resolves, scan the lockfile to determine which override selectors matched at least one dependency, then emit unused-override events for unmatched selectors — same behavior as the local-resolver path. Also fix the e2e test to include an actual unused override and verify the warning is emitted. Use spawnPnpm (async) to capture stdout without deadlocking the counting proxy's event loop.
…g, and project manifests Don't compare post-override resolved versions against selector ranges — the lockfile stores post-override values, so comparing them against the old selector range produces false positives (e.g. foo@^1: 2.0.0 resolves to 2.0.0, which doesn't satisfy ^1). depEntryMatches now checks name presence only; the selector range was already applied during resolution. Guard semver.satisfies with semver.validRange for parentPkg.bareSpecifier — non-semver strings like 'latest' can reach this code via override selectors and produce unexpected results, matching isIntersectingRange's guard in the local resolver path. Fix parent-version filtering: an absent version with a parent range set must NOT pass through (previously version == null short-circuited the AND chain, letting unknown-version packages through). Extend parent-scoped override matching to also check workspace project manifests (importers), not just resolved packages. Importer snapshots don't carry name/version, so the scan now accepts a projectManifests list mapping importer IDs to manifests, letting parent-scoped overrides match project names like my-app>foo. Adds unit tests covering cross-version scoped overrides, genuinely unused overrides, absent parent versions, workspace-project parents, and non-semver parent ranges.
… assertion Collect chars directly into String instead of mapping to String per char. Check HashSet size and membership directly instead of collecting into a Vec of references to temporaries.
…ing, hoist enumerations
Rename pnpm:unusedOverride to pnpm:unused-override to match the
kebab-case convention used by every other multi-word pnpm log channel
(e.g. fetching-progress, package-manifest, lockfile-verification).
Updated across TS logger, reporter, tests, and pacquet serde mapping.
Add C1 control range (0x80-0x9F) to the TS sanitizeSelector regex;
Rust is already covered by char::is_control().
Add proc.on('error', reject) to the test spawnPnpm wrapper so spawn
failures fail fast instead of timing out.
Hoist Object.values/entries(lockfile.importers/packages) out of the
override loop in findAppliedOverrideSelectorsFromLockfile — they are
invariant across iterations.
…parent range Fix stale pnpm:unusedOverride reference in the changeset — the actual channel name is pnpm:unused-override (kebab-case). Move findAppliedOverrideSelectorsFromLockfile to a new internal module (src/install/findAppliedOverrides.ts) so api.ts's export * from install/index.js no longer exposes it as public API. The unit test imports from the new module directly. Guard semver.satisfies with semver.validRange and manifest.version != null in createVersionsOverrider's parent-scoped filter. Non-semver bareSpecifiers like 'latest' are treated as non-matching instead of potentially throwing. Adds a regression test for parent@latest>child.
Lockfile importer snapshots don't carry peerDependencies, so the pnpr-server scan missed parent-scoped overrides whose target only appears in a workspace project's peerDependencies. Check the project manifest's peerDependencies directly alongside the importer's dependencies/devDependencies/optionalDependencies. Adds unit test for the peer-dependency case.
b744003 to
cae43bd
Compare
…check helpers The unused-override gate compared lockfile importer count against projects.length (the mutation batch), but tryFrozenInstall uses Object.keys(ctx.projects).length (the full workspace set). In a partial install (--filter, pnpm add in one project) the batch is a subset, so the gate could misidentify a partial install as full reanalysis and emit false warnings. Use ctx.projects to match. Extract targetInImporter and targetInSnapshot helpers in findAppliedOverrides to deduplicate the depEntryMatches chains and keep the importer (deps/devDeps/optionalDeps) vs snapshot (deps/optionalDeps/peerDeps) group lists explicit and drift-proof.
|
Code review by qodo was updated up to the latest commit cae43bd |
|
Code review by qodo was updated up to the latest commit af82991 |
|
Code review by qodo was updated up to the latest commit af82991 |
|
@zkochan Can you please review the PR? It helps keep pnpm-workspace.yaml tidy as it help remove unused overrides. I usually delete the overrides one by one and run install to see if it they were actually changing anything. |
| for (const override of parsedOverrides) { | ||
| const targetName = override.targetPkg.name | ||
|
|
||
| if (override.parentPkg != null) { |
| const parentRange = override.parentPkg.bareSpecifier | ||
| const parentRangeValid = parentRange == null || semver.validRange(parentRange) != null | ||
|
|
||
| for (const { importerId, manifest: projectManifest } of projectManifests) { |
| } | ||
| if (applied.has(override.selector)) continue | ||
|
|
||
| for (const [depPath, snapshot] of packageEntries) { |
| } else { | ||
| for (const importer of importerSnapshots) { |
| } | ||
| } | ||
| if (applied.has(override.selector)) continue | ||
| for (const snapshot of packageSnapshots) { |
| * A selector is considered matched if its target name appears as a | ||
| * dependency key in any importer or package snapshot. The target's | ||
| * version range (bareSpecifier) is NOT checked against the resolved | ||
| * version because the lockfile stores post-override values — the | ||
| * override already changed the version, so comparing the new version | ||
| * against the old selector range would produce false positives (e.g. | ||
| * `foo@^1: 2.0.0` resolves to 2.0.0, which doesn't satisfy ^1). |
| /// Selectors arrive pre-sorted from the emission site | ||
| /// (`install_with_fresh_lockfile`), so no re-sort is needed here. | ||
| fn flush_pending_unused_overrides(&mut self) { |
| let selectors = std::mem::take(&mut self.pending_unused_overrides); | ||
| let sanitized: Vec<String> = | ||
| selectors.iter().map(|s| sanitize_override_selector(s)).collect(); |
| } else { | ||
| format!("{} overrides matched no dependency", sanitized.len()) | ||
| }; | ||
| self.push_warning(&format!("{}: {}", head, sanitized.join(", "))); |
| /// Strip ASCII control characters (C0 range 0x00–0x1F and DEL 0x7F) | ||
| /// from an override selector before rendering, so a crafted key | ||
| /// cannot inject/spoof terminal output. | ||
| fn sanitize_override_selector(selector: &str) -> String { | ||
| selector.chars().filter(|ch| !ch.is_control()).collect() | ||
| } |
…c comment Restore sort_unstable in flush_pending_unused_overrides so the reporter is resilient to unsorted event order — mirrors the TS reporter which sorts, and removes the fragile coupling to emission-site ordering. Fix sanitize_override_selector doc comment: char::is_control() strips all Unicode control characters (C0, DEL, C1, and other control code points), not just ASCII C0 + DEL.
|
Code review by qodo was updated up to the latest commit 1aeb576 |
| const resolutionDone$ = log$.stage.pipe( | ||
| filter((log) => log.stage === 'resolution_done') | ||
| ) | ||
| return log$.unusedOverride.pipe( | ||
| buffer(resolutionDone$), | ||
| map((unusedOverrides) => { | ||
| if (unusedOverrides.length === 0) return Rx.EMPTY | ||
| const selectors = unusedOverrides | ||
| .map((log) => sanitizeSelector(log.selector)) | ||
| .sort() | ||
| const head = selectors.length === 1 | ||
| ? '1 override matched no dependency' | ||
| : `${selectors.length} overrides matched no dependency` | ||
| return Rx.of({ | ||
| msg: formatWarn(`${head}: ${selectors.join(', ')}`), | ||
| }) | ||
| }) |
There was a problem hiding this comment.
1. Unused overrides buffer ignores prefix 📎 Requirement gap ≡ Correctness
reportUnusedOverrides() buffers all pnpm:unused-override events globally and flushes them on the first resolution_done stage event without partitioning by log.prefix. In recursive/multi-prefix runs this can mix selectors across workspaces/lockfileDirs or flush too early, leading to missing, incorrect, or ambiguous warnings that fail to clearly identify which override selectors matched nothing.
Agent Prompt
## Issue description
`reportUnusedOverrides()` currently ignores `UnusedOverrideLog.prefix`/`StageLog.prefix`, buffering all `pnpm:unused-override` events together and flushing them on any `resolution_done` stage event. In multi-prefix (recursive) installs this can merge selectors from different workspaces/lockfileDirs or flush before all selectors for a given prefix have arrived, producing missing, incorrect, or context-free warnings.
## Issue Context
- PR Compliance ID 1 requires `pnpm install` to emit a clear warning identifying which override selector(s) matched nothing.
- Each `pnpm:unused-override` (`UnusedOverrideLog`) and `pnpm:stage` (`StageLog`) event includes a `prefix` (workspace root / lockfileDir, typically `ctx.lockfileDir`).
- The warning needs to be accurate per-prefix; other reporters use `log.prefix` to disambiguate output (e.g., via zoom-out/prefix formatting).
## Fix Focus Areas
- pnpm11/cli/default-reporter/src/reporterForClient/reportUnusedOverrides.ts[19-43]
- pnpm11/cli/default-reporter/src/reporterForClient/index.ts[111-119]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| if lockfile_for_reuse.is_none() | ||
| && let Some(overrider) = versions_overrider.as_ref() | ||
| && let Some(parsed) = parsed_overrides.as_ref() | ||
| { | ||
| let applied = overrider.applied_selectors(); | ||
| let mut unused: Vec<&str> = parsed | ||
| .iter() | ||
| .map(|override_entry| override_entry.selector.as_str()) | ||
| .filter(|selector| !applied.contains(*selector)) | ||
| .collect(); |
There was a problem hiding this comment.
2. Pacquet gate skips updates 🐞 Bug ≡ Correctness
pacquet emits unused-override events only when lockfile_for_reuse.is_none(), but UpdateReuseScope::None disables reuse (full re-resolve) while lockfile_for_reuse can still be Some. This suppresses unused-override warnings in full re-resolution flows like pacquet update, undermining the feature.
Agent Prompt
### Issue description
The unused-override diff is gated by `lockfile_for_reuse.is_none()`, but pacquet can still perform a full re-resolve (no subtree reuse) when `update_reuse_scope` is `UpdateReuseScope::None` even if a prior lockfile exists. In that case `applied_selectors()` should be complete and the warning should run, but currently it is skipped.
### Issue Context
`UpdateReuseScope::None` explicitly means “reuse nothing — the whole graph re-resolves”, and the resolver’s reuse path short-circuits when this scope is set.
### Fix Focus Areas
- pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs[1048-1110]
- pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs[1202-1234]
- pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs[48-63]
- pacquet/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs[2089-2097]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
|
Code review by qodo was updated up to the latest commit 1aeb576 |
zkochan
left a comment
There was a problem hiding this comment.
I don't know if it is a good idea. On one hand it could be a problem if the override didn't work. On the other hand, maybe there is a big shared set of overrides shared across the org and it is not a problem if some repositories don't match all of them.
@zkochan do you want it as a |
Summary
Warn during
pnpm installwhen an entry inoverridesmatches no dependency in the resolved tree. The warning is buffered until theresolution_donestage and rendered as a single grouped line by the default reporter:Implemented in both the TypeScript CLI and the Rust
pacquet/port.Closes #10315.
How it works
createVersionsOverrideraccepts an optionalonAppliedcallback that fires whenever an override selector matches a manifest's dependency. The installer collects applied selectors into aSet<string>.pnpm:unused-overridelog event.forceFullResolution || empty packages && all importers present), matching the same gateverifyPatchesuses. This avoids false positives on frozen/partial installs where the applied set would be incomplete.reportUnusedOverridesreporter buffers events untilpnpm:stage { stage: 'resolution_done' }, then renders one sorted, grouped WARN line (singular/plural aware).parseWantedDependenciescallsonOverrideAppliedwhen an override applies to a directly-added dependency, sopnpm add foowith anoverrides: { foo: ... }entry is not falsely warned about.Public API additions
@pnpm/core-loggers— newpnpm:unused-overridechannel (unusedOverrideLogger,UnusedOverrideLog,UnusedOverrideMessage).@pnpm/hooks.read-package-hook—createVersionsOverrideracceptsonApplied;createReadPackageHookthreads it asonOverrideApplied.VersionOverrideWithoutRawSelectorkept as deprecated alias for backward compatibility.@pnpm/installing.deps-installer—ProcessedInstallOptionsexposesappliedOverrides: Set<string>.@pnpm/cli.default-reporter— newreportUnusedOverrideswired into the client reporter pipeline.Pacquet parity
VersionsOverriderrecords applied selectors viaArc<Mutex<HashSet<String>>>.install_with_fresh_lockfilediffs parsed overrides against applied selectors after resolution, emitsLogEvent::UnusedOverrideper unmatched selector, thenStage::ResolutionDone.ResolutionDone.Squash Commit Body
Checklist
pacquet/port, or the description notes what still needs porting.pnpm changeset) if this PR changes any publishedpackage. Keep it short and written for pnpm users — it becomes a release note.
Written by an agent (opencode, glm-5.2).
Summary by CodeRabbit
[WARN]when anoverridesselector matches no dependency.