Skip to content

Migrate from npm to pnpm #63

Description

@jasonmadigan

npm's flat node_modules lets code import any transitive dependency (phantom deps), and its default posture runs postinstall scripts from every package without restriction. pnpm fixes both structurally: strict node_modules isolation prevents phantom dependency access, and since v10/v11 it blocks lifecycle scripts by default, enforces minimum release age (1 day cooldown on new versions), and blocks exotic subdependencies (git/tarball refs in transitive deps).

What needs to change

Lock files:

  • Delete frontend/package-lock.json and frontend/yarn.lock
  • Generate frontend/pnpm-lock.yaml via pnpm import or pnpm install

Dockerfile (root):

  • Install pnpm in the build stage (corepack enable pnpm or npm i -g pnpm)
  • npm ci -> pnpm install --frozen-lockfile
  • npm run build -> pnpm run build
  • npm start -> pnpm start
  • Remove npm from apt-get install line in runtime stage

frontend/Dockerfile:

  • Same npm -> pnpm swaps as above

CI (.github/workflows/e2e.yml):

  • Add pnpm/action-setup@v4 step
  • npm ci -> pnpm install --frozen-lockfile
  • npx playwright -> pnpm exec playwright

Makefile:

  • npm run dev -> pnpm run dev
  • npx -> pnpm exec

Docs (README.md, CLAUDE.md):

  • npm install -> pnpm install

Claude command (.claude/commands/screenshots.md):

  • npm install --no-save playwright -> pnpm equivalent

pnpm config to set

In frontend/.npmrc (pnpm reads this):

strict-peer-dependencies=true
auto-install-peers=true

Consider also enabling in frontend/package.json or .npmrc:

minimum-release-age=1440
block-exotic-subdeps=true
strict-dep-builds=true

These are defaults in pnpm 11+ but being explicit is self-documenting.

Verification

  • pnpm install completes cleanly
  • pnpm run build produces working frontend
  • pnpm run dev starts dev server
  • pnpm exec vitest run passes all tests
  • pnpm exec playwright test passes e2e
  • Docker build succeeds
  • No phantom dependency imports (build would fail if any exist)

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions