package.json— added"packageManager": "pnpm@9.15.4"(enables Corepack to enforce the exact pnpm version) and anenginesblock that fails npm/yarn with an explicitplease-use-pnpmmessage if someone tries to install with them directly. Added apreinstallscript that runsscripts/verify-pnpm.js.scripts/verify-pnpm.js— inspectsnpm_config_user_agent(set by every package manager) at install time and hard-fails with instructions if the installer isn't pnpm. This is the first line of defense - it fires before any dependency resolution happens, so anpm installnever gets far enough to generate apackage-lock.json..npmrc— setspackage-manager-strict=true(Corepack enforces thepackageManagerfield) andengine-strict=true..gitignore— explicitly ignores/yarn.lockand/npm-shrinkwrap.jsonin addition to the pre-existing/package-lock.jsonrule, so an accidental lockfile from another package manager can never be committed..github/workflows/ci.yml— added a "Verify pnpm lockfile is the only lockfile" step that fails the build ifpackage-lock.json,yarn.lock, ornpm-shrinkwrap.jsonexist in the repo, beforepnpm install --frozen-lockfileruns.src/lib/__tests__/pnpmLockfile.test.ts— Vitest coverage assertingpnpm-lock.yamlexists, that no competing lockfiles exist, and thatpackage.jsondeclares apnpm@packageManager.
Multiple lockfiles (e.g. a stray package-lock.json committed by someone
running plain npm install) cause dependency resolution to silently drift
between contributors/CI and can reintroduce vulnerable or duplicate
transitive versions that pnpm-lock.yaml had already deduped/pinned. This
change makes pnpm the only supported installer at three layers: local
install-time (preinstall script + Corepack), source control
(.gitignore), and CI (explicit lockfile check + --frozen-lockfile).
- Run
npm installlocally - it should fail immediately with the "This repository only supports pnpm" message. - Run
pnpm install- it should proceed normally. - Confirm CI's new "Verify pnpm lockfile is the only lockfile" step passes on a clean checkout.
-
pnpm test -- pnpmLockfilepasses locally.