可以看到 pnpm v7 已经进 rc
了,下个月就要正式 release 了。
因为这次大版本升级带来的 Breaking change 还不少,这里先用 rc
版做一下升级,总结一些经验。
我们此处以 7.0.0-rc.2
为例,CHANGELOG 。
提前需要注意的是,rc
意味着不会再有大的变化了,但是 rc.1
到 rc.2
还是有变化的(全局 store
缓存位置变了),所以实际到 v7 正式 release 时,很大概率还有新的变化,所以需要额外注意下。
change | solution | important |
---|---|---|
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 projects | peer 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 -y
去实现快速 init 一个包,现在因为 script
的 argv 被完全传递,需要用 pnpm init -- -y
,所以我们的解法是以后不使用 pnpm init
了,直接回归到 npm init -y
即可,或者手动创建一个 package.json
。
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
即可。
正常我们升级或者安装一个全局包是 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
里了,挺好。
这个不多说了,记得改成 glob 语法,现在我们可能用 turborepo ,所以没有 pnpm --filter
使用的余地了。
package.json
里限制一下 pnpm 版本,防止被 v6 的用户跑:
// package.json
{
"engines": {
"node": ">=14",
"pnpm": ">=7.0.0"
},
"packageManager": "pnpm@7.0.0"
}
因为现在是直接想下传递 pnpm run <script>
后面所有的 argv ,所以就少传一层 --
即可,举个例子:
// package.json
{
"scripts": {
"build": "tsc",
// 以前,我们要多一层 `--` 去传 argv
"dev": "pnpm build -- --watch",
// 殺 现在,去掉 `--` 直接向下传即可
"dev": "pnpm build --watch"
}
}
最多的影响就是子包会报 type 引用找不到的问题(比如 tsc --noEmit
或者 fork-ts-checker-webpack-plugin 探测)。
解法是手动修 types 报错(装上 @types
,在子包或者顶层都可以),或者配一下 @types
的 hoist 。
在 .npmrc
里把 strict-peer-dependencies
置为 false
,我们不需要严格的 peer deps 依赖链。
# .npmrc
strict-peer-dependencies=false
成熟的开发者需不需要 peer 应该心知肚明,因为有些场景就是刻意不安装全 peer deps 的(比如基础库,要留某个 dep 给用户安装,但是我又需要他作为 dev deps)。
目前的经验谈并没有覆盖到所有的 Breaking 场景,但无论怎么变都离不开 CHANGELOG 写的十几条,在其中判断是哪一条有问题再对应去解即可。