How to migrate from yarn / npm to pnpm
Tags: javascript, pnpm, webdev, performance
Published: 2022-05-29 20:17:30

## Motivation
`pnpm` is more performant at fetching, resolving, and storing dependencies. My personal experience shows that in some projects `pnpm` can be approx. 10x time faster at resolving dependencies and up to 3x more efficient for disk usage.

It is also easy to start using `pnpm` if you have used `npm` or `yarn` before because the CLI is very similar.

[https://pnpm.io/motivation](https://pnpm.io/motivation)

## Migration guide

**Step 1**: Install `pnpm` [Installation](https://pnpm.io/installation)
**Step 2**: Delete `node_modules` 

```
npx npkill
```

**Step 3**: Add to `package.json`
```json
"scripts": {
  "preinstall": "npx only-allow pnpm", 
  ...
}
```
This will prevent other devs from accidentally installing dependencies with anything else than `pnpm` 

**Step 4**: Create `pnpm-workspace.yaml`
```yaml
packages:
  # include packages in subfolders (e.g. apps/ and packages/)
  - "apps/**"
  - 'packages/**'
  # if required, exclude some directories
  - '!**/test/**'
```
**Step 4 (a)**: remove `"workspaces"` from `"package.json"`, since it's no longer needed.

**Step 5**: Run 
```
pnpm import
```
This command will create a `pnpm-lock.yaml` file based on `yarn.lock` (or `packages-lock.json`)
**Step 6**: Remove `yarn.lock` (or `packages-lock.json`)
**Step 7**: Install dependencies
```
pnpm i
```
**Step 8**: Replace `npm run` (or `yarn`) to `pnpm` in all `package.json` and other files (E.g. `pnpm test` instead of `npm run test`)

**Important! You need to keep in mind that `pnpm` doesn’t use dependency hoisting:**

> When installing dependencies with npm or Yarn Classic, all packages are hoisted to the root of the modules directory. As a result, source code has access to dependencies that are not added as dependencies to the project.
> By default, pnpm uses symlinks to add only the direct dependencies of the project into the root of the modules directory. 
[pnpm](https://pnpm.io/motivation#creating-a-non-flat-node_modules-directory)

In practice it means that if you have a package `A` that imports a package `B`  (`import something from 'B'`) but doesn’t explicitly specify `B` in the `dependencies` or `devDependencies`, then the execution will fail.   

## Cheatsheet
 

| Tables        | Commands      | Cool  |
| ------------- |:-------------:| -----:|
| Install dependencies  | `pnpm i` | [https://pnpm.io/cli/install](https://pnpm.io/cli/install) |
| Add a dependency      | `pnpm add <package>`      |   [https://pnpm.io/cli/add](https://pnpm.io/cli/add)|
| Shows all packages that depend on the specified package | `pnpm why <package>`    |    [https://pnpm.io/cli/why](https://pnpm.io/cli/why) |
| Run a command as if it was executed form the root of the project rather than a workspace package | `pnpm -w <command>`      |    [https://pnpm.io/pnpm-cli#-w---workspace-root](https://pnpm.io/pnpm-cli#-w---workspace-root) |
| Restrict commands to specific subsets of packages | `pnpm --filter <package_selector> <command>`      |    [https://pnpm.io/filtering](https://pnpm.io/filtering) |
| This runs an arbitrary command from each package's "scripts" object | `pnpm -r <command>`      |    [https://pnpm.io/cli/run#--recursive--r](https://pnpm.io/cli/run#--recursive--r) |