pnpm is my go-to package manager for javascript projects, if you’re already using pnpm, you most likely would also like to use it for monorepo: codebases containing multiple projects.
There’re a lot of benefits of putting everything in one single workspace, but it also increases complexity. Thankfully pnpm has great workspace support.
Let’s say that you have a monorepo, where you have three packages:
All of them have an npm script called build
which creates a production build for themselves.
The relation is both web
and mobile
depend on the components
package, if you want to build web
, you want the build
script of components
to be executed first.
This is how you do it in pnpm:
Here it uses the --filter
flag with a package name followed by three dots to tell pnpm to run the build script of web
‘s dependencies first, and then run web
‘s own build script.
If you change the command above to run dev
script (which builds the package and watch fiiles to rerun on changes):
Bad news, it will get stuck in the dev
script of components
, because it will never finish and pnpm awaits each command to finish before running the next!
Now you need the --parallel
flag to disregard the topological order of dependencies and simply run all the scripts in parallel:
But this creates another problem:
pnpm dev
fails!When components
package has a build step, for example one that bundles src/index.ts
to dist/index.js
, chances are the dev
script will fail because scripts are running in parallel and components/dist/index.js
might not exist at the point when web#dev
starts running.
The simple solution is to run build
script once before the dev
script:
Alternatively, I have a package called lets-run
which you can use to run a command only if a certain file exist (otherwise keep trying until it exists):
You can also just import the src
files from the components
package and configure web
to compile the files imported from packages in this monorepo, but the configuration would vary in different projects, so I’m not going to talk about this.
Now this is what pnpm doesn’t do, you need a build system that has cache support by design for this.
And I don’t have a package for this 😜, because tools like Turborepo or NX already does it beautifully. Personally I prefer Turborepo because it’s simpler and requires much less configuration. The build command above can be converted to using Turborepo like this:
You can continue using pnpm to run dev scripts since you don’t really need caching there.