The TypeScript pnpm CLI freezes at v11; pnpm 12 will be the Rust pacquet port. To make that split legible, all TypeScript source, test, and build directories move under a new top-level pnpm11/ directory. The name states the version boundary rather than implying a behavioral fork, since the two stacks are meant to behave identically. Scope is source-only: the shared workspace root stays at the repo root. pnpm-workspace.yaml, package.json, pnpm-lock.yaml, .pnpmfile.cjs, .meta-updater, __patches__, .changeset, .husky, and the lint/spell configs remain in place, so one pnpm workspace and one Cargo workspace still span all three products. pnpr/client and pacquet/tasks/registry-mock stay as cross-product workspace members. Rewiring the move required: - pnpm-workspace.yaml globs prefixed with pnpm11/ - root package.json script paths, eslint.config.mjs, tsconfig.lint.json, .gitignore, and CODEOWNERS updated - .meta-updater/src/index.ts literals repointed (pnpm11/pnpm/package.json, pnpm11/__utils__, pnpm11/__typings__, and the main package directory) - regenerated every moved package's repository/homepage URL via meta-updater - pnpm11/pnpm/bundle-deps.ts and __utils__/scripts/src/typecheck-only.ts climb one more level to reach the repo root .meta-updater stays at the repo root because @pnpm/meta-updater resolves its config at <cwd>/.meta-updater/main.mjs. TS CI (.github/workflows/ci.yml) now only runs when pnpm11/-relevant paths change, via a dorny/paths-filter changes job plus a TS CI / Success aggregate gate; branch protection should require only that gate.
pnpm Benchmarks
Compares pnpm install performance between the current branch (HEAD) and
main, across the six scenarios listed below.
This wrapper builds both pnpm revisions and runs hyperfine through the
shared Rust orchestrator at
pacquet/tasks/integrated-benchmark,
so scenario / fixture / workspace / install-script / report generation
stay consistent with the pacquet benchmark.
Prerequisites
cargo(install Rust via rustup if you don't have it).hyperfine,pnpm,node,giton$PATH.
Usage
./benchmarks/bench.sh
The script:
- Builds the
integrated-benchmarkbinary in release mode. - Clones the current repo into the temp work-env once per revision
(
HEADandmain) and runspnpm install && pnpm run compile-onlyin each to producepnpm/dist/pnpm.mjs.compile-onlyskips theupdate-manifestspass that the rootcompilescript does — it would rewrite tracked files and trigger a second install per revision, neither of which the bench needs. - Runs hyperfine on each scenario with
--registry=npm(hitsregistry.npmjs.orgdirectly, no proxy — same as before). - Writes a per-scenario
BENCHMARK_REPORT.md/.jsonand a consolidatedresults.mdinto the temp work-env. The path is printed at the end of the run. - Emits
bencher-results.json— a hyperfine-shaped file with one result per scenario (the@HEADrevision only,commandrenamed to the scenario name) that theBenchmarksGitHub Actions workflow uploads to Bencher for continuous tracking.
Scenarios
Slugs follow <linker>.<action>.<cache state>.<store state> so the
leading segment groups runs by linker mode. Today there are two
groups (isolated-linker.* and gvs-linker.*); future scenarios
will add hoisted-linker.* and pnp-linker.*.
Every current scenario starts with node_modules wiped — "fresh"
names that target state; future variants that begin with a populated
node_modules will use a different action prefix.
| # | Slug | Lockfile | Cache | Store | Description |
|---|---|---|---|---|---|
| 1 | isolated-linker.fresh-restore.hot-cache.hot-store |
✔ frozen | hot | hot | Restore from lockfile with both directories hot (repeat-headless shape) |
| 2 | isolated-linker.fresh-add-dep.hot-cache.hot-store |
✔ + add dep | hot | hot | pnpm add <dep> against an existing lockfile |
| 3 | isolated-linker.fresh-install.hot-cache.hot-store |
✗ | hot | hot | Resolve from scratch with both directories hot |
| 4 | isolated-linker.fresh-restore.cold-cache.cold-store |
✔ frozen | cold | cold | Restore from lockfile with cold disks (typical CI shape) |
| 5 | isolated-linker.fresh-install.cold-cache.cold-store |
✗ | cold | cold | True cold start — no lockfile, nothing cached |
| 6 | gvs-linker.fresh-restore.hot-cache.hot-store |
✔ frozen | hot | hot + GVS | Frozen-lockfile restore with enableGlobalVirtualStore: true, pre-warmed GVS |
All scenarios use --ignore-scripts and isolated store/cache directories per revision.
Fixture
The fixture lives at fixture/ — a synthetic
package.json with ~80 typical front-end dependencies, plus a committed
pnpm-lock.yaml (generated once with pnpm install --lockfile-only).
The lockfile is checked in so every CI run starts from the same
resolution graph regardless of registry drift.
Configuration
Environment variables read by bench.sh:
WARMUP— number of warmup runs before timing (default: 1)RUNS— number of timed runs per benchmark (default: 10)