You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
pacquet install (no flags) on a clean project does not produce a pnpm-lock.yaml. The install succeeds and node_modules/ is populated, but no lockfile is ever written. Re-running pacquet install in the same directory re-resolves everything against the registry instead of reusing a pinned graph.
--frozen-lockfile → frozen-lockfile path. Reads pnpm-lock.yaml, writes <virtual_store_dir>/lock.yaml (the current-lockfile snapshot), never touches pnpm-lock.yaml.
no flag + config.lockfile == true → returns InstallError::UnsupportedLockfileMode ("Installing with a writable lockfile is not yet supported.").
no flag + config.lockfile == false (pacquet's current default) → InstallWithoutLockfile: runs the resolver but discards everything except materialization output. No pnpm-lock.yaml is written.
What's already in place
Resolver is complete.resolving-deps-resolver produces a full transitive DependenciesGraph from a package.json, with auto-install-peers, abbreviated-metadata fast path, npm aliases, workspaces, catalogs, overrides, patches, runtime resolvers, etc. all landed.
Lockfile writer is complete.lockfile/src/save_lockfile.rs serializes a full Lockfile to pnpm-lock.yaml atomically, with round-trip tests.
InstallWithoutLockfile already runs the resolver (install_without_lockfile.rs) and uses the resulting graph to fetch, import, and materialize node_modules.
Missing piece
A single adapter that converts the resolver's DependenciesGraph into a Lockfile (its importers / packages / snapshots maps). The resolver output has everything the lockfile needs — dep_path, resolved_package_id, integrity, resolution, peer info, children — but no code today consumes the graph for anything other than materialization. The graph is built, walked once for node_modules, then dropped.
Honor included (--no-optional, --no-dev) so the lockfile reflects what the resolver actually produced.
In InstallWithoutLockfile::run, after the resolve completes, call the adapter and save_to_path the result to the workspace root.
Rename InstallWithoutLockfile → something like InstallWithFreshLockfile (the "without lockfile" framing stops being accurate once it writes one). Adjust the dispatch in install.rs to remove the UnsupportedLockfileMode branch.
Port the upstream pnpm tests for fresh-install lockfile shape into crates/package-manager/. The most relevant TS tests live in installing/deps-installer/test/lockfile.ts and installing/deps-installer/test/install/*.ts — pick the subset that doesn't depend on incremental update (that's issue 3).
Out of scope (separate issues)
GVS layout in the fresh-install path. The fresh-resolve path currently hardcodes VirtualStoreLayout::legacy. Switching it to GVS-when-enabled is its own design decision (the deliberate scoping comment at install.rs:581-585 needs to be revisited once the path actually has a lockfile to anchor a store-registry entry against). Tracked separately.
Stale-lockfile rewrite + preferFrozenLockfile dispatch. When a lockfile exists but doesn't match package.json, we should re-resolve and rewrite it (and gate on preferFrozenLockfile=true to keep the frozen path the default when the lockfile matches). Tracked separately.
Why this comes first
This is the smallest standalone unit: the adapter + writer hookup unblocks both follow-ups (issues 2 and 3 both want to mutate the lockfile produced by this path).
Written by an agent (Claude Code, claude-opus-4-7).
Problem
pacquet install(no flags) on a clean project does not produce apnpm-lock.yaml. The install succeeds andnode_modules/is populated, but no lockfile is ever written. Re-runningpacquet installin the same directory re-resolves everything against the registry instead of reusing a pinned graph.Root cause
The install dispatch in
pacquet/crates/package-manager/src/install.rshas three branches today and none of them writes a lockfile:--frozen-lockfile→ frozen-lockfile path. Readspnpm-lock.yaml, writes<virtual_store_dir>/lock.yaml(the current-lockfile snapshot), never touchespnpm-lock.yaml.config.lockfile == true→ returnsInstallError::UnsupportedLockfileMode("Installing with a writable lockfile is not yet supported.").config.lockfile == false(pacquet's current default) →InstallWithoutLockfile: runs the resolver but discards everything except materialization output. Nopnpm-lock.yamlis written.What's already in place
resolving-deps-resolverproduces a full transitiveDependenciesGraphfrom apackage.json, with auto-install-peers, abbreviated-metadata fast path, npm aliases, workspaces, catalogs, overrides, patches, runtime resolvers, etc. all landed.lockfile/src/save_lockfile.rsserializes a fullLockfiletopnpm-lock.yamlatomically, with round-trip tests.InstallWithoutLockfilealready runs the resolver (install_without_lockfile.rs) and uses the resulting graph to fetch, import, and materializenode_modules.Missing piece
A single adapter that converts the resolver's
DependenciesGraphinto aLockfile(itsimporters/packages/snapshotsmaps). The resolver output has everything the lockfile needs —dep_path,resolved_package_id, integrity, resolution, peer info, children — but no code today consumes the graph for anything other than materialization. The graph is built, walked once fornode_modules, then dropped.Proposed scope (this issue)
pub fn dependencies_graph_to_lockfile(graph: &DependenciesGraph, manifest: &PackageManifest, ...) -> Lockfilesomewhere appropriate (likelypacquet/crates/package-manager/).DependenciesGraphNode→SnapshotEntry(peer-suffixedPackageKeys included).importersmap (root importer for now; workspaces tracked separately at Add workspace support topacquet install --frozen-lockfilepacquet#431).packagesmap (PkgId→PackageMetadata).included(--no-optional,--no-dev) so the lockfile reflects what the resolver actually produced.InstallWithoutLockfile::run, after the resolve completes, call the adapter andsave_to_paththe result to the workspace root.InstallWithoutLockfile→ something likeInstallWithFreshLockfile(the "without lockfile" framing stops being accurate once it writes one). Adjust the dispatch ininstall.rsto remove theUnsupportedLockfileModebranch.crates/package-manager/. The most relevant TS tests live ininstalling/deps-installer/test/lockfile.tsandinstalling/deps-installer/test/install/*.ts— pick the subset that doesn't depend on incremental update (that's issue 3).Out of scope (separate issues)
VirtualStoreLayout::legacy. Switching it to GVS-when-enabled is its own design decision (the deliberate scoping comment atinstall.rs:581-585needs to be revisited once the path actually has a lockfile to anchor a store-registry entry against). Tracked separately.preferFrozenLockfiledispatch. When a lockfile exists but doesn't matchpackage.json, we should re-resolve and rewrite it (and gate onpreferFrozenLockfile=trueto keep the frozen path the default when the lockfile matches). Tracked separately.Why this comes first
This is the smallest standalone unit: the adapter + writer hookup unblocks both follow-ups (issues 2 and 3 both want to mutate the lockfile produced by this path).
Written by an agent (Claude Code, claude-opus-4-7).