当前位置: 首页 > 工具软件 > V6scripts > 使用案例 >

聊聊pnpm v6升v7拥抱的变化和经验谈

文国发
2023-12-01

前言

可以看到 pnpm v7 已经进 rc 了,下个月就要正式 release 了。

因为这次大版本升级带来的 Breaking change 还不少,这里先用 rc 版做一下升级,总结一些经验。

正文

我们此处以 7.0.0-rc.2 为例,CHANGELOG

rc 变化跟进

提前需要注意的是,rc 意味着不会再有大的变化了,但是 rc.1rc.2 还是有变化的(全局 store 缓存位置变了),所以实际到 v7 正式 release 时,很大概率还有新的变化,所以需要额外注意下。

Breaking change 点晴

changesolutionimportant
Node.js 12 is not supported没什么需要注意的,现在我们都用 nodejs 16 了,这个月也会出 nodejs 18 ,届时将全升 v18-
The root package is excluded by default, when running pnpm -r exec|run|add这个在以前,更多时候我们思维上就认为 monorepo 里 -r 就不会执行根目录,所以这次把他惯性思维化了,挺好-
Filtering by path is done by globs
In pnpm v6, in order to pick packages under a certain directory, the following filter was used: --filter=./apps
In pnpm v7, a glob should be used: --filter=./apps/**
很关键,这可能要带来不少 scripts 里对 --filter 参数的改动,特别是构建的 script ,但不需要花太多实践,变成 glob 即可
The NODE_PATH env variable is not set in the command shims (the files in node_modules/.bin). This env variable was really long and frequently caused errors on Windows
Also, the extend-node-path setting is removed.
很明显这个不是我们需要注意的重点-
The embed-readme setting is false by default不需要关心-
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 intepreted as pnpm arguments unless -- came before it.这个不要太 Breaking ,package.json#scripts 还有涉及到的手动脚本都需要改,本质上是少传一层 -- 参数,但影响面和改起来的心智负担真不小
Side effects cache is turned on by default. To turn it off, use side-effects-cache=false这个没什么关系,我们用全局 lock 的话缓不缓存无所谓-
The npm_config_argv env variable is not set for scripts我们不关心-
pnpx is now just an alias of pnpm dlx这个变化因为之前在文档里也很强调 pnpx 要被废弃了,所以还算有心理预期,包括 yarn 2 也是都在走 dlx 的风格-
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这也是个重点,因为我们理性预期 pnpm add -g 的包应该帮我自动注册并且覆盖到全局 bin 里,这样比较方便,但其实这是把双刃剑,所以这次让 bin 不自动注册了也能理解,也就是我们需要额外给他加到 bin (环境变量)里
pnpm pack should only pack a file as an executable if it’s a bin or listed in the publishConfig.executableFiles array几乎用不到-
-W is not an alias of --ignore-workspace-root-check anymore. Just use -w or --workspace-root instead, which will also allow to install dependencies in the root of the workspace需要注意。以前我们在 monorepo 都是 pnpm add -DW ,现在要用 pnpm add -Dw
Allow to execute a lifecycle script in a directory that doesn’t match the package’s name. Previously this was only allowed with the --unsafe-perm CLI option不重要-
Local dependencies referenced through the file: protocol are hard linked (not symlinked). If you need to symlink a dependency, use the link: protocol instead看起来是为了防止 file 链的包依赖造成的非预期行为而改变的,一般我们用不到,所以不关心他-
strict-peer-dependencies is true by default挺关键的,v6 是默认不在乎 peer deps ,一旦开启肯定会有很多安装和运行失败的情况,使用起来痛苦程度增加了
A prerelease version is always added as an exact version to package.json. If the next version of foo is 1.0.0-beta.1 then running pnpm add foo@next will add this to package.json不关键-
Dependencies of the root workspace project are not used to resolve peer dependencies of other workspace projectspeer deps 相关的,不关键,我们不关心-
Don’t hoist types by default to the root of node_modules需要注意。以前默认提升 @types/* 和 eslint 系到 root ,默认友好,这次把 @types/* 砍掉了其实也会造成一些隐性的问题
Any package with “prettier” in its name is hoisted是个好习惯-
Changed the location of the global store from ~/.pnpm-store to <pnpm home directory>/store是个好习惯,这下搭配上面提到的 PNPM_HOME 就有自己的作用和存储域了,变得清真了起来(这里开始有点理解为什么默认不注册 bin 了)-

经验谈

这里直接给出一些升级过程的经验谈,具体可根据自身需要取用。

pnpm init 不传参了

以前我们可以 pnpm init -y 去实现快速 init 一个包,现在因为 script 的 argv 被完全传递,需要用 pnpm init -- -y ,所以我们的解法是以后不使用 pnpm init 了,直接回归到 npm init -y 即可,或者手动创建一个 package.json

monorepo 不能在 submodule (workspace) 里 init 了

monorepo 场景下,比如创建一个 packages/app 项目,此时 package/app 里面是空的,需要先把 package.json 创建出来,所以我们需要用 pnpm init -y ,现在无论如何都不能被 pnpm init 出来:

ERR_PNPM_PACKAGE_JSON_EXISTS  package.json already exists

pnpm init -- -y 也无法生效,必须使用 npm init -y ,以后忘掉 pnpm init 即可。

setup 注册全局 bin

正常我们升级或者安装一个全局包是 pnpm add -g pnpm 的形式,但是因为现在不自动注册 bin 了,所以会被警告。

解法是跑一下 pnpm setup 即可,pnpm 会自动把环境变量和目录追加到 ~/.zshrc 的尾部。

# .zshrc

# ... 尾部

export PNPM_HOME="/Users/username/Library/pnpm"
export PATH="$PNPM_HOME:$PATH"

同时以前的全局 store 缓存在 ~/.pnpm-store 等多个位置,现在会放到 $PNPM_HOME/store 里,直接跳 cd $PNPM_HOME 就可以看到。

这里也帮我们把全局 bin 加到 PATH 里了,挺好。

pnpm filter 变化

这个不多说了,记得改成 glob 语法,现在我们可能用 turborepo ,所以没有 pnpm --filter 使用的余地了。

pnpm engine 限制

package.json 里限制一下 pnpm 版本,防止被 v6 的用户跑:

// package.json
{
  "engines": {
    "node": ">=14",
    "pnpm": ">=7.0.0"
  },
  "packageManager": "pnpm@7.0.0"
}
pnpm script 传参改变

因为现在是直接想下传递 pnpm run <script> 后面所有的 argv ,所以就少传一层 -- 即可,举个例子:

// package.json
{
  "scripts": {
    "build": "tsc",
    //  以前,我们要多一层 `--` 去传 argv
    "dev": "pnpm build -- --watch",
    // 殺 现在,去掉 `--` 直接向下传即可
    "dev": "pnpm build --watch"
  }
}
types 不再提升了

最多的影响就是子包会报 type 引用找不到的问题(比如 tsc --noEmit 或者 fork-ts-checker-webpack-plugin 探测)。

解法是手动修 types 报错(装上 @types ,在子包或者顶层都可以),或者配一下 @types 的 hoist 。

peer deps 不需要严格

.npmrc 里把 strict-peer-dependencies 置为 false ,我们不需要严格的 peer deps 依赖链。

# .npmrc
strict-peer-dependencies=false

成熟的开发者需不需要 peer 应该心知肚明,因为有些场景就是刻意不安装全 peer deps 的(比如基础库,要留某个 dep 给用户安装,但是我又需要他作为 dev deps)。

总结

目前的经验谈并没有覆盖到所有的 Breaking 场景,但无论怎么变都离不开 CHANGELOG 写的十几条,在其中判断是哪一条有问题再对应去解即可。

 类似资料: