* refactor(pnpr): rename pnpm-registry to pnpr Rename the registry server across the board to match the npm wrapper package name, which was already `@pnpm/pnpr`. - crate `pnpm-registry` -> `pnpr`, `pnpm-registry-fixtures` -> `pnpr-fixtures` - binaries `pnpm-registry` -> `pnpr`, `pnpm-registry-prepare` -> `pnpr-prepare` - module paths and log targets `pnpm_registry::*` -> `pnpr::*` - binary-locating env vars `PNPM_REGISTRY_BIN` -> `PNPR_BIN`, `PNPM_REGISTRY_PREPARE_BIN` -> `PNPR_PREPARE_BIN` - top-level directory `registry/` -> `pnpr/` (crates, npm wrapper, fixtures) The registry-mock storage concept is intentionally left as-is: `PNPM_REGISTRY_MOCK_PORT`/`PNPM_REGISTRY_MOCK_STORAGE`/`PNPM_REGISTRY_STORAGE`, the `~/.cache/pnpm-registry/storage` path + benchmark cache keys, and the external `pnpm-registry-mock` npm package referenced in test fixtures. * style(pnpr): rustfmt import grouping after rename * ci(pnpr): point typos at pnpr instead of removed registry dir * chore(pnpr): update pre-push path filter from registry to pnpr
8.5 KiB
Contributing to pacquet
See also CODE_STYLE_GUIDE.md for the code style guide.
Scope and Roadmap
pacquet's scope is defined by the roadmap in #299. The current focus is Stage 1 — Headless installer: making pacquet install --frozen-lockfile feature-complete with pnpm install --frozen-lockfile.
Stage 1 focuses on pacquet install and the settings and behavior needed to match pnpm install --frozen-lockfile. Other top-level commands exist in the CLI today, but they are not part of Stage 1 and are not receiving feature work, and new top-level commands are out of scope. pacquet is intended to be executed by the pnpm CLI under the hood, so configuration arrives through pnpm settings (such as .npmrc and pnpm-workspace.yaml) rather than through new command-line flags.
Before opening a pull request that adds a new setting or user-visible feature, confirm the feature is listed under Stage 1 of the roadmap. Work that does not appear under Stage 1 will not be reviewed or merged at this time, regardless of implementation quality. Stage 2 and later items are deferred until Stage 1 is complete.
Opening an issue first is optional when the change is in Stage 1 and the implementation exactly mirrors how the pnpm CLI works: same behavior, same defaults, same error codes, same file formats. See AGENTS.md for the parity rule. Open an issue first when the right approach is not obvious from upstream code, or to coordinate on in-flight work.
Deviating from pnpm's behavior is not an option in pacquet. If you believe pnpm itself should change, raise it in the pnpm repository first. Once the change has landed in pnpm and shipped, the corresponding port can be made here.
Bug fixes, performance improvements, tests, and documentation for behavior that already exists do not need a roadmap entry and may be sent directly as pull requests.
Pull requests for new top-level commands, or for features outside the current Stage 1 scope, will be closed with a pointer to the roadmap.
Commit Message Convention
This project uses Conventional Commits.
Format
type(scope): lowercase description
Rules
- Types:
feat,fix,refactor,perf,docs,style,chore,ci,test,lint. - Scopes (optional): a crate name (
cli,store,tarball,registry,lockfile,npmrc,network,fs,package-manager, etc.), or another relevant area such asdeps,readme,benchmark, ortoolchain. - Description: always lowercase after the colon, no trailing period, brief (3-7 words preferred).
- Breaking changes: append
!before the colon. For example:feat(cli)!: remove deprecated flag. - Code identifiers in descriptions should be wrapped in backticks. For example:
chore(deps): update `serde`.
There are no exceptions to this format. Version release commits follow the same rules as any other commit.
Writing Style
Write documentation, comments, and other prose for ease of understanding first. Prefer a formal tone when it does not hurt clarity, and use complete sentences. Avoid mid-sentence breaks introduced by em dashes or long parenthetical clauses. Em dashes are a reliable symptom of loose phrasing; when one appears, restructure the surrounding sentence so each clause stands on its own rather than swapping the em dash for another punctuation mark.
Code Style
See CODE_STYLE_GUIDE.md. Formatting and lint-level rules are enforced by cargo fmt, taplo format, and cargo clippy; the style guide covers everything those tools cannot enforce.
Dylint / perfectionist
A separate CI job (Dylint) runs perfectionist over the workspace. perfectionist is early, unstable software and is not yet battle-tested, so it can produce false positives and false negatives.
If perfectionist flags code that is actually correct, or fails to flag code its rule description says it should, do not work around the lint silently:
- Silence the specific finding at the affected site with
#[expect(perfectionist::rule_name, reason = "...")]. Always include areason, and write it as a sentence explaining why the lint is wrong here. Do not use#[allow(...)];#[expect]errors when the suppression is no longer needed, so the workaround disappears once perfectionist is fixed. - Open a new issue on
KSXGitHub/perfectionistdescribing the false positive or false negative, with a minimal repro, and tag/cc @KSXGitHubin the issue body.
The same procedure applies when a perfectionist rule itself is wrong — for example, a rule that flags an idiom the rule's documentation says it should permit. Silence the site with #[expect(..., reason = "...")], link the upstream issue from the reason if one already exists, and file the issue if it does not. Do not edit dylint.toml to globally disable a rule, and do not pin perfectionist to an older tag to dodge a finding.
You can run the same check locally with just dylint (requires cargo-dylint and dylint-link; install with cargo binstall cargo-dylint dylint-link).
Setup
Prerequisites
Install these first:
rustupcargo-binstalljust- Node.js
pnpmgit
Install
Install the project's task tools and the git pre-push hook:
just init
just init invokes cargo-binstall to install cargo-nextest, cargo-watch, cargo-insta, typos-cli, taplo-cli, wasm-pack, and cargo-llvm-cov. The repo-wide pnpm install wires up husky, whose pre-push hook runs pacquet/scripts/pre-push-rust.sh (format, doc, dylint) alongside the TypeScript compile and lint checks.
Install the test dependencies:
just install
Automated Checks
Before submitting, run:
just ready
This runs typos, cargo fmt, just check (which is cargo check --locked --workspace --all-targets), just test (which is cargo nextest run), and just lint (which is cargo clippy --locked --workspace --all-targets -- --deny warnings), then prints git status. CI runs the same commands on Linux, macOS, and Windows.
Important
Run
just readybefore every commit. This rule applies to all changes, including documentation edits, comment changes, and config updates. Any change can break formatting, linting, building, or tests across the supported platforms.
Note
Integration tests that need the local registry mock start
pnprautomatically. After dependencies are installed,cargo test,cargo nextest run, andjust testshould not require a separate registry process.
Debugging
Set the TRACE environment variable to enable trace-level logging for a given module:
TRACE=pacquet_tarball just cli add fastify
Testing
just install # install necessary dependencies
just test # run tests
When porting tests from the upstream pnpm/pnpm TypeScript repository, see
plans/TEST_PORTING.md. It tracks the tests
scheduled for porting (with upstream file paths and line numbers), the
expected layout for not-yet-implemented behavior (known_failures modules
guarded by pacquet_testing_utils::allow_known_failure!), and the
verification step of temporarily breaking the implementation to confirm a
ported test actually fails for the right reason before committing.
Benchmarking
First, start a local registry server, such as verdaccio:
verdaccio
Then use the integrated-benchmark task to run benchmarks. For example:
# Compare the branch you are working on against main
just integrated-benchmark --scenario=isolated-linker.fresh-restore.cold-cache.cold-store pacquet@my-branch pacquet@main
# Compare the current commit against the previous commit
just integrated-benchmark --scenario=isolated-linker.fresh-restore.cold-cache.cold-store pacquet@HEAD pacquet@HEAD~
# Compare pacquet of the current commit against pnpm
just integrated-benchmark --scenario=isolated-linker.fresh-restore.cold-cache.cold-store --with-pnpm pacquet@HEAD
# Compare pacquet of the current commit, pacquet of main, and pnpm against each other
just integrated-benchmark --scenario=isolated-linker.fresh-restore.cold-cache.cold-store --with-pnpm pacquet@HEAD pacquet@main
# See more options
just integrated-benchmark --help