Subchapter 6.2
references/best-practices-migration.mdMarkdown7 KBView on GitHub
Guide for migrating existing projects from npm or Yarn to pnpm, plus upgrading pnpm v10 → v11.
v11 changes how configuration is read. Most of it is mechanical — run the codemod:
cd /path/to/project
pnpx codemod run pnpm-v10-to-v11The codemod automatically:
package.json#pnpm settings into pnpm-workspace.yaml (the pnpm field is no longer read)..npmrc: only auth/registry settings stay in .npmrc; every other key moves to pnpm-workspace.yaml as camelCase (e.g. node-linker → nodeLinker). Per-subproject .npmrc files become packageConfigs["<name>"].onlyBuiltDependencies, neverBuiltDependencies, ignoredBuiltDependencies, onlyBuiltDependenciesFile) into one allowBuilds: { name: true|false } map.managePackageManagerVersions/packageManagerStrict/packageManagerStrictVersion with pmOnFail: download|ignore|warn|error.allowNonAppliedPatches → allowUnusedPatches, auditConfig.ignoreCves → auditConfig.ignoreGhsas.useNodeVersion → devEngines.runtime, and bumps packageManager.Manual follow-ups (not automatable):
CVE-… IDs to GHSA-… in auditConfig.ignoreGhsas.ignorePatchFailures removed — failed patches now always throw.npm_config_* env vars → pnpm_config_* (CI, shell profiles, Docker).pnpm link <name> → use a path (pnpm link ./foo); pnpm link --global → pnpm add -g ..pnpm install -g (no args) and pnpm server removed.package.json script named clean/setup/deploy/rebuild now shadows the built-in — use pnpm pm <name> for the built-in.# Remove npm lockfile and node_modules
rm -rf node_modules package-lock.json
# Install with pnpm
pnpm install# Remove yarn lockfile and node_modules
rm -rf node_modules yarn.lock
# Install with pnpm
pnpm installpnpm can import existing lockfiles:
# Import from npm or yarn lockfile
pnpm import
# This creates pnpm-lock.yaml from:
# - package-lock.json (npm)
# - yarn.lock (yarn)
# - npm-shrinkwrap.json (npm)pnpm is strict about dependencies. If code imports a package not in package.json, it will fail.
Problem:
// Works with npm (hoisted), fails with pnpm
import lodash from 'lodash' // Not in dependencies, installed by another packageSolution: Add missing dependencies explicitly:
pnpm add lodashpnpm reports peer dependency issues by default.
Option 1: Let pnpm auto-install (default in v8+):
autoInstallPeers: trueOption 2: Install manually:
pnpm add react react-domOption 3: Suppress warnings if acceptable:
peerDependencyRules:
ignoreMissing:
- reactSome tools don’t work with symlinks. Use hoisted mode:
nodeLinker: hoistedOr hoist specific packages:
publicHoistPattern:
- '*eslint*'
- '*babel*'If native modules fail, try:
# Rebuild all native modules
pnpm rebuild
# Or reinstall
rm -rf node_modules
pnpm installCreate pnpm-workspace.yaml:
packages:
- 'packages/*'Update internal dependencies to use workspace protocol:
{
"dependencies": {
"@myorg/utils": "workspace:^"
}
}Install:
rm -rf node_modules packages/*/node_modules package-lock.json
pnpm installRemove Yarn-specific files:
rm yarn.lock .yarnrc.yml
rm -rf .yarnCreate pnpm-workspace.yaml matching workspaces in package.json:
packages:
- 'packages/*'Update package.json - remove Yarn workspace config if not needed:
{
// Remove "workspaces" field (optional, pnpm uses pnpm-workspace.yaml)
}Convert workspace references:
// From Yarn
"@myorg/utils": "*"
// To pnpm
"@myorg/utils": "workspace:*"pnpm can replace Lerna for most use cases:
# Lerna: run script in all packages
lerna run build
# pnpm equivalent
pnpm -r run build
# Lerna: run in specific package
lerna run build --scope=@myorg/app
# pnpm equivalent
pnpm --filter @myorg/app run build
# Lerna: publish
lerna publish
# pnpm: use changesets instead
pnpm add -Dw @changesets/cli
pnpm changeset
pnpm changeset version
pnpm publish -rKeep only auth/registry in .npmrc; put everything else in pnpm-workspace.yaml (camelCase).
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
//npm.myorg.com/:_authToken=${MYORG_TOKEN}registries:
default: https://registry.npmjs.org/
'@myorg': https://npm.myorg.com/
autoInstallPeers: true
strictPeerDependencies: falseMost scripts work unchanged. Update pnpm-specific patterns:
{
"scripts": {
// npm: recursive scripts
"build:all": "npm run build --workspaces",
// pnpm: use -r flag
"build:all": "pnpm -r run build",
// npm: run in specific workspace
"dev:app": "npm run dev -w packages/app",
// pnpm: use --filter
"dev:app": "pnpm --filter @myorg/app run dev"
}
}Update CI configuration:
# Before (npm)
- run: npm ci
# After (pnpm)
- uses: pnpm/action-setup@v4
- run: pnpm install --frozen-lockfile # or: pnpm ciAdd to package.json for Corepack:
{
"packageManager": "pnpm@10.0.0"
}For large projects, migrate gradually:
pnpm import to create lockfileIf migration causes issues:
# Remove pnpm files
rm -rf node_modules pnpm-lock.yaml pnpm-workspace.yaml
# Restore npm
npm install
# Or restore Yarn
yarn installKeep old lockfile in git history for easy rollback.