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
Publish the crates that read a pnpm lockfile to crates.io, renamed from pacquet-* to pnpm-*, so tools outside this repository can read the resolved dependency graph without reimplementing a parser.
Every Rust crate here is currently publish = false (70 of 71 — pnpr-client is the exception).
Why
A lockfile is the only place the resolved dependency graph exists: exact versions, and which package pulled in what. Any tool that needs to reason about a project's real graph either reads it or guesses.
The concrete consumer is pnpm/xray, which reports dependencies that installed packages use but never declared, and has to decide whether each one should be proposed as an optional peer or as a regular dependency. That decision needs subgraph membership: is the phantom already reachable from the package's own dependencies?
The measurement is what makes this worth filing rather than working around. Against Yarn's compatibility database (133 hand-labelled entries):
rule
right
wrong
precision
phantom is in the package's subgraph
29
16
64%
ecosystem never declares it as a peer
56
78
42%
both
29
0
100%
Subgraph membership carries half the signal, and the error it removes is the one that breaks installs — proposing a regular dependency for something meant to be shared, which installs a second React or a second store. I measured it with a depth-2 registry approximation that unions dependencies across all versions, which over-approximates the graph. A lockfile closure is exact and strictly tighter.
Scope
pacquet-lockfile pulls in seven internal crates, so the closure is eight. Under the pnpm- prefix:
All eight names are free on crates.io. pnpm itself is already reserved by the pnpm team (placeholder 0.0.0, published 2024), so the prefix sits under a name the project already controls.
Eight crates is eight published API surfaces, and that is the real cost of this request.
The rename
pacquet is the in-repo identifier; the published CLI is pnpm. Anything on crates.io should carry the name people would search for, so the crates become pnpm-*.
Doing this only for the eight published crates would leave the workspace half-renamed, so the consistent version is all 71. That is mechanical but wide:
71 crate names across 75 Cargo.toml files
4,675 pacquet_* references across 806 Rust files
Worth doing in one commit, separate from any publishing change, so the diff stays reviewable as a pure rename.
[workspace.package].repository also still points at https://github.com/pnpm/pacquet, which would ship as metadata on every published crate.
Things worth deciding
Which crates are actually API, and which are incidental.pnpm-fs and pnpm-diagnostics are internal utilities that happen to sit on the path. A narrower read-only crate re-exporting just the lockfile types would publish one name instead of eight, at the cost of a thin wrapper to maintain.
What stability is being promised. These are at 0.0.1 and change freely with the CLI. Publishing invites external code to depend on them; 0.x communicates that, but only if the churn is expected.
Alternatives
Reimplement the parser in each tool. Works today, and drifts the moment the lockfile format changes — which is exactly the kind of duplication publishing avoids.
Extend pnpm why / pnpm list to answer graph queries as JSON. This may be the better answer for most consumers: no Rust coupling, no published API surface, and it puts the same information in users' hands directly. The cost is a process per query and a pnpm binary on PATH. If this is preferred, the interesting piece is a machine-readable "which packages can reach X, and through what" query.
Publish nothing; vendor the crates. Cheapest now, worst later.
I have no strong preference between publishing the crates and extending pnpm why — both solve xray's problem. Filing this to get the direction decided rather than to advocate for one.
This discussion was converted from issue #13947 on September 26, 2026 11:10.
Heading
Bold
Italic
Quote
Code
Link
Numbered list
Unordered list
Task list
Attach files
Mention
Reference
Menu
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
What
Publish the crates that read a pnpm lockfile to crates.io, renamed from
pacquet-*topnpm-*, so tools outside this repository can read the resolved dependency graph without reimplementing a parser.Every Rust crate here is currently
publish = false(70 of 71 —pnpr-clientis the exception).Why
A lockfile is the only place the resolved dependency graph exists: exact versions, and which package pulled in what. Any tool that needs to reason about a project's real graph either reads it or guesses.
The concrete consumer is pnpm/xray, which reports dependencies that installed packages use but never declared, and has to decide whether each one should be proposed as an optional peer or as a regular dependency. That decision needs subgraph membership: is the phantom already reachable from the package's own dependencies?
The measurement is what makes this worth filing rather than working around. Against Yarn's compatibility database (133 hand-labelled entries):
Subgraph membership carries half the signal, and the error it removes is the one that breaks installs — proposing a regular dependency for something meant to be shared, which installs a second React or a second store. I measured it with a depth-2 registry approximation that unions dependencies across all versions, which over-approximates the graph. A lockfile closure is exact and strictly tighter.
Scope
pacquet-lockfilepulls in seven internal crates, so the closure is eight. Under thepnpm-prefix:All eight names are free on crates.io.
pnpmitself is already reserved by the pnpm team (placeholder0.0.0, published 2024), so the prefix sits under a name the project already controls.Eight crates is eight published API surfaces, and that is the real cost of this request.
The rename
pacquetis the in-repo identifier; the published CLI ispnpm. Anything on crates.io should carry the name people would search for, so the crates becomepnpm-*.Doing this only for the eight published crates would leave the workspace half-renamed, so the consistent version is all 71. That is mechanical but wide:
Cargo.tomlfilespacquet_*references across 806 Rust filesWorth doing in one commit, separate from any publishing change, so the diff stays reviewable as a pure rename.
[workspace.package].repositoryalso still points athttps://github.com/pnpm/pacquet, which would ship as metadata on every published crate.Things worth deciding
pnpm-fsandpnpm-diagnosticsare internal utilities that happen to sit on the path. A narrower read-only crate re-exporting just the lockfile types would publish one name instead of eight, at the cost of a thin wrapper to maintain.0.0.1and change freely with the CLI. Publishing invites external code to depend on them;0.xcommunicates that, but only if the churn is expected.Alternatives
pnpm why/pnpm listto answer graph queries as JSON. This may be the better answer for most consumers: no Rust coupling, no published API surface, and it puts the same information in users' hands directly. The cost is a process per query and a pnpm binary on PATH. If this is preferred, the interesting piece is a machine-readable "which packages can reach X, and through what" query.I have no strong preference between publishing the crates and extending
pnpm why— both solve xray's problem. Filing this to get the direction decided rather than to advocate for one.Written by an agent (Claude Code, claude-opus-5).
All reactions