Work with PNPM
Rhapsody uses pnpm as its package manager.
Why PNPM?
As Rhapsody continues to evolve, we are moving toward a world where the codebase is organized more like a monorepo, with individual packages and apps. In an ideal state, each package and app would also manage its own dependencies. To accomplish this, we need to make use of a common monorepo construct called "workspaces" that allows us to define all the packages within a repo for the package manager to handle for us. npm@7 shipped with workspaces support but sadly its workspaces functionality still lags far behind the more established workspaces-aware package managers such as pnpm and yarn. Consequently, we looked beyond npm for a solution. We evaluated yarn, but found that the churn between its v1, v2, and v3 to be unhelpful. yarn is also leaning heavily into its PnP feature, which has its own compatibility issues and wasn't a solution we needed. pnpm has been in use for years at Salesloft in the Design System and Cross Platform Components repo (via rush) so we know it's reliable and effective. It's used heavily by other large organizations as well such as Microsoft, Prisma, and Wix as well as by large open-source projects such as Vue, Cycle.js, and Verdaccio. Further, it's monorepo management is top-notch and provides the building blocks we need to move forward with our architecture goals.
Setup
Run the following commands in the Rhapsody repo:
# Install pnpm globally
npm install -g pnpm
# Add configuration to your global .npmrc file
echo 'store-dir=~/src/.pnpm-store' >> ~/.npmrc
# Remove all old node_modules directories
pnpm -r exec rm -rf node_modules
# Install all dependencies via pnpm
pnpm install
Common CLI Commands
pnpm's CLI commands differ somewhat from npm's. Below are some common used commands:
pnpm install: install all dependenciespnpm add <pkg>: add a new dependency or update an existing dependencypnpm run <command>: run a given<command>listed in thepackage.jsonscriptspnpm why <pkg>: see what dependencies use<pkg>and what versions are used
What about npx?
PNPM has two commands that replace npx: pnpm dlx and pnpm exec. To understand the difference, let's talk about npx for a moment. With npm@6, invoking npx with a package name would first look at the current project to see if that dependency was installed. If the dependency was already installed, it would invoke the binary for that dependency. If the dependency wasn't installed, it would download the dependency from the registry and invoke the binary. Starting with npm@7, this behavior changed slightly. npx no longer downloads dependencies from the registry by default. As a safety measure, npx defaults to only running locally installed dependencies and prompts the user if a download is required. To automatically download a dependency, one needs to pass the --yes flag. PNPM splits these two use cases into two commands:
pnpm exec <pkg>: run the binary for a dependency that's already installedpnpm dlx <pkg>: download a dependency then run the binary
Pro Tips
- In whatever shell you use, create an alias for
p=pnpmto shorten typing outpnpm(or accidentally typingnpmfrom motor memory). - You can omit "run" when running most commands with
pnpm, similar to howyarnworks. It will automatically run scripts when they don't collide with built-in CLI commands. For example,pnpm run qa 1becomespnpm qa 1(or with the above tipp qa 1). - To delete all depencencies and install from scratch, run
pnpm -w -r exec rm -rf node_modulesthenpnpm install. This will deletenode_modulesdirectories for every workspace package, including the root, before installing dependencies. pnpmprovides advanced filtering for running commands against a subset of the entire repo. This will become more valuable as the number of workspace packages in Rhapsody grows.