feat(pacquet): port lockfileOnly setting#12046
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (7)
🚧 Files skipped from review as they are similar to previous changes (4)
📜 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). (8)
🧰 Additional context used📓 Path-based instructions (2)pacquet/**/*.rs📄 CodeRabbit inference engine (pacquet/AGENTS.md)
Files:
pacquet/**/{tests,test}/*.rs📄 CodeRabbit inference engine (pacquet/AGENTS.md)
Files:
🧠 Learnings (3)📚 Learning: 2026-05-20T19:40:55.051ZApplied to files:
📚 Learning: 2026-05-22T00:08:44.646ZApplied to files:
📚 Learning: 2026-05-20T23:07:58.444ZApplied to files:
🔇 Additional comments (12)
📝 WalkthroughWalkthroughAdds a Changes--lockfile-only feature implementation
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 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 |
Micro-Benchmark ResultsLinux |
98e0ed7 to
2690e5b
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #12046 +/- ##
==========================================
+ Coverage 88.92% 88.94% +0.02%
==========================================
Files 235 235
Lines 30780 30839 +59
==========================================
+ Hits 27371 27430 +59
Misses 3409 3409 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Review Summary by QodoPort pnpm's lockfileOnly setting to pacquet
WalkthroughsDescription• Add --lockfile-only flag to install and add commands • Resolve dependencies and write pnpm-lock.yaml without fetching tarballs or materializing node_modules • Validate lockfile freshness when combining --frozen-lockfile with --lockfile-only • Fail fast when --lockfile-only conflicts with lockfile: false configuration • Add comprehensive test coverage porting pnpm's lockfileOnly test suite Diagramflowchart LR
CLI["CLI: --lockfile-only flag"]
CLI -->|install| Install["Install::run"]
CLI -->|add| Add["Add::run"]
Install -->|frozen path| FrozenPath["Re-persist lockfile<br/>Return early"]
Install -->|fresh path| Fresh["InstallWithFreshLockfile"]
Fresh -->|skip prefetch| Resolve["Resolve graph<br/>No tarball fetch"]
Resolve -->|write lockfile| Write["Write pnpm-lock.yaml<br/>Return early"]
Add -->|mutate manifest| AddManifest["Update package.json"]
AddManifest -->|forward flag| Install
Validate["Config validation<br/>lockfile-only + lockfile:false<br/>= error"]
CLI -.->|validate| Validate
File Changes1. pacquet/crates/cli/src/cli_args/install.rs
|
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 `@pacquet/crates/cli/tests/lockfile_only.rs`:
- Around line 11-12: The file currently re-exports the local module via a glob
(pub use _utils::*), which violates the no-local-glob rule; remove the glob and
either keep the module private (delete the pub use line) or replace it with
explicit re-exports of the specific symbols you need from module _utils (e.g.,
pub use _utils::{ThingA, helper_fn}) so only named items are re-exported; update
any call sites to import the explicit names if you choose to make the module
private.
- Around line 33-35: The cas_blobs helper is brittle because it checks
path.contains("files/") which fails on Windows; change the filter to be
path-separator-agnostic by inspecting path components instead (e.g. call
get_all_files(store_dir).into_iter().filter(|path|
Path::new(path).components().any(|c| c.as_os_str() == "files")).collect()), and
ensure you convert the matched Path/OsStr back to String consistently (e.g.
to_string_lossy().into_owned()) so cas_blobs still returns Vec<String>;
reference functions: cas_blobs and get_all_files.
🪄 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: dfd4107d-fa2b-4c63-8aaa-7b0e9d4d4bf6
📒 Files selected for processing (7)
pacquet/crates/cli/src/cli_args/add.rspacquet/crates/cli/src/cli_args/install.rspacquet/crates/cli/tests/lockfile_only.rspacquet/crates/package-manager/src/add.rspacquet/crates/package-manager/src/install.rspacquet/crates/package-manager/src/install/tests.rspacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: ubuntu-latest / Node.js 22.13.0 / Test
- GitHub Check: windows-latest / Node.js 22.13.0 / Test
- GitHub Check: Run benchmark on ubuntu-latest
- GitHub Check: Lint and Test (ubuntu-latest)
- GitHub Check: Lint and Test (macos-latest)
🧰 Additional context used
📓 Path-based instructions (2)
pacquet/**/*.rs
📄 CodeRabbit inference engine (pacquet/AGENTS.md)
pacquet/**/*.rs: Warnings are errors (--deny warningsin lint). Do not silence them with#[allow(...)]unless there is a specific, justified reason.
Choose owned vs. borrowed parameters to minimize copies; widen to the most encompassing type (&Pathover&PathBuf,&strover&String) when it doesn't force extra copies.
PreferArc::clone(&x)/Rc::clone(&x)overx.clone()for reference-counted types, so the cost is visible at the call site.
Follow Rust API Guidelines for naming: https://rust-lang.github.io/api-guidelines/naming.html
No star imports inside module bodies. Writeuse super::{Foo, bar}instead ofuse super::*;, and the same for any other glob whose target is a module you control. Two forms stay allowed: external-crate preludes such asuse rayon::prelude::*;and root-of-module re-exports such aspub use submodule::*;in alib.rs.
Doc comments (///,//!) document the contract: preconditions, postconditions, panics, the reason the function exists. They are not a re-narration of the body.
Do not restate at call sites what the callee's doc comment already says. If///on the function says 'no-op when …', the caller should not repeat that. Update the doc once; let every call site benefit.
Tests are documentation. Do not duplicate them in prose. If a behavioral scenario, edge case, failure mode, or worked example is already captured by a test, do not also narrate it in the doc comment on the implementation. The same applies in reverse: a test's own doc comment should not re-explain what the asserts already say, only the why if it is not obvious.
Use// SAFETY:,// TODO:, and similar prefixes to signal hidden invariants or known follow-ups that a reader cannot recover from the code alone.
When editing existing code, do not break a method chain (includingpipe-trait.pipe(...)chains) into intermediateletbindings unless you can justify the rewrite. Valid justifications include a chain that fails to compile after your...
Files:
pacquet/crates/package-manager/src/add.rspacquet/crates/cli/src/cli_args/add.rspacquet/crates/cli/src/cli_args/install.rspacquet/crates/package-manager/src/install.rspacquet/crates/package-manager/src/install_with_fresh_lockfile.rspacquet/crates/cli/tests/lockfile_only.rspacquet/crates/package-manager/src/install/tests.rs
pacquet/**/{tests,test}/*.rs
📄 CodeRabbit inference engine (pacquet/AGENTS.md)
pacquet/**/{tests,test}/*.rs: Tests must not be tolerant of a missing build / runtime environment by silently returning early when a tool isn't found. If the test needs a tool, just call into it and let the existing.unwrap()/.expect(...)panic when the tool is absent.
Prefer platform-specific gates like#[cfg_attr(target_os = "windows", ignore = "...")]or#[cfg(unix)]over runtime probe-and-skip helpers for platform-locked tools, because the gate is visible tocargo testand shows up in the test report.
Follow the test-logging guidance in the style guide — log before non-assert_eq!assertions,dbg!complex structures, skip logging for simple scalarassert_eq!.
Files:
pacquet/crates/cli/tests/lockfile_only.rs
🧠 Learnings (3)
📚 Learning: 2026-05-20T19:40:55.051Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11774
File: pacquet/crates/resolving-deps-resolver/src/resolve_peers.rs:0-0
Timestamp: 2026-05-20T19:40:55.051Z
Learning: In the pacquet Rust code, ensure the semver implementation uses the `node-semver` crate (not `nodejs-semver`). `node-semver`’s public API does not include a `satisfies_with_prerelease`-style method; prerelease-tolerant matching should be implemented inline by first calling `Range::satisfies`, and when it rejects a prerelease version, retry matching against a stripped `MAJOR.MINOR.PATCH` base of the prerelease version.
Applied to files:
pacquet/crates/package-manager/src/add.rspacquet/crates/cli/src/cli_args/add.rspacquet/crates/cli/src/cli_args/install.rspacquet/crates/package-manager/src/install.rspacquet/crates/package-manager/src/install_with_fresh_lockfile.rspacquet/crates/cli/tests/lockfile_only.rspacquet/crates/package-manager/src/install/tests.rs
📚 Learning: 2026-05-22T00:08:44.646Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11837
File: pacquet/crates/resolving-npm-resolver/src/pick_package.rs:33-51
Timestamp: 2026-05-22T00:08:44.646Z
Learning: In the pnpm/pnpm repo’s pacquet Rust crates, do not flag Unicode ellipsis characters (U+2026, `…`) in Rust doc comments (`///` / `/** */`) as a lint violation. The pacquet crate’s `dylint.toml` only enables `perfectionist::derive_ordering`, and the Dylint `unicode-ellipsis` rule is not enabled for this project—so `…` in doc comments is an intentional, repo-consistent style.
Applied to files:
pacquet/crates/package-manager/src/add.rspacquet/crates/cli/src/cli_args/add.rspacquet/crates/cli/src/cli_args/install.rspacquet/crates/package-manager/src/install.rspacquet/crates/package-manager/src/install_with_fresh_lockfile.rspacquet/crates/cli/tests/lockfile_only.rspacquet/crates/package-manager/src/install/tests.rs
📚 Learning: 2026-05-20T23:07:58.444Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11784
File: pacquet/crates/resolving-deps-resolver/src/hoist_peers.rs:120-133
Timestamp: 2026-05-20T23:07:58.444Z
Learning: When reviewing code in this pacquet Rust port, follow the upstream pnpm compatibility rule: only match pnpm’s behavior exactly. Do not propose review changes that intentionally deviate from pnpm’s documented/observed behavior, even if pnpm appears buggy. If you identify a real bug in pnpm behavior, the review should prioritize fixing it upstream in pnpm first, and avoid implementing a pnpm-behavior workaround here unless the same fix has already landed upstream.
Applied to files:
pacquet/crates/package-manager/src/add.rspacquet/crates/cli/src/cli_args/add.rspacquet/crates/cli/src/cli_args/install.rspacquet/crates/package-manager/src/install.rspacquet/crates/package-manager/src/install_with_fresh_lockfile.rspacquet/crates/cli/tests/lockfile_only.rspacquet/crates/package-manager/src/install/tests.rs
🔇 Additional comments (7)
pacquet/crates/package-manager/src/install/tests.rs (1)
69-69: LGTM!Also applies to: 139-139, 184-184, 258-258, 325-325, 409-409, 478-478, 567-567, 713-713, 815-815, 1017-1017, 1091-1091, 1188-1188, 1319-1319, 1418-1418, 1525-1525, 1576-1576, 1669-1669, 1765-1765, 1830-1830, 1896-1896, 1988-1988, 2096-2096, 2158-2158, 2247-2247, 2355-2355, 2470-2470, 2551-2551, 2751-2751, 2876-2876, 2977-2977, 3084-3084, 3176-3176, 3271-3271, 3363-3363, 3452-3452, 3548-3548, 3641-3641, 3739-3739, 3840-3840, 3927-3927, 4012-4012, 4083-4083, 4150-4150, 4216-4216, 4285-4285, 4370-4370, 4433-4433, 4519-4519, 4571-4571, 4643-4643, 4716-4716, 4783-4783, 5036-5036, 5218-5218, 5367-5367, 5517-5517, 5596-5596, 5648-5648, 5756-5756, 5853-5853
pacquet/crates/cli/tests/lockfile_only.rs (1)
1-10: LGTM!Also applies to: 14-32, 37-284
pacquet/crates/cli/src/cli_args/add.rs (1)
85-89: LGTM!Also applies to: 132-132
pacquet/crates/cli/src/cli_args/install.rs (1)
83-88: LGTM!Also applies to: 214-214, 291-291
pacquet/crates/package-manager/src/install.rs (1)
140-152: LGTM!Also applies to: 309-319, 346-346, 349-356, 736-756, 865-865, 868-868, 940-940, 966-969
pacquet/crates/package-manager/src/add.rs (1)
35-38: LGTM!Also applies to: 72-72, 118-118
pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs (1)
148-154: LGTM!Also applies to: 324-324, 501-522, 717-760
Integrated-Benchmark Report (Linux)Scenario: Isolated linker: fresh restore, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 2.10619682816,
"stddev": 0.23389690801367494,
"median": 2.03772575316,
"user": 2.6839114599999996,
"system": 3.2414313200000002,
"min": 1.9506793126600002,
"max": 2.75222574966,
"times": [
2.0431120156600002,
2.14113537766,
2.03332842966,
2.03887764866,
2.03657385766,
1.9506793126600002,
1.9719466796600003,
2.10145188666,
2.75222574966,
1.9926373236600001
]
},
{
"command": "pacquet@main",
"mean": 1.9879522551600002,
"stddev": 0.05653843189216168,
"median": 1.9743547011600002,
"user": 2.6942929599999994,
"system": 3.19173012,
"min": 1.9264751206600002,
"max": 2.12484721666,
"times": [
1.9264751206600002,
1.96949812166,
1.9907137386600002,
2.12484721666,
1.9492454006600002,
1.9865213536600002,
1.9792112806600002,
1.9578569806600001,
2.03778762666,
1.95736571166
]
}
]
}Scenario: Isolated linker: fresh restore, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 0.6461327111,
"stddev": 0.01328872438336953,
"median": 0.6434235424000001,
"user": 0.36951024,
"system": 1.31888886,
"min": 0.6232941314000001,
"max": 0.6736648214000001,
"times": [
0.6736648214000001,
0.6232941314000001,
0.6420423574,
0.6364569384000001,
0.6426479474000001,
0.6441991374000001,
0.6493818824,
0.6558997654000001,
0.6406886314000001,
0.6530514984000001
]
},
{
"command": "pacquet@main",
"mean": 0.6583893615,
"stddev": 0.013929173203895603,
"median": 0.6551761724,
"user": 0.36459684,
"system": 1.3290545599999999,
"min": 0.6378143264000001,
"max": 0.6896485804000001,
"times": [
0.6896485804000001,
0.6378143264000001,
0.6555069904,
0.6499928154000001,
0.6521966204,
0.6625356674000001,
0.6712636634000001,
0.6573571754,
0.6548453544000001,
0.6527324214000001
]
}
]
}Scenario: Isolated linker: fresh install, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 2.3317092817800003,
"stddev": 0.029738614634645853,
"median": 2.34310809658,
"user": 3.8818582,
"system": 3.03535138,
"min": 2.29354436958,
"max": 2.38573571958,
"times": [
2.34254785158,
2.34366834158,
2.29354436958,
2.29618661058,
2.31539739558,
2.34423987858,
2.29900950258,
2.38573571958,
2.35096969258,
2.34579345558
]
},
{
"command": "pacquet@main",
"mean": 2.3341033367799997,
"stddev": 0.02248033575467846,
"median": 2.34144389158,
"user": 3.8430397,
"system": 3.0485176800000002,
"min": 2.29798132658,
"max": 2.3603300905799998,
"times": [
2.3603300905799998,
2.34342933758,
2.33762647358,
2.35338760658,
2.29992254558,
2.31248604358,
2.29798132658,
2.33945844558,
2.3448658185799998,
2.35154567958
]
}
]
}Scenario: Isolated linker: fresh install, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 1.52508339606,
"stddev": 0.018502043665102358,
"median": 1.5246331340600001,
"user": 1.7524151000000003,
"system": 1.82179768,
"min": 1.4851054240600001,
"max": 1.5469693880600002,
"times": [
1.5469693880600002,
1.51199292106,
1.4851054240600001,
1.5243261240600001,
1.5341115120600002,
1.5431134700600002,
1.5249401440600001,
1.51874317606,
1.5434223340600002,
1.5181094670600002
]
},
{
"command": "pacquet@main",
"mean": 1.5556893176600002,
"stddev": 0.04943078561416003,
"median": 1.5357794260600002,
"user": 1.7304346999999995,
"system": 1.8646747799999996,
"min": 1.5096290900600002,
"max": 1.66758604906,
"times": [
1.5476894360600002,
1.5230548940600002,
1.66758604906,
1.59612995206,
1.59543004006,
1.5387068570600002,
1.5276600630600001,
1.5328519950600001,
1.51815480006,
1.5096290900600002
]
}
]
} |
|
| Branch | pr/12046 |
| Testbed | pacquet |
Click to view all benchmark results
| Benchmark | Latency | Benchmark Result milliseconds (ms) (Result Δ%) | Upper Boundary milliseconds (ms) (Limit %) |
|---|---|---|---|
| isolated-linker.fresh-install.cold-cache.cold-store | 📈 view plot 🚷 view threshold | 2,331.71 ms(-0.44%)Baseline: 2,342.00 ms | 2,810.40 ms (82.97%) |
| isolated-linker.fresh-install.hot-cache.hot-store | 📈 view plot 🚷 view threshold | 1,525.08 ms(+4.57%)Baseline: 1,458.40 ms | 1,750.09 ms (87.14%) |
| isolated-linker.fresh-restore.cold-cache.cold-store | 📈 view plot 🚷 view threshold | 2,106.20 ms(+0.74%)Baseline: 2,090.77 ms | 2,508.92 ms (83.95%) |
| isolated-linker.fresh-restore.hot-cache.hot-store | 📈 view plot 🚷 view threshold | 646.13 ms(-5.07%)Baseline: 680.65 ms | 816.78 ms (79.11%) |
Ports pnpm's `--lockfile-only` to pacquet: resolve dependencies and write `pnpm-lock.yaml` without fetching any tarball into the store or materializing `node_modules`. `lockfile-only` is in pnpm's `excludedPnpmKeys` (like `frozen-lockfile`), so it's a pure per-invocation CLI flag with no `pnpm-workspace.yaml` / `config.yaml` counterpart. It's threaded straight from the CLI through `Install` / `Add`, mirroring the existing `frozen_lockfile` plumbing — no config-crate changes. - `--lockfile-only` flag added to `install` and `add`. - `Install::run`: frozen / auto-frozen + `--lockfile-only` validates the on-disk lockfile (errors on stale, matching pnpm), re-persists the wanted lockfile, and returns without materializing. Otherwise the fresh-resolve path runs and `.modules.yaml`, the current lockfile, and the workspace-state file are skipped (mirrors pnpm skipping `updateWorkspaceState` under `lockfileOnly`). - `InstallWithFreshLockfile::run`: skips the `PrefetchingResolver` wrapper so no tarball is fetched (matching pnpm's `dryRun: opts.lockfileOnly`), then writes `pnpm-lock.yaml` and returns before prefetch / virtual-store / symlink / hoist / bin steps. - `--lockfile-only` with `lockfile: false` (pnpm's `useLockfile: false`) now fails with `ERR_PNPM_CONFIG_CONFLICT_LOCKFILE_ONLY_WITH_NO_LOCKFILE`, matching pnpm's `extendInstallOptions` guard. Tests: new `crates/cli/tests/lockfile_only.rs` ports every lockfileOnly test from `installing/deps-installer/test/` — the two in `install/lockfileOnly.ts` (resolve-and-write with no download/link; `--frozen-lockfile --lockfile-only` rejects a stale lockfile) plus the two lockfileOnly-focused cases in `lockfile.ts` (the `useLockfile: false` conflict and the workspace "new project added" importer update).
2690e5b to
9d42340
Compare
…2046) Ports pnpm's `--lockfile-only` to pacquet: resolve dependencies and write `pnpm-lock.yaml` without fetching any tarball into the store or materializing `node_modules`. `lockfile-only` is in pnpm's `excludedPnpmKeys` (like `frozen-lockfile`), so it's a pure per-invocation CLI flag with no `pnpm-workspace.yaml` / `config.yaml` counterpart. It's threaded straight from the CLI through `Install` / `Add`, mirroring the existing `frozen_lockfile` plumbing — no config-crate changes. - `--lockfile-only` flag added to `install` and `add`. - `Install::run`: frozen / auto-frozen + `--lockfile-only` validates the on-disk lockfile (errors on stale, matching pnpm), re-persists the wanted lockfile, and returns without materializing. Otherwise the fresh-resolve path runs and `.modules.yaml`, the current lockfile, and the workspace-state file are skipped (mirrors pnpm skipping `updateWorkspaceState` under `lockfileOnly`). - `InstallWithFreshLockfile::run`: skips the `PrefetchingResolver` wrapper so no tarball is fetched (matching pnpm's `dryRun: opts.lockfileOnly`), then writes `pnpm-lock.yaml` and returns before prefetch / virtual-store / symlink / hoist / bin steps. - `--lockfile-only` with `lockfile: false` (pnpm's `useLockfile: false`) now fails with `ERR_PNPM_CONFIG_CONFLICT_LOCKFILE_ONLY_WITH_NO_LOCKFILE`, matching pnpm's `extendInstallOptions` guard. Tests: new `crates/cli/tests/lockfile_only.rs` ports every lockfileOnly test from `installing/deps-installer/test/` — the two in `install/lockfileOnly.ts` (resolve-and-write with no download/link; `--frozen-lockfile --lockfile-only` rejects a stale lockfile) plus the two lockfileOnly-focused cases in `lockfile.ts` (the `useLockfile: false` conflict and the workspace "new project added" importer update).
Ports pnpm's
lockfileOnly/--lockfile-onlyto pacquet — one of the items tracked in #12042: resolve dependencies and writepnpm-lock.yamlwithout fetching any tarball into the store or materializingnode_modules.Design
lockfile-onlylives in pnpm'sexcludedPnpmKeys(alongsidefrozen-lockfile), so it is a pure per-invocation CLI flag — not a validpnpm-workspace.yaml/config.yamlkey. It is therefore threaded straight from the CLI throughInstall/Add, mirroring the existingfrozen_lockfileplumbing. No config-crate changes.What changed
--lockfile-onlyadded to bothinstallandadd.Install::run:--frozen-lockfile/ auto-frozen (preferFrozenLockfile) +--lockfile-only: the freshness gate still runs (a stale lockfile surfacesOutdatedLockfile, matching pnpm), then the wanted lockfile is re-persisted and the run returns without touchingnode_modules. Mirrors pnpm'sindex.ts#L979-L986..modules.yaml, the current lockfile, and the workspace-state file are skipped — there is nonode_modulesto describe, and writing the workspace-state file would make the next install's up-to-date check believe materialization happened (pnpm skipsupdateWorkspaceStateunderlockfileOnly).--lockfile-onlywithlockfile: false(pnpm'suseLockfile: false) fails fast withERR_PNPM_CONFIG_CONFLICT_LOCKFILE_ONLY_WITH_NO_LOCKFILE, matching pnpm'sextendInstallOptionsguard.InstallWithFreshLockfile::run: skips thePrefetchingResolverwrapper so no tarball is fetched (matching pnpm'sdryRun: opts.lockfileOnly), then writespnpm-lock.yamland returns before the prefetch / virtual-store / symlink / hoist / bin steps.Tests
New
pacquet/crates/cli/tests/lockfile_only.rsports every lockfileOnly-focused test frominstalling/deps-installer/test/:writes_lockfile_without_downloading_or_linking— portsinstall/lockfileOnly.ts"install with lockfileOnly = true": lockfile written (direct + transitive deps), nonode_modules, no CAS blob in the store, repeat-run stable, follow-up ordinary install materializes.frozen_lockfile_only_rejects_a_stale_lockfile— portsinstall/lockfileOnly.ts"do not update the lockfile when lockfileOnly and frozenLockfile are both used".lockfile_false_with_lockfile_only_is_a_config_conflict— portslockfile.ts"fail when installing with useLockfile: false and lockfileOnly: true".lockfile_only_updates_importers_when_a_project_is_added— portslockfile.ts"update the lockfile when a new project is added to the workspace and lockfile-only installation is used". The second run uses--no-prefer-frozen-lockfileto force re-resolution: pacquet's auto-frozen freshness gate validates only the root importer today, so it wouldn't otherwise notice a newly-added sibling (pre-existing workspace-freshness limitation, independent of this change).All other
lockfileOnlyreferences in the upstream test suite (deepRecursive,catalogs,peerDependencies, …) merely use it as setup to generate a lockfile and aren't lockfileOnly behavior tests.The 61 existing
Install { … }literals ininstall/tests.rswere updated withlockfile_only: false.cargo check/clippy --deny warnings/fmtclean; the 4 new tests pass,pacquet-package-manager365/365,pacquet-cliadd+install green.Pacquet-only change (the pnpm CLI already implements
lockfileOnly), so no changeset.Written by an agent (Claude Code, claude-opus-4-8).
Summary by CodeRabbit
New Features
--lockfile-onlytopacquet addandpacquet installto update the lockfile without downloading deps or creatingnode_modules, matching pnpm behavior--lockfile-onlywhen lockfile writing is disabledTests