Pnpm basics - how to transition quickly
What is pnpm ?
A package manager that aims to be:
- fast
- efficient on node_modules size
- strict
Let's break this down:
Due to efficient centralized store management, pnpm
is expected to be up to 2x faster than npm
.
- pnpm writes one unique file only ever once on your disk
- pnpm uses a different (very complex) algorithm to fetch and resolve the dependencies of your project
To understand better the motivation behind the pnpm, please refer to the official docs
Why pnpm is faster ?
The traditional way of installing dependencies with npm
contains 3 stages:
- resolving
- fetching
- linking
As a result, everything lands in the node_modules
of your project.
Npm proceeds with each stage at the time, so fetching
has to wait for the resolving
step and so on.
Pnpm on the other hand runs the installation stages separately for each dependency.
Once a package is resolved, it immediately starts to fetch it.
For more benchmarks, you can check the official pnpm benchmarks
Smaller node_modules
Packages files are stored in a central content-addressable store.
Any project can access this store. Only files, that are different between package versions will be added to the store.
Let’s consider the following versions of React:
- React 17
- React 18.2.0
- React 18.3.0
If hypothetically, the file jsx-dev-runtime.js
will have the same content in all of these 3 versions, pnpm will store the file once.
This approach greatly reduces the size of the node_modules
, especially if you have multiple projects on your machine.
More strict than npm
Pnpm is stricter than npm.
When you install @testing-library/jest-dom
with npm it will hoist all of its dependencies to the root of the node_modules
.
Package @testing-library/jest-dom
is shipped with a dependency lodash
which, even if you don't use it in your project, can be still imported and used e.g
// it will work since lodash is a dependency of the `@testing-library/jest-dom`
import lodash from 'lodash'
Although it is a rare case scenario, I have seen it in production apps, it is bad because:
- you have 0 control over the version of the
lodash
that@testing-library/jest-dom
lodash
is not your dependency listed in thepackage.json
- in case
@testing-library/jest-dom
removeslodash
in the next version it will break your app
Lodash is hoisted because npm
follows the flat module structure by default.
Let's take a look at how pnpm
solves this problem:
- pnpm add only direct dependencies of your app to the
node_modules
- your node_modules will only contain
@testing-library/jest-dom
in this example - your app will break if you attempt to import
lodash
like in the npm example @testing-library/jest-dom
will still havelodash
to function properly. It will be stored in thenode_modules/.pnpm
directory
How to migrate
- install pnpm with the corepack
- rm -rf node_modules
- rm
package-lock.json
- pnpm will create its own lock-filepnpm-lock.yaml
- pnpm install
- update ci workflow workflow
I use Github and Gitlab configs from the docs and had no issues so far.
It's up to you whether to cache the store on CI.
An official list of the pnpm continuous integration
Typing pnpm
in your terminal can be too verbose, you can always create an
alias in shell config .zshrc
alias pn=pnpm
Metrics
During my migration to pnpm, I was using the 9.1.0
version of the pnpm.
All of my checks were done with time npm/pnpm i
on medium-sized Qwik City blog with a ton of mdx, and a fairly standard amount of dependencies.
Results showing average from the 5 runs of each command.
Without node_modules
:
- total time npm:
3.374s
- total time pnpm:
1.701s
With node_modules
:
- total time npm:
0.826s
- total time pnpm:
0.591s
GitHub actions:
- total install time npm:
16s
- total install time pnpm:
13s
Additionallypnpm
has to be installed, in my case it takes 1-2 seconds on GitHub action
Size of the node_modules
note that it's the only Qwik project on my computer:
- with npm:
260MB node_modules/
- with pnpm:
240MB node_modules/
Monorepo
This article won't cover how pnpm
handles the monorepo setups.
I will cover it in an upcoming article, once I get more insights into it in my daily job.