SIGN IN SIGN UP

chore: migrate to pnpm (#4762)

* chore: migrate package manager from yarn to pnpm

Switch from Yarn Classic (1.22, EOL) to pnpm for stricter dependency
isolation, better lockfile integrity, and continued upstream security
fixes.

Repo-level changes:
- Add pnpm-workspace.yaml; remove the `workspaces` field from
  package.json (pnpm uses the yaml exclusively)
- Delete yarn.lock and .yarnrc; commit pnpm-lock.yaml
- Set packageManager to pnpm@11.1.0 with sha512 integrity for corepack
- Add engines.pnpm so npm/yarn invocations get a clear engine warning
- Add pnpm to .mise.toml
- lerna.json: npmClient -> pnpm
- Root scripts (reinstall, start) and husky pre-commit hook switched
  from yarn to pnpm
- Add @types/node to root devDependencies so tsconfig "types": ["node"]
  resolves under pnpm's strict layout (was previously hoisted from
  workspaces in yarn classic)

Workspace packages: all internal cross-package deps switched to the
`workspace:^` protocol so pnpm links the local copy. External dep
versions are unchanged.

Install scripts:
- pnpm 11 denies install scripts by default; allowlist is in
  pnpm-workspace.yaml (allowBuilds). Three known builders are listed
  with explicit `false` decisions; security hardening commits may
  flip individual entries with a documented reason.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* ci: switch CI workflows, Dockerfiles, and docs to pnpm

CI workflows:
- All jobs add a pnpm/action-setup@v4 step before setup-node so the
  node cache uses the pnpm store. pnpm version is sourced from
  package.json `packageManager` field.
- yarn install --frozen-lockfile -> pnpm install --frozen-lockfile
- commitlint job invokes the CLI via `node @commitlint/cli/cli.js`
  because pnpm's strict layout does not symlink workspace binaries
  to the root node_modules/.bin/ (matches the .husky/commit-msg
  pattern).
- baseline-apt job installs Node from apt and pnpm via corepack
  (corepack ships with Node >=16.10).

Dockerfiles:
- Dockerfile.ci and Dockerfile.dev enable corepack and call pnpm.
- npm pack of the published @commitlint/* packages is unchanged
  (it fetches from the registry, independent of the local lockfile).

Docs:
- README and CONTRIBUTING switched to pnpm commands.
- CONTRIBUTING adds an admonition for contributors with pre-pnpm
  local clones: a one-time node_modules cleanup and pointers to
  mise / corepack / direct pnpm install.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: declare @commitlint/utils where pkg-check is used

@commitlint/load and @commitlint/top-level have pkg/deps scripts that
invoke the pkg-check / dep-check binaries provided by
@packages/utils (published name: @commitlint/utils) but neither
package listed it as a devDependency. Resolves today only because
yarn classic hoists the binary to the root; under pnpm's strict
layout it is not visible from the workspace.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(utils): use pnpm pack in pkg-check

The hardcoded `yarn pack --filename` invocation in pkg-check fails
without yarn on PATH. Switch to `pnpm pack --pack-destination` and
parse the produced filename from stdout — pnpm prints the absolute
path of the tarball, so the basename is joined with the temp dir
we passed as the destination.

Note: pkg-check is invoked manually at release time, not in CI.
The PRELUDE-based import test has separate pre-existing issues
under pnpm's tarball contents; addressing those is out of scope
for this commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* ci: install pnpm via npm in baseline-apt job

Ubuntu's stock corepack (bundled with apt-installed Node) trips on
the dynamic ESM imports pnpm 11 uses at launch and throws
ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING. Skip corepack for this job
and install pnpm directly via npm, pulling the version from the
packageManager field so it stays in sync.

This keeps the job honest about what we're testing — "stock apt
plus one explicit pnpm install" — instead of pretending stock
Ubuntu corepack works with current pnpm.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: tighten pnpm supply-chain settings

Three additions to .npmrc, each defensive against a different
failure class:

- strict-dep-builds=true: turn the "unapproved install script"
  warning into a hard error. A new transitive dep that ships a
  postinstall now blocks install until someone explicitly decides
  whether to allow it in pnpm-workspace.yaml's allowBuilds.

- verify-deps-before-run=error: refuse to run pnpm scripts when
  node_modules diverges from the lockfile. Catches the "edited
  package.json, forgot to reinstall" class of drift.

- resolution-mode=time-based: when resolving a version range,
  prefer the version that was current as of the most-recent direct
  dep publish. Limits exposure to a compromised brand-new release
  of a transitive dep.

All three are config-only; the existing lockfile and node_modules
state already satisfy them, so install + build + test pass
unchanged (1188/1188).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(renovate): rename stale ignoreDeps entry

@commitlint/test-environment no longer exists — the package was
renamed to vitest-environment-commitlint. Renovate was silently
ignoring nothing; rename the entry so the internal-package safety
net actually covers it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(renovate): config:recommended + weekly lockfile maintenance

- Rename the deprecated config:base preset to config:recommended
  (renovate's recommended modern equivalent).
- Enable lockFileMaintenance with a weekly Monday-morning schedule.
  Pairs with resolution-mode=time-based: the periodic refresh becomes
  the explicit moment new transitive versions get adopted, instead of
  drifting in on each direct-dep range bump.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: address copilot review feedback

Four small nits, none of which change behaviour for the green path:

- README: the inline comment for `pnpm start` claimed "run tests,
  again on change" but the script maps to `tsc -b --watch`. Reword
  to match reality.
- .npmrc: drop legacy-peer-deps=true. It was added in 2022 for
  npm/yarn-era peer-dep handling; pnpm 11 auto-installs peers, the
  full suite passes without it, and corepack now blocks accidental
  `npm install` so the legacy flag has no remaining purpose.
- package.json: tighten engines.pnpm from >=10 to >=11. The repo
  uses pnpm-11-only features (allowBuilds in pnpm-workspace.yaml,
  verify-deps-before-run=error).
- .mise.toml: pin Node to 22.12.0 to match engines.node, instead of
  the looser "22" which mise resolves to the latest 22.x at install.

Lockfile reshuffles slightly under resolution-mode=time-based now
that legacy-peer-deps is gone — net reduction in graph size.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: enforce node engines via pnpm instead of pinning in mise

Replace the exact node = "22.12.0" pin (rejects auto-bumped patch
releases) with the broader node = "22" plus engine-strict=true in
.npmrc. mise serves the latest 22.x stable (currently well above
22.12), and pnpm now hard-fails install when engines.node /
engines.pnpm aren't met — turning those fields from soft advisories
into real floors.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(mise): relax pnpm pin to major-only

Same reasoning as the node = "22" relaxation: the exact in-project
pnpm version is already enforced by the packageManager field +
corepack + engines.pnpm + engine-strict. mise just needs "some
modern 11.x", not a specific patch. Removes the false-positive
"missing: pnpm@11.1.0" warning when contributors already have
another 11.x installed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* ci: bump pnpm/action-setup v4 -> v6

pnpm 11.1.0 requires Node >=22.13. action-setup@v4 runs on a bundled
Node 20, so its `npm install -g pnpm@11.1.0` step fails with
ERR_PNPM_UNSUPPORTED_ENGINE before it ever touches the matrix Node.
v6 ships Node 24 and resolves it; also clears the deprecation
warning about Node 20 actions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
E
escapedcat committed
1329a25fd6f13b993fdf4e8c1b5a25ff2bf7ee07
Parent: f8be069
Committed by GitHub <noreply@github.com> on 5/12/2026, 1:48:55 PM