Today I migrated 6 repos from yarn v1 to pnpm in about 4 hours and I decided to put the process into this article to share some issues I encountered.
Basic info
The repos are:
- https://github.com/yue4u/blog this blog
- https://github.com/yue4u/dhu a library/cli/website monorepo
- https://github.com/yue4u/lab a monorepo website deployed on
netlify - https://github.com/yue4u/holoframe another website deployed on
netlify - https://github.com/yue4u/leetcode a repo only using jest for testing
- https://github.com/yue4u/rss a simple library
The script
Since I think I'll be migrating more repos in the future so I put some basic logics to an zx script.
js#!/usr/bin/env zx// https://github.com/google/zx/issues/124#issuecomment-898469918process.env.FORCE_COLOR = 3;await $`find . -type d -name node_modules -prune -ok rm -rv {} \\;`;await $`find . -type f -name yarn.lock -prune -ok rm -rv {} \\;`;await $`find . -type f -name "yarn*log" -prune -ok rm -rv {} \\;`;const w = (m) => console.log(chalk.yellow(m));const pkg = await fs.readJson("./package.json");if (pkg.scripts?.preinstall) {w(`package.json already has 'scripts.preinstall', skipping add 'only-allow pnpm'`);} else {pkg.scripts ??= {};pkg.scripts.preinstall = "npx -y only-allow pnpm";await fs.writeJson("./package.json", pkg, { spaces: 2 });}const refs =await $`grep --exclude-dir={node_modules,.git,dist} -rl "yarn" . | tee /dev/tty || true`;if (refs.stdout) {w("Found reference to yarn, better check them out.");}await $`pnpm i`;
Things this script does:
- delete node_modules and yarn related files
- adding
"preinstall": "npx only-allow pnpm"to package.json https://pnpm.io/only-allow-pnpm - warn about potential usage of
yarnin build scripts / ci commands - run the
pnpm icommand
Issues
gastby does not build
Fix: use the gatsby-plugin-pnpm package. change commit
Since this blog is deployed directly to GCP bucket by myself without CI, the migration is done as soon as the local build succeeded.
mono repo & lerna
see https://pnpm.io/workspaces
changes made:
1. add pnpm-workspace.yaml file
Could also delete workspaces in package.json.
2. change dependencies reference to use workspace: protocol
diff- "@dhu/core": "^0.5.0",+ "@dhu/core": "workspace:*",
3. change some yarn workspace command usage
e.g. yarn workspace @dhu/cli dev -> pnpm dev --filter @dhu/cli
4. using @changesets/cli instead of lerna
Since lerna doesn't really support pnpm,
@changesets/cli seems to be a better choice. pnpm is using changesets itself and even has detailed official docs: Using Changesets with pnpm, which also includes the releasing flow and how to step up GitHub Actions. Awesome!
5. move patch-package to package dir
pnpm does not hoist deps to the root node_modules by default, we need move the "postinstall": "patch-package" to sub dirs.
using pnpm with frameworks and providers
It might be super(1) hacky(2) to use pnpm with frameworks like gridsome or nextjs. And I'm afraid it's not easy to deploy on hosting providers like vercel currently.
Netlify also does not support pnpm yet, however, by adding the config below to netlify.toml, it's possible to deploy pnpm managed repos on it. Related issue: Feature request: Add PNPM support #1633.
toml[build.environment]NPM_FLAGS = "--version"[build]command = "npx pnpm install --store=node_modules/.pnpm-store && npx pnpm build"
There is another issue though... If you have "preinstall": "npx only-allow pnpm" in package.json, it will fail the build process. So for netlify sites it's better to skip the preinstall script when $CI env variable is presented or just remove it for now.
Conclusions
As long as using pnpm locally or in managed environment, the migration process should be easy and straightforward.
In general, I think I'll start new projects using pnpm by default from now on. At the same time, I still feels pnpm does not work out-of-the-box when it comes to major frameworks and hosting providers at this moment.