Skip to content

chore: migrate to pnpm#4762

Merged
escapedcat merged 12 commits into
masterfrom
chore/migrate-to-pnpm-v2
May 12, 2026
Merged

chore: migrate to pnpm#4762
escapedcat merged 12 commits into
masterfrom
chore/migrate-to-pnpm-v2

Conversation

@escapedcat

@escapedcat escapedcat commented May 12, 2026

Copy link
Copy Markdown
Member

Why

  • Yarn 1.x is EOL — no upstream security fixes.
  • pnpm 11 ships with install-script default-deny (biggest single supply-chain mitigation).
  • Stronger lockfile integrity and tool integrity (corepack + sha512 pin).

What

  • Switch package manager: yarn → pnpm 11 with workspace:^ for internal deps.
  • pnpm-workspace.yaml replaces the root workspaces field; yarn.lock / .yarnrc removed; pnpm-lock.yaml committed.
  • packageManager field pinned with sha512 hash; engines.pnpm added; .mise.toml adds pnpm; lerna.json npmClient: pnpm; husky hooks switched.
  • allowBuilds in pnpm-workspace.yaml explicitly set to false for @swc/core, esbuild, nx.
  • .npmrc: strict-dep-builds=true, verify-deps-before-run=error, resolution-mode=time-based. Hard-fails on unapproved postinstalls, prevents stale node_modules, and biases resolution away from brand-new transitive releases.
  • CI workflows + Dockerfiles use pnpm install --frozen-lockfile; commitlint job invokes the CLI via node
    @commitlint/cli/cli.js (no root .bin/ under pnpm strict).
  • Baseline-apt job installs pnpm via npm install -g because stock Ubuntu corepack throws
    ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING against pnpm 11.
  • Declared two phantom devDeps that pnpm strict surfaces (@commitlint/utils in load + top-level).
  • pkg-check: yarn pack → pnpm pack.
  • CONTRIBUTING gets an admonition for contributors with pre-pnpm clones (one-time cleanup + pnpm install paths).

Scope

  • Contributors only. Published packages and end users are unaffected.

escapedcat and others added 4 commits May 12, 2026 14:12
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 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>
@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>
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>
@qodo-code-review

Copy link
Copy Markdown
ⓘ You've reached your Qodo monthly free-tier limit. Reviews pause until next month — upgrade your plan to continue now, or link your paid account if you already have one.

escapedcat and others added 4 commits May 12, 2026 14:32
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>
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>
@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>
- 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>

This comment was marked as resolved.

escapedcat and others added 4 commits May 12, 2026 15:23
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>
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>
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>
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>
@escapedcat escapedcat merged commit 1329a25 into master May 12, 2026
16 checks passed
@escapedcat escapedcat deleted the chore/migrate-to-pnpm-v2 branch May 12, 2026 13:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants