Migrating from pnpm 6 to 7

Troubleshooting a major version migration

This article was published on May 05, 2022, and takes approximately 5 minutes to read.

Pnpm 7 was released, and as with any project that follows semver, some features and breaking changes were expected.

I'm writing this post because the migration wasn't entirely trivial, but I could figure out everything and make my setup work fine with the new version.

In that sense, I won't cover every change but the ones that gave me some headache.

Changes

All changes I'm going to list (with how I fix them) are possible to check in their release notes from v7. I strongly recommend you take a look there.

Filtering and install

The root package is excluded by default when running pnpm -r exec|run|add

In v6, when we run a command, the root package json was also considered.

In my pipelines (using v6 still), I used to install only dependencies for the project I was building, for example:

Copied
pnpm install --filter website...

This always downloaded ONLY the dependencies declared in the root package json AND the ones from my website (and dependent packages) only. All the other dependencies were simply ignored.

That's awesome because it makes the installation lightweight.

Despite not being mentioned there, I notice that in v7, when I filter the installation, the root deps are not installed.

In my case, this is a problem because I use root dependencies (like scripty) in every single project inside my monorepo.

Long story short, I had to add another filter in my command to now, also consider root:

Copied
pnpm install --filter website... --filter .
--filter . says: "includes root"

Running a script

When using pnpm run <script>, all command line arguments after the script name are now passed to the script's argv, even --. For example, pnpm run echo --hello -- world will now pass --hello -- world to the echo script's argv. Previously flagged arguments (e.g. --silent) were interpreted as pnpm arguments unless -- came before it.

This was the most impactful change for my workflow.

In all my CI scripts I had the a lot of the following command:

Copied
pnpm run build -F website...

While this works fine in v6, in v7, I start getting the following error message:

Copied
ERR_PNPM_NO_SCRIPT Missing script: build

As the change message said, now, every parameter after the command will be forward to that command specifically. That means we have to pass all pnpm flags before the command:

Copied
-pnpm run build -F website...
+pnpm -F website... run build

peerDependencies error

In v6, when we don't have installed the peer dependencies of dependencies, we get the following warning:

Copied
WARN Issues with peer dependencies found

It's indeed something we want to avoid.

Personally, I never had problems with this approach (though I'm not sure if it's the best approach or if doing that could lead to weird problems). I use that strategy to force forced every project to use the same version of a specific dependency.

Though, after updating to v7, this message becomes an error:

Copied
ERR_PNPM_PEER_DEP_ISSUES Unmet peer dependencies

And because of now it's an error, Renovate is no longer able to automatically update my dependencies.

To avoid this problem, I needed a list of peer dependencies that are allowed to be missing:

Copied
package.json
{
  //... other configs...
  "pnpm": {
    "peerDependencyRules": {
      "ignoreMissing": [
        "@babel/core",
        "@popperjs/core",
        "@sanity/color",
        "autoprefixer",
        "babel-loader",
        "caniuse-lite",
        "eslint",
        "postcss",
        "prettier",
        "react",
        "react-dom",
        "react-is",
        "redux",
        "typescript",
        "webpack"
      ]
    }
}

pnpx

pnpx is now just an alias of pnpm dlx.

This didn't break anything for me, but before, I had the exact written a similar alias in my .zshrc:

Copied
alias pnpmx="pnpm dlx"

Now I can remove it and rely on the alias given from pnpm CLI.

PNPM_HOME

pnpm install -g pkg will add the global command only to a predefined location. pnpm will not try to add a bin to the global Node.js or npm folder. To set the global bin directory, either set the PNPM_HOME env variable or the global-bin-dir setting.

In v7, if we try to add a global package, you have to set up first our PNPM_HOME otherwise we get the following error message:

Copied
ERROR Unable to find the global bin directory
Run "pnpm setup" to create it automatically, or set the global-bin-dir setting or the PNPM_HOME env variable. The global bin directory should be in the PATH.

As the error message says, we can simply run pnpm setup, and it'll automatically create that env var in our bash/zsh config.

lock file

Lockfile version bumped to v5.4.

I don't think we have huge problems having a v5.3 lock file while using pnpm@7. However, the order way around (lock@5.4 but using pnpm@6) will give you the following message:

Copied
WARN Your pnpm-lock.yaml was generated by a newer version of pnpm.
It is a compatible version, but it might get downgraded to version 5.3

If you're working as a team, it might be a good idea to make sure everyone updates their pnpm to the latest version.

To keep things up-to-date, let's regen the lock file:

Copied
rm pnpm-lock.yaml && pnpm i

I'm always afraid to regenerate lock files due to yarn and npm problems in the past, but luckily I've never ever had issues with pnpm.

Miscellaneous

Vercel

I use Vercel to deploy my projects. They provide pnpm as a global package... but v6.

So to solve that, I had to hack around my "installation" command:

Copied
pnpm add -g pnpm@7 && pnpm i --frozen-lock --filter website...

In a nutshell, I first update pnpm and then run the installation. The deployment will fail if I don't do that due to the v5.3 lock file warning message.

pnpm/action-setup

While using Github actions, I use pnpm/action-setup to avoid the setup boilerplate.

I only had to adapt it to add the --filter . and set it up to use the last version:

Copied
      - uses: pnpm/action-setup@v2.2.1
        with:
          version: latest
          run_install: |
            args: [--frozen-lockfile, --filter lambdas..., --filter, .]

Conclusion

This process took me some time to figure out, but I'm into Front-end infrastructure, so no bummer.

I hope these tips could save you some time while migrating from pnpm 6 to 7.

I strongly recommend you read the full release notes from this tag, but from every dependency you update.

I create this habit, and it saves me a bunch of time while updating depencies.

Resources