* chore(dylint): upgrade perfectionist to 0.0.0-rc.21
Bump the perfectionist dylint library from 0.0.0-rc.17 to 0.0.0-rc.21 and
migrate the configuration to the rc.21 rule names and semantics.
Renamed config keys (perfectionist renamed every lint to name the
anti-pattern it flags):
- macro_argument_binding -> impure_macro_arguments
- derive_ordering -> unordered_derives
- import_granularity -> import_granularity_mismatch
- unit_test_file_layout -> excessive_inline_tests
Remove three lint suppressions that upstream bug fixes made unnecessary
(each now reported an unfulfilled #[expect]):
- two bare_url suppressions on include_str!-ed fixtures (comment scanners
now ignore include_str!-ed files)
- the import_granularity suppression in resolve_importer tests (the rule
no longer folds a named item into self; the both-module-and-item case
is left unflagged by default)
flat_module_pattern was removed from perfectionist in favor of
clippy::mod_module_files, which this workspace already enables; update
CODE_STYLE_GUIDE.md to match.
Tune the heavy new active-by-default rules for this change:
- needless_borrowed_parameters: disabled pending a parity-reviewed pass
(https://github.com/pnpm/pnpm/issues/12715)
- bare_identifier_reference: narrowed to own-module, multi-word names
(https://github.com/pnpm/pnpm/issues/12716)
- clap_help_markdown: code spans ignored; link leaks still flagged
Simplify the executor test gate now that excessive_inline_tests accepts a
compound #[cfg(all(test, unix))] on an external test module.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WTm1H87D62ECyf9nLgPZkZ
* fix(lint): comply with new perfectionist rc.21 rules
Apply the rc.21 rule changes across the Rust workspace and tune the
heaviest new active-by-default rules in dylint.toml.
Code compliance:
- bare_identifier_reference: wrap backticked identifiers that resolve in
the documenting item's own module as intra-doc links (~197 sites). The
rule is narrowed to own-module, multi-word names; widening is tracked in
https://github.com/pnpm/pnpm/issues/12716.
- avoidable_string_escapes: convert escape-only string literals to raw
strings (74 sites).
- allow_attributes: convert #[allow] to #[expect] where the lint
deterministically fires (8 sites). Drop three suppressions whose lint
does not actually fire — a too_many_arguments on a 2-arg fn and two
large_stack_frames blocks already neutralised by Box::pin (the rule
flagged dead #[allow]s under its driver; #[expect] is unfulfilled under
stable clippy).
- wildcard_imports: expand `use super::*` to explicit imports in six test
modules (pacquet-cli, pnpr).
Config (dylint.toml): disable needless_borrowed_parameters
(https://github.com/pnpm/pnpm/issues/12715), unpinned_repo_ref
(https://github.com/pnpm/pnpm/issues/12717), and clap_help_markdown
(https://github.com/pnpm/pnpm/issues/12718) pending dedicated follow-up
passes.
Edits are limited to doc comments, raw-string conversions, lint
attributes, and import lists — no runtime behaviour changes. Verified
locally: cargo dylint, cargo doc, cargo clippy (all -D warnings), taplo
format --check, and the affected crates' unit tests all pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WTm1H87D62ECyf9nLgPZkZ
* test(executor): restore the original Unix-only test gate
The rc.21 upgrade moved the gate from a plain `#[cfg(test)] mod tests;`
(with `#![cfg(unix)]` inside the test file) to `#[cfg(all(test, unix))]`
on the module declaration. No lint required that move: rc.21's
`excessive_inline_tests` recognises the external test module regardless
of where the Unix gate sits, so the original form was already clean
(verified with `cargo dylint`). Revert it and trim the now-obsolete
workaround rationale from the comment — the rc.17 `unit_test_file_layout`
compound-`cfg` misclassification it dodged is fixed in rc.21.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WTm1H87D62ECyf9nLgPZkZ
* chore(dylint): trim stale-prone comments in dylint.toml
Remove the verbose rationale comment above the `disable` list — its
per-rule counts and narrative would drift out of date; the disabled rules
are tracked in their follow-up issues and the commit history. Shorten the
`bare_identifier_reference` comment to the configured intent plus its
tracking issue, dropping the firing-count and default-behaviour narrative.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WTm1H87D62ECyf9nLgPZkZ
* chore(dylint): disable perfectionist bare_identifier_reference
Its autofix blindly rewrites every doc code span into an intra-doc link,
which silently produces false intra-links when the span names an upstream
(TypeScript) type that only shares a name with a local Rust item. The rule
itself is sound, so disable it rather than keep narrowing it until the
autofix is removed or downgraded to MaybeIncorrect.
Keep the one site corrected by hand (graph-hasher dep_state) as a reference
for the right pattern: an explicit reference link to the upstream source
instead of a self-resolving intra-doc link.
Re-enable tracked in https://github.com/pnpm/pnpm/issues/12722.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WTm1H87D62ECyf9nLgPZkZ
* chore(executor): drop the doc-comment churn in the test-module gate
The earlier revert rewrote the gate comment in tests.rs instead of
restoring it, leaving a documentation-only diff against the base. Restore
the original comment verbatim so this file is unchanged by the PR.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WTm1H87D62ECyf9nLgPZkZ
* docs(pacquet): link false intra-doc links to their upstream source
Several doc comments wrapped an upstream (pnpm TypeScript / yarn) type or
const name in [`Name`], which resolved to a same-named local Rust item — a
false intra-link that reads as "upstream's Name" but points at the port.
Convert each to an explicit reference link to the upstream definition
([`Name`][ts-Name] plus a commit-pinned [ts-Name]: <url> permalink),
sourcing the URL from the doc block's existing citation, the file's
module-level reference, or (for the handful with no in-repo citation) the
upstream definition located at pnpm main 6fadd7def9. SINGLE_LINE_KEYS lives
in the out-of-scope zkochan/js-yaml fork, so it becomes a plain code span
rather than a fabricated permalink.
Doc-comment-only change. Verified with cargo doc, cargo dylint, and cargo
fmt (all -D warnings / --check), plus a rendered-HTML check that every
reference link resolves.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WTm1H87D62ECyf9nLgPZkZ
* docs(lockfile): permalink SINGLE_LINE_KEYS to the js-yaml fork source
github.com/zkochan/js-yaml returns 404 (the fork is not a public repo),
verified against known-good controls (nodeca/js-yaml, pnpm/pnpm, and the
zkochan user page all 200). A repo permalink therefore cannot be formed,
so point the reference at the immutable, version-pinned npm CDN copy of
the fork's dumper.js, where SINGLE_LINE_KEYS (cpu/engines/os/libc) is
defined at lines 63-68.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WTm1H87D62ECyf9nLgPZkZ
* docs(pacquet): pin upstream permalinks to their definition line ranges
Every upstream-source permalink added in this PR now carries a line anchor
(#Ln for a one-line definition, #Ln-Lm for a multi-line one) pointing at the
exact definition it cites, resolved against each link's pinned commit.
Three links pointed at a module that only imports or uses the symbol rather
than defining it; repoint them to the defining file: DepPath to
core/types/src/misc.ts, EnvLockfile to the lockfile.types index, and
InvalidWorkspaceManifestError to its dedicated error module.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WTm1H87D62ECyf9nLgPZkZ
* docs(pacquet): reword doc links that named non-existent upstream identifiers
Several doc comments linked a bracketed identifier that does not exist in the
upstream source. Replace each with plain English describing the code it
mirrors, keeping a permalink only where one resolves:
- render_specifier: upstream inlines the specifier string in its no-resolver
error rather than naming a function; link the error block instead.
- HoistingLimits: yarn exposes only the lowercase `hoistingLimits` option
(no named type); link that option's definition.
- ConfigFileChangeType / ConfigReport / EnvVariableChange: pacquet's own
names for report shapes from pnpm's os.env.path-extender package, which
lives outside the pnpm/pnpm monorepo (the old links 404'd). Name the
package without a dead link.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WTm1H87D62ECyf9nLgPZkZ
* docs: share reference
* docs(lockfile): point js-yaml fork links at pnpm/js-yaml
The `@zkochan/js-yaml` source repo is pnpm/js-yaml, not zkochan/js-yaml
(a hard 404). Repoint the module-level fork link there.
Also drop the `#L63-L68` anchor from the SINGLE_LINE_KEYS jsDelivr
reference: 0.0.11 has no tag in pnpm/js-yaml and the anchor is inert on
the raw CDN file, so it resolved to nothing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WTm1H87D62ECyf9nLgPZkZ
* fix(lint): resolve dylint findings surfaced by the main merge
Merging main brought two pre-existing items into the linted tree:
- pnpr storage tests imported `CachedTarballIntegrity` and
`TARBALL_INTEGRITY_SUFFIX`, but main removed their uses; drop the now
dead imports.
- integrated-benchmark's per-path `rm` retry command is escape-heavy as a
normal string literal; make it a raw string (avoidable_string_escapes).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WTm1H87D62ECyf9nLgPZkZ
* chore(rust/dylint): upgrade perfectionist to 0.0.0-rc.22 and re-enable bare_identifier_reference
rc.22 downgrades `bare_identifier_reference`'s autofix from
`MachineApplicable` to `MaybeIncorrect` and rewords its diagnostic to warn
that a doc code span may name a foreign symbol of the same name — the exact
fix that justified disabling the rule (pnpm/pnpm#12722). Re-enable it at the
previously narrowed scope (`reference_scope = "own_module"`, `min_words = 2`;
widening still tracked in pnpm/pnpm#12716).
Comply with the 8 sites it flags in the merged tree — all foreign/upstream
references, so each gets an explicit reference link to its upstream source
(or a reword), never a self-resolving intra-doc link:
- `DependencyManifest` (resolving-resolver-base): reword; the upstream type
is already linked earlier in the same doc.
- `WebAuthFetchOptions`, `WebAuthFetchResponse`, `WebAuthTimeoutError`,
`OtpErrorBody`, `SyntheticOtpError`, `OtpNonInteractiveError`,
`OtpSecondChallengeError` (the ported `@pnpm/network.web-auth` crate):
link to their definitions in the upstream TypeScript package.
No other rc.22 change affects the workspace: no rules added, removed, or
renamed; no rule config-schema change; same nightly toolchain.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WTm1H87D62ECyf9nLgPZkZ
* fix(lint): resolve rc.22 lint findings from the latest main merge
main's newly merged code tripped two rc.22 rules:
- The `repo` command's tests used `use super::*` (wildcard_imports);
expand it to the four items the tests actually use.
- The integrated-benchmark `BenchmarkScenario` enum carried an
`#[expect(clippy::enum_variant_names)]`. main's new
`IsolatedFreshRestoreColdCacheColdStoreColdPnpr` variant ends in
`Pnpr`, breaking the shared `...Store` suffix the lint had fired on, so
the lint no longer fires and the expectation is unfulfilled. The
suppression is dead (its own comment anticipated this once another
linker bucket landed) — drop it; `#[allow]` would instead trip the
re-enabled allow_attributes rule.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WTm1H87D62ECyf9nLgPZkZ
---------
Co-authored-by: Claude <noreply@anthropic.com>
简体中文 | 日本語 | 한국어 | Italiano | Português Brasileiro
Fast, disk space efficient package manager:
- Fast. Up to 2x faster than the alternatives (see benchmark).
- Efficient. Files inside
node_modulesare linked from a single content-addressable storage. - Great for monorepos.
- Strict. A package can access only dependencies that are specified in its
package.json. - Deterministic. Has a lockfile called
pnpm-lock.yaml. - Works as a Node.js version manager. See pnpm runtime.
- Works everywhere. Supports Windows, Linux, and macOS.
- Battle-tested. Used in production by teams of all sizes since 2016.
- Experimental Rust port. Includes pacquet, an experimental port of the CLI written in Rust.
- See the full feature comparison with npm and Yarn.
To quote the Rush team:
Microsoft uses pnpm in Rush repos with hundreds of projects and hundreds of PRs per day, and we’ve found it to be very fast and reliable.
Platinum Sponsors
|
|
|
Gold Sponsors
|
|
|
|
|
|
|
|
|
|
|
Silver Sponsors
|
|
|
|
|
|
|
|
|
|
|
| ⏱️ Time.now |
Support this project by becoming a sponsor.
Background
pnpm uses a content-addressable filesystem to store all files from all module directories on a disk. When using npm, if you have 100 projects using lodash, you will have 100 copies of lodash on disk. With pnpm, lodash will be stored in a content-addressable storage, so:
- If you depend on different versions of lodash, only the files that differ are added to the store.
If lodash has 100 files, and a new version has a change only in one of those files,
pnpm updatewill only add 1 new file to the storage. - All the files are saved in a single place on the disk. When packages are installed, their files are linked from that single place consuming no additional disk space. Linking is performed using either hard-links or reflinks (copy-on-write).
As a result, you save gigabytes of space on your disk and you have a lot faster installations!
If you'd like more details about the unique node_modules structure that pnpm creates and
why it works fine with the Node.js ecosystem, read this small article: Flat node_modules is not the only way.
💖 Like this project? Let people know with a tweet
Getting Started
Benchmark
pnpm is up to 2x faster than npm and Yarn classic. See all benchmarks here.
Benchmarks on an app with lots of dependencies:
License
MIT, except the pnpr/ directory, which is source-available under the PolyForm Shield License 1.0.0.