拓冰建站拓冰建站
首页 / 资讯中心 / 正文

pnpm 报错 ERR_PNPM_IGNORED_BUILDS 解决指南:@parcel/watcher 与 canvas 构建脚本放行

1. 这个报错到底在说什么第一次看到[ERR_PNPM_IGNORED_BUILDS] Ignored build scripts: parcel/watcher2.5.6, canvas2.11.2这行红字的时候很多人第一反应是我是不是装错了什么。其实恰恰相反这不是安装失败而是 pnpm 主动告诉你有两个依赖包带了构建脚本但它默认没有执行。pnpm 从 v10 开始收紧了安全策略。以前 npm 和早期 pnpm 在安装依赖时只要包里有postinstall、preinstall、install这类生命周期脚本就会自动跑一遍。这个机制方便是方便但风险也大——你装一个第三方包它能在你机器上执行任意命令供应链投毒基本都从这里下手。pnpm 的做法是默认不执行这些脚本装完之后给你一条警告把被忽略的包列出来让你自己决定要不要放行。所以这条报错翻译成人话就是parcel/watcher和canvas这两个包想跑构建脚本我拦下来了你要用它们的话得手动批准。parcel/watcher是文件监听库Parcel 打包器和不少工具链都在用它带原生模块安装时需要编译或下载对应平台的二进制。canvas是 Node.js 环境下的 Canvas 绘图库底层依赖 Cairo、Pango 这些 C 库安装时要用 node-gyp 编译原生扩展。这两个包都属于必须跑构建脚本才能正常工作的类型被拦下来之后运行时大概率会报Cannot find module或者直接抛原生模块加载失败。这篇文章就是围绕这个报错把原因、几种解法、各自的适用场景、以及我踩过的坑一次性讲清楚。不管你是刚接触 pnpm 的新手还是从 npm/yarn 迁移过来的老手看完都能直接动手解决。2. 为什么 pnpm 要默认拦截构建脚本2.1 供应链安全的现实考量要理解这个改动得先知道构建脚本能干什么。一个包的postinstall脚本权限和你的用户账户完全一样。它可以读你的环境变量、翻你的 SSH 密钥、往~/.bashrc里塞东西、连外部服务器下载可执行文件。2022 年那波node-ipc事件、后来的colors和faker作者自毁事件都是靠生命周期脚本搞出来的。pnpm 的维护者在这件事上态度很明确默认不信任显式授权。这个思路和现代包管理器的方向一致比如 npm 也在讨论类似的默认禁用策略。代价就是你现在装完依赖可能要多一步操作但换来的是可控性。2.2 被忽略的包会有什么后果被拦截的包不会消失node_modules里照样有它的文件只是构建脚本没跑。对于纯 JS 包这没影响因为不需要编译。但对于带原生扩展的包问题就来了parcel/watcher的原生模块没编译require的时候会去找.node文件找不到就报错canvas的build/Release/canvas.node不存在require(canvas)直接抛异常具体表现可能是启动时报Error: Cannot find module ../build/Release/canvas.node也可能是运行到某个功能时才崩。所以看到这条警告如果你的项目确实用到这两个包就必须处理。2.3 为什么偏偏是这两个包parcel/watcher和canvas有个共同点它们都依赖平台相关的原生代码。parcel/watcher为了性能用 C 写了文件监听的核心逻辑不同操作系统Windows、macOS、Linux编译出来的二进制不一样。canvas更重它把 Cairo 图形库、Pango 文字排版库、libjpeg、libpng 这些 C 库全绑进来了安装时要用 node-gyp 现场编译。这类包在 npm 生态里叫 native addon它们的构建脚本不是可选项是必需品。pnpm 拦截它们等于把要不要编译的决定权交给你。3. 三种解法按场景选3.1 方案一用 pnpm approve-builds 交互式放行pnpm 提供了一个专门的命令来处理这件事pnpm approve-builds执行后会列出所有被忽略的、带构建脚本的包让你用空格键勾选、回车确认。选中的包会被记录到package.json的pnpm.onlyBuiltDependencies字段里下次安装就不会再拦。这个方案适合本地开发、包数量不多、你想逐个确认的情况。缺点是每次遇到新包都要手动勾一次CI 环境里没法交互。3.2 方案二在 package.json 里显式声明白名单如果你已经知道哪些包需要放行直接在package.json里写死更省事{ pnpm: { onlyBuiltDependencies: [ parcel/watcher, canvas ] } }这样配置之后pnpm install会自动执行这两个包的构建脚本其他包依然被拦截。这是我最推荐的方案因为它把信任哪些包这件事变成了代码的一部分提交到仓库里团队所有人、CI 环境行为一致。注意onlyBuiltDependencies是白名单写进去的包才会跑脚本。如果你后面又遇到别的包被拦记得往数组里加。3.3 方案三全局放开不推荐但要知道pnpm 也支持完全关闭这个拦截pnpm config set ignore-scripts false或者在.npmrc里写ignore-scriptsfalse这样所有包的构建脚本都会执行回到 npm 的老行为。我不推荐这么做因为等于放弃了 pnpm 给你的安全保护。除非你在一个完全隔离的、可信的构建环境里否则别用。3.4 三种方案对比方案适用场景安全性团队一致性操作成本approve-builds本地开发、临时处理高低本地记录每次手动onlyBuiltDependencies项目长期配置、CI高高随仓库走一次配置ignore-scriptsfalse隔离环境、临时调试低中一次配置我的建议很直接项目里用方案二遇到新包临时用方案一确认方案三只在万不得已时用。4. 实操从报错到跑通4.1 复现问题先确认你确实遇到了这个问题。在一个用 pnpm 管理的项目里package.json依赖里有canvas或parcel/watcher然后pnpm install你会看到类似输出WARN Ignored build scripts: parcel/watcher2.5.6, canvas2.11.2. Run pnpm approve-builds to pick which dependencies should be allowed to run scripts.注意版本号可能不同parcel/watcher和canvas的版本随你项目锁定。4.2 用 approve-builds 放行pnpm approve-builds终端会显示一个多选列表用方向键移动、空格勾选? Choose which packages to build (Press space to select, a to toggle all, i to invert selection) ❯◯ parcel/watcher ◯ canvas勾选后回车pnpm 会立即执行这些包的构建脚本并把它们写入package.json的pnpm.onlyBuiltDependencies。4.3 手动配置 onlyBuiltDependencies如果你更喜欢直接改文件打开package.json找到或新增pnpm字段{ name: your-project, dependencies: { canvas: ^2.11.2 }, pnpm: { onlyBuiltDependencies: [ parcel/watcher, canvas ] } }保存后重新安装pnpm install这次应该看到构建脚本正常执行没有警告了。4.4 验证是否真的生效光看安装没报错还不够得确认原生模块真的编译出来了。对于canvasnode -e const { createCanvas } require(canvas); const c createCanvas(200, 100); const ctx c.getContext(2d); ctx.fillStyle red; ctx.fillRect(0, 0, 200, 100); console.log(c.toBuffer(image/png).length);如果输出一个正数PNG 字节数说明canvas能正常工作。如果报Cannot find module说明构建脚本还是没跑成功往下看排查部分。对于parcel/watchernode -e const watcher require(parcel/watcher); console.log(typeof watcher.subscribe);输出function就对了。4.5 CI 环境下的处理CI 里没法交互所以必须用onlyBuiltDependencies。另外要注意canvas编译需要系统依赖Ubuntu/Debianapt-get install -y build-essential libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-devmacOSbrew install pkg-config cairo pango libpng jpeg giflib librsvgWindows装windows-build-tools或者 Visual Studio Build Tools再配好 PythonCI 的 Docker 镜像里如果缺这些构建脚本会失败但 pnpm 可能只报一个 warning不会让整个 install 失败。所以 CI 里最好加一步验证比如上面那个node -e检查。5. 常见问题与排查5.1 放行了还是报 Cannot find module最常见的原因是构建脚本执行了但编译失败。pnpm 默认会把构建输出藏起来你需要看详细日志pnpm install --reporterappend-only或者直接手动触发某个包的构建pnpm rebuild canvaspnpm rebuild会强制重新跑指定包的构建脚本并且把输出打出来。如果看到node-gyp报错基本就是系统缺 C 库或者 Python 环境问题。5.2 node-gyp 报 Python 相关错误canvas编译依赖 Pythonnode-gyp 用。如果报gyp ERR! find Python检查python3 --version确保有 Python 3并且 node-gyp 能找到它。必要时设置pnpm config set python /usr/bin/python3Windows 上更麻烦建议直接用windows-build-tools或者手动装 Visual Studio Build Tools 并勾选 C 工作负载。5.3 换了 Node 版本后 canvas 又崩了原生模块和 Node 的 ABI 版本绑定。你从 Node 18 升到 Node 20之前编译的canvas.node就不兼容了需要重新编译pnpm rebuild canvas如果项目里多个包都有原生模块直接pnpm rebuild全部重编一遍。5.4 pnpm 版本太老没有 approve-buildsapprove-builds是 pnpm 10 才有的命令。如果你用的是 pnpm 9 或更早要么升级npm install -g pnpmlatest要么直接用onlyBuiltDependencies配置这个字段在 pnpm 9 后期版本也支持。升级前记得看下项目的packageManager字段别和团队用的版本冲突。5.5 问题速查表现象可能原因解决安装后仍报 Cannot find module构建脚本没跑或编译失败pnpm rebuild 包名看详细日志node-gyp 报 Python 错误缺 Python 或路径不对装 Python 3pnpm config set python换 Node 版本后崩ABI 不兼容pnpm rebuild重编CI 里静默失败缺系统 C 库镜像里装 build-essential 等没有 approve-builds 命令pnpm 版本太老升级 pnpm 或用 onlyBuiltDependencies5.6 几个我踩过的坑第一个坑以为onlyBuiltDependencies写包名就行结果写成了parcel/watcher2.5.6这种带版本号的格式pnpm 不认。这个字段只写包名不带版本。第二个坑在 monorepo 里onlyBuiltDependencies要写在根package.json写在子包里不生效。pnpm 的配置是根级的。第三个坑canvas在 Apple SiliconM1/M2上编译有时候会因为 Rosetta 和原生 arm64 的库混用出问题。解决办法是确保 Homebrew 装的是 arm64 版本node也是 arm64 版本别用 Rosetta 跑 Node。第四个坑Docker 构建时为了减小镜像用了node:alpine结果canvas编译需要的glibc相关库在 Alpine 的 musl 上跑不通。要么换node:slimDebian 基础要么在 Alpine 里装build-base cairo-dev pango-dev这些。6. 关于 pnpm 构建策略的延伸理解6.1 这个机制背后的设计哲学pnpm 把安装和执行代码拆开了。传统包管理器把这两件事混在一起装依赖就等于信任所有依赖的代码。pnpm 的做法是安装归安装执行归执行执行需要显式授权。这个思路其实和现代操作系统的权限模型一致——App 装进来不代表能随便访问你的通讯录得你点同意。理解这一点你就不会觉得这个报错烦人了。它是在提醒你这两个包要在你机器上跑代码你确认一下。6.2 对项目迁移的影响如果你从 npm 或 yarn 迁移到 pnpm遇到这个报错是必然的。迁移时建议先跑一次pnpm install把所有被拦的包列出来逐个判断哪些是真需要构建脚本的原生模块、需要下载二进制的把确认的写进onlyBuiltDependencies跑一遍测试确认没有运行时缺模块的问题别图省事直接ignore-scriptsfalse那样等于把 pnpm 的安全优势丢了还不如继续用 npm。6.3 和内网/离线环境的配合有些团队在内网环境用 pnpm依赖从私有 registry 拉。这种场景下canvas的构建脚本可能还需要联网下载预编译二进制某些包会尝试从 GitHub Releases 拉。如果内网不通外网构建会失败。解决办法是提前把二进制缓存好或者配置canvas_binary_host_mirror指向内网镜像。这个和onlyBuiltDependencies是两回事但经常一起出现值得注意。7. 最后几句实在话ERR_PNPM_IGNORED_BUILDS这个报错本质上不是 bug是 pnpm 在帮你做安全决策。处理它的核心就一句话把真正需要跑构建脚本的包写进package.json的pnpm.onlyBuiltDependencies。我自己的习惯是新项目初始化时就把常用的原生模块包列进去省得每次装完再处理。团队协作时这个配置随仓库走谁拉下来行为都一样CI 也不会因为交互式命令卡住。如果你正在从 npm 迁移别急着关掉这个保护。花十分钟把被拦的包过一遍你会对自己项目的依赖有更清楚的认识——哪些是纯 JS 的轻量包哪些是拖家带口带原生编译的重家伙心里有数之后后面排查问题会快很多。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门