pnpm 在国内网络下的三个坑:ECONNRESET、allowBuilds 与废弃的 pnpm 字段
今天搭这个博客时,pnpm create astro 卡了整整 12 分钟最后失败。排查过程踩了三个坑,每一个单看都很反直觉,记录下来。
坑一:单连接测速快,不代表并发下载也快
一开始我用 curl 测速,淘宝镜像源跑出 253 KB/s,比官方源的 108 KB/s 快一倍多,于是果断切了过去。
结果 pnpm 一跑就满屏报错:
GET https://registry.npmmirror.com/... error (ECONNRESET)
GET https://registry.npmmirror.com/... error (ETIMEDOUT)
[WARN] Tarball download average speed 14 KiB/s ... is below 50 KiB/s
问题在于:curl 测的是单个连接下 1 个文件的下载速度,而 pnpm 会同时发起几十个并发请求。 这两件事对网络的要求完全不同。
ECONNRESET 的意思是连接被中途掐断,不是速度慢。说明这条网络对高并发连接有干扰(或 CDN 对高频请求做了限流)。单连接测速根本测不出这个问题。
修法是降低并发 + 提高重试:
pnpm config set network-concurrency 3
pnpm config set fetch-retries 6
pnpm config set fetch-retry-mintimeout 5000
pnpm config set fetch-retry-maxtimeout 120000
pnpm config set fetch-timeout 600000
改完立刻顺畅,287 个包一路装完,零个 ECONNRESET。
教训:诊断网络问题要看并发场景,别只看单文件下载速度。
坑二:pnpm 默认不让依赖跑构建脚本
装完包,新的错误来了:
[ERR_PNPM_IGNORED_BUILDS] Ignored build scripts: esbuild@0.28.2
这是 pnpm 10+ 引入的安全策略——默认禁止依赖执行 postinstall 脚本,防止供应链攻击。但 esbuild 恰恰需要 postinstall 来就位它的二进制文件。
更麻烦的是,这个“忽略”会让 pnpm install 退出码为 1。而 pnpm 在执行任何脚本(包括 pnpm build)前会先做一次依赖状态检查:
at runDepsStatusCheck (pnpm.mjs:273323)
at runPnpmCli (pnpm.mjs:271523)
这个检查内部调用 pnpm install,一失败,整个 pnpm build 就被连坐阻断,根本跑不到 astro。这是我卡最久的地方——报错信息看起来是“构建失败”,其实构建压根没开始。
坑三:pnpm 11 换掉了配置字段,而且是两次
我先按老经验,在 package.json 里加:
{
"pnpm": {
"onlyBuiltDependencies": ["esbuild"]
}
}
pnpm 回了一句:
The “pnpm” field in package.json is no longer read by pnpm.
package.json 里的 pnpm 字段在 pnpm 11 已被彻底废弃,设置搬去了 pnpm-workspace.yaml。
换到 pnpm-workspace.yaml 写 onlyBuiltDependencies,还是不生效。最后发现 pnpm 自己把配置文件改写成了这样:
allowBuilds:
esbuild: set this to true or false
onlyBuiltDependencies:
- esbuild
那个 set this to true or false 不是乱码,是 pnpm 生成的占位提示——它在告诉我该填什么。填上 true 之后,postinstall 立刻执行:
.../esbuild@0.28.2/node_modules/esbuild postinstall$ node install.js
.../esbuild@0.28.2/node_modules/esbuild postinstall: Done
所以 pnpm 11 的正确写法是 allowBuilds:
# pnpm-workspace.yaml
allowBuilds:
esbuild: true
最终可用的完整配置
# 网络:治 ECONNRESET
pnpm config set network-concurrency 3
pnpm config set fetch-retries 6
pnpm config set fetch-retry-mintimeout 5000
pnpm config set fetch-retry-maxtimeout 120000
# pnpm-workspace.yaml:治被拦截的构建脚本
allowBuilds:
esbuild: true
# .npmrc:用国内镜像(单连接更快)
registry=https://registry.npmmirror.com
小结
| 现象 | 真实原因 | 修法 |
|---|---|---|
| 满屏 ECONNRESET | 高并发连接被干扰,不是速度问题 | 降并发 + 增重试 |
pnpm build 直接退出 |
依赖状态检查失败连坐了构建 | 放行构建脚本 |
| 配置改了不生效 | pnpm 11 废弃了旧字段 | 改用 allowBuilds |
一句话总结:遇到“构建失败”时,先确认构建到底有没有真的开始跑。