|
| 1 | +# Keep pnpm lockfile as the only package lock |
| 2 | + |
| 3 | +## What was implemented |
| 4 | + |
| 5 | +- `package.json` — added `"packageManager": "pnpm@9.15.4"` (enables |
| 6 | + Corepack to enforce the exact pnpm version) and an `engines` block that |
| 7 | + fails npm/yarn with an explicit `please-use-pnpm` message if someone tries |
| 8 | + to install with them directly. Added a `preinstall` script that runs |
| 9 | + `scripts/verify-pnpm.js`. |
| 10 | +- `scripts/verify-pnpm.js` — inspects `npm_config_user_agent` (set by every |
| 11 | + package manager) at install time and hard-fails with instructions if the |
| 12 | + installer isn't pnpm. This is the first line of defense - it fires before |
| 13 | + any dependency resolution happens, so a `npm install` never gets far |
| 14 | + enough to generate a `package-lock.json`. |
| 15 | +- `.npmrc` — sets `package-manager-strict=true` (Corepack enforces the |
| 16 | + `packageManager` field) and `engine-strict=true`. |
| 17 | +- `.gitignore` — explicitly ignores `/yarn.lock` and `/npm-shrinkwrap.json` |
| 18 | + in addition to the pre-existing `/package-lock.json` rule, so an |
| 19 | + accidental lockfile from another package manager can never be committed. |
| 20 | +- `.github/workflows/ci.yml` — added a "Verify pnpm lockfile is the only |
| 21 | + lockfile" step that fails the build if `package-lock.json`, `yarn.lock`, |
| 22 | + or `npm-shrinkwrap.json` exist in the repo, before `pnpm install |
| 23 | + --frozen-lockfile` runs. |
| 24 | +- `src/lib/__tests__/pnpmLockfile.test.ts` — Vitest coverage asserting |
| 25 | + `pnpm-lock.yaml` exists, that no competing lockfiles exist, and that |
| 26 | + `package.json` declares a `pnpm@` `packageManager`. |
| 27 | + |
| 28 | +## Why |
| 29 | + |
| 30 | +Multiple lockfiles (e.g. a stray `package-lock.json` committed by someone |
| 31 | +running plain `npm install`) cause dependency resolution to silently drift |
| 32 | +between contributors/CI and can reintroduce vulnerable or duplicate |
| 33 | +transitive versions that `pnpm-lock.yaml` had already deduped/pinned. This |
| 34 | +change makes pnpm the only supported installer at three layers: local |
| 35 | +install-time (`preinstall` script + Corepack), source control |
| 36 | +(`.gitignore`), and CI (explicit lockfile check + `--frozen-lockfile`). |
| 37 | + |
| 38 | +## Manual verification checklist |
| 39 | + |
| 40 | +- [ ] Run `npm install` locally - it should fail immediately with the |
| 41 | + "This repository only supports pnpm" message. |
| 42 | +- [ ] Run `pnpm install` - it should proceed normally. |
| 43 | +- [ ] Confirm CI's new "Verify pnpm lockfile is the only lockfile" step |
| 44 | + passes on a clean checkout. |
| 45 | +- [ ] `pnpm test -- pnpmLockfile` passes locally. |
0 commit comments