Work / Charades / Wiki / Concepts
package-manager
Conceptverified 2026-10-30
package-manager
This project uses pnpm (the only lockfile is pnpm-lock.yaml, and every CI workflow
runs pnpm install --frozen-lockfile). Do not use npm or yarn, except for npm version,
which is the release trigger.
A stray yarn.lock or package-lock.json in the working tree is not harmless.
@expo/package-manager resolves in the order bun, yarn, npm, pnpm and returns the first
lockfile it finds, so one silently makes every expo install, expo prebuild and
expo run:* shell out to that manager and rewrite its own lockfile, a loop that survives
until the file is deleted. .gitignore covers them so one cannot be committed, but
ignoring a file does not stop Expo from reading it. Deleting it is the only thing that
stops the loop. A packageManager field in package.json would not help either: Expo's
resolver only looks at lockfiles, and pr-check.yml pins pnpm itself for the reason
documented there.
pnpm-workspace.yaml carries two settings that matter:
nodeLinker: hoisted: Expo requires it.@expo/cli, metro and react-native deep-import internals of their own dependencies, which pnpm's default isolated layout does not expose, and the dev server dies with "Cannot find module @expo/metro/metro/lib/formatBundlingError.js".minimumReleaseAge: 1440: a version published less than 24h ago will not install. This is the guard against a maintainer-account takeover shipping a credential stealer in a patch release.pnpm install --frozen-lockfilevalidates the committed lockfile against it too, so a lockfile written with the policy switched off fails in CI.