◆

pnpm run 详解与正则批量运行脚本

2023-03-03 01:570

场景

当我们执行下面两个命令时,发现都可以执行,而且效果是一样的

pnpm run clean
pnpm clean

刚从 npm 换过来时会觉得奇怪,npm 里省略 run 是不行的(npm clean 会报未知命令),pnpm 却两种都能跑。这不是巧合,而是 pnpm 刻意设计的命令解析顺序。

run 命令

  • 当你使用一个未知命令时, pnpm 会查找一个具有指定名称的脚本, 所以 pnpm run clean 和 pnpm clean 相同.
  • 如果没有指定名称的脚本,那么 pnpm 将以 shell 脚本的形式执行该命令,所以你可以做类似 pnpm eslint 的事情

所以完整的解析链是三级:先看是不是 pnpm 内置命令(install、add、update 这些),不是的话去 package.json 的 scripts 里找同名脚本,还找不到就当成可执行文件在 node_modules/.bin 里找并执行——最后这一级等价于 npx。

这带来一个必须注意的冲突:如果你的 script 名字和 pnpm 内置命令重名,省略 run 会执行内置命令而不是你的脚本。最典型的是叫 test、start、add、link 的脚本。比如 script 里定义了 "add": "node scripts/add.js",执行 pnpm add 会去装包而不是跑你的脚本,必须写全 pnpm run add。养成写 run 的习惯能避免这类问题,尤其在 CI 脚本里。

第三级那个 shell 兜底很实用。pnpm eslint src 相当于 npx eslint src,直接用项目本地装的 eslint,不用在 scripts 里为每个工具都注册一条命令,也不用担心全局版本和项目版本不一致。

顺带说个和 npm 的区别:给脚本传参数时 npm 需要用 -- 隔开(npm run test -- --watch),pnpm 不需要,直接 pnpm test --watch 就会把 --watch 透传给脚本。写惯 npm 的话加上 -- 也不报错,pnpm 会兼容处理。

正则表达式

pnpm 支持用正则一次匹配多个脚本名批量执行,这是 npm 没有的能力。

假设你有下面脚本命令

{
  "scripts": {
    "test": "echo 执行",
    "test:new": "echo 执行new",
    "test:old": "echo 执行old"
  }
}

你可以通过正则表达式运行上面的所有命令

用 / 包裹正则,pnpm 会把所有名字匹配的脚本都执行一遍:

pnpm run /^test/

这条命令会依次跑 test、test:new、test:old 三个脚本。这个特性配合命名约定很好用——把同类脚本统一加前缀,比如 lint:eslint、lint:stylelint、lint:prettier,然后 pnpm run /^lint:/ 一次全跑,不用再单独维护一条 "lint": "npm-run-all lint:*" 这样的聚合脚本。

几点要注意。正则要写在 / 之间,pnpm run ^test 不带斜杠会被当成普通脚本名去查找然后报找不到。默认是串行执行,中间任何一个脚本失败会中断后面的;想并行加 --parallel,但并行时多个脚本的输出会混在一起,排查问题不方便,跑 lint 这类互不干扰的任务才适合。

另外这个语法只匹配脚本名,正则里的特殊字符要小心。/test/ 是子串匹配,会连带匹配到 pretest、mytest 之类的名字,想精确控制就加锚点 ^ 和 $。

批量执行还有个副作用容易忘:npm 生命周期钩子会照常触发。如果定义了 pretest,跑 test 时它会先执行,那么用 /^test/ 批量跑三个 test 脚本时,pretest 只在 test 前面跑一次,test:new 和 test:old 有各自的 pretest:new、pretest:old 才会跑。这块行为不直观,依赖钩子做准备工作的场景建议显式写进脚本里,别指望钩子。

💬留言板0

还没有留言,来发表第一条吧~

PAGE LOAD / LIVEpnpm run 详解与正则批量运行脚本加载中