解决 pnpm 构建脚本被阻止的问题
当使用 pnpm 时出现 "Ignored build scripts" 警告,这是因为 pnpm 的安全策略阻止了某些包的构建脚本执行。这是 pnpm 的一个安全特性,防止恶意包执行潜在危险的构建脚本。
Ignored build scripts: esbuild.
Run "pnpm approve-builds" to pick which dependencies should be allowed to run scripts.
一、问题原因分析
1.1 为什么会发生?
安全策略:pnpm 默认阻止包的构建脚本(如
postinstall、preinstall等)依赖包行为:某些包(如
esbuild)在安装后需要运行构建脚本许可列表:pnpm 需要明确的许可才能运行这些脚本
1.2 常见触发包
esbuild # 需要编译原生模块 sharp # 图像处理库 bcrypt # 密码加密 sqlite3 # SQLite 数据库绑定 node-sass # Sass 编译器
二、解决方案
2.1 方法一:批准特定包的构建脚本(推荐)
bash
# 批准单个包 pnpm install --ignore-scripts=false esbuild # 或批准所有包(不推荐,有安全风险) pnpm config set ignore-scripts false
2.2 方法二:使用 pnpm 的命令行工具
bash
# 1. 查看需要批准的包 pnpm install # 会显示哪些包的构建脚本被阻止 # 2. 运行批准命令 pnpm approve-builds # 这会交互式地询问您要批准哪些包 # 3. 或者一次性批准所有(谨慎使用) pnpm approve-builds --all
2.3 方法三:项目级配置
在项目根目录创建.npmrc文件:
ini
# .npmrc # 允许 esbuild 运行构建脚本 public-hoist-pattern[]=*esbuild* # 或允许所有包 ignore-scripts=false
或通过命令行设置:
bash
# 仅为当前项目设置 pnpm config set ignore-scripts false # 全局设置(影响所有项目) pnpm config set --global ignore-scripts false
2.4 方法四:使用 package.json 配置
json
{ "name": "my-vue3-app", "version": "1.0.0", "pnpm": { "patchedDependencies": { "esbuild@0.19.0": "patches/esbuild@0.19.0.patch" }, "overrides": { "esbuild": "0.19.0" } }, "scripts": { "postinstall": "pnpm rebuild esbuild" } }三、Vue3 项目的完整解决方案
3.1 针对 Vue3 + Vite 项目的配置
步骤 1:创建 pnpm 配置文件
yaml
# .pnpmfile.cjs module.exports = { hooks: { readPackage(pkg) { // 允许 esbuild 运行脚本 if (pkg.name === 'esbuild') { pkg.scripts = pkg.scripts || {}; } // 允许其他需要构建的包 const allowedPackages = [ 'esbuild', '@esbuild/darwin-x64', '@esbuild/linux-x64', '@esbuild/win32-x64', 'sharp', 'bcrypt' ]; if (allowedPackages.includes(pkg.name)) { console.log(`允许 ${pkg.name} 运行构建脚本`); } return pkg; } } };步骤 2:创建项目级配置
ini
# .npmrc # pnpm 配置 shamefully-hoist=true public-hoist-pattern[]=*esbuild* public-hoist-pattern[]=*vite* public-hoist-pattern[]=*vue* auto-install-peers=true strict-peer-dependencies=false # 允许运行构建脚本 ignore-scripts=false
步骤 3:创建安装脚本
bash
#!/bin/bash # install.sh echo "🚀 安装依赖并批准必要的构建脚本..." # 安装依赖,跳过脚本执行 pnpm install --ignore-scripts # 批准必要的包 pnpm approve-builds --package esbuild pnpm approve-builds --package @esbuild/darwin-x64 pnpm approve-builds --package @esbuild/linux-x64 pnpm approve-builds --package @esbuild/win32-x64 # 如果需要,批准更多包 # pnpm approve-builds --package sharp # pnpm approve-builds --package bcrypt # 重新安装并运行脚本 echo "🔄 重新安装并运行构建脚本..." pnpm install echo "✅ 安装完成!"
3.2 Vue3 + TypeScript + Vite 的完整示例
项目结构:
text
vue3-project/ ├── .npmrc ├── .pnpmfile.cjs ├── package.json ├── pnpm-workspace.yaml ├── install.sh └── src/
package.json配置示例:
json
{ "name": "vue3-vite-app", "private": true, "version": "1.0.0", "type": "module", "scripts": { "dev": "vite", "build": "vue-tsc && vite build", "preview": "vite preview", "install:safe": "./install.sh", "postinstall": "pnpm rebuild esbuild && pnpm rebuild @esbuild/darwin-x64", "setup": "pnpm install --ignore-scripts && pnpm approve-builds --all && pnpm install" }, "dependencies": { "vue": "^3.3.4", "vue-router": "^4.2.0", "pinia": "^2.1.0" }, "devDependencies": { "@vitejs/plugin-vue": "^4.2.3", "@vue/tsconfig": "^0.4.0", "esbuild": "^0.19.0", "typescript": "^5.0.2", "vite": "^4.3.9", "vue-tsc": "^1.4.2" }, "packageManager": "pnpm@8.6.0" }pnpm-workspace.yaml(如果使用 monorepo):
yaml
packages: # 所有包都在 packages/ 目录下 - 'packages/**' # 不包括 tests 目录 - '!**/test/**'
四、针对不同场景的解决方案
4.1 开发环境解决方案
方案 A:宽松策略(适合个人项目)
bash
# 1. 禁用脚本阻止 pnpm config set ignore-scripts false # 2. 安装依赖 pnpm install # 3. 如果需要,恢复安全设置 pnpm config set ignore-scripts true
方案 B:选择性批准(适合团队项目)
创建scripts/setup-dev.js:
javascript
// scripts/setup-dev.js const { execSync } = require('child_process'); const fs = require('fs'); const path = require('path'); // 需要批准的包列表 const ALLOWED_PACKAGES = [ 'esbuild', '@esbuild/darwin-x64', '@esbuild/linux-x64', '@esbuild/win32-x64', '@esbuild/darwin-arm64', '@esbuild/linux-arm64' ]; console.log('🔧 设置开发环境...'); try { // 检查是否使用 pnpm const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8')); const isPnpm = packageJson.packageManager?.includes('pnpm') || fs.existsSync('pnpm-lock.yaml'); if (!isPnpm) { console.log('📦 使用 npm/yarn,无需特殊处理'); process.exit(0); } console.log('📦 检测到使用 pnpm,处理构建脚本...'); // 方法1:使用 approve-builds console.log('✅ 批准必要的构建脚本...'); ALLOWED_PACKAGES.forEach(pkg => { try { execSync(`pnpm approve-builds --package ${pkg}`, { stdio: 'inherit' }); } catch (error) { console.log(`⚠️ 无法批准 ${pkg}: ${error.message}`); } }); // 方法2:重新安装 console.log('🔄 重新安装依赖...'); execSync('pnpm install', { stdio: 'inherit' }); console.log('🎉 开发环境设置完成!'); } catch (error) { console.error('❌ 设置失败:', error.message); process.exit(1); }在package.json中:
json
{ "scripts": { "prepare": "node scripts/setup-dev.js", "dev": "vite" } }4.2 CI/CD 环境解决方案
GitHub Actions 示例:
yaml
# .github/workflows/ci.yml name: CI on: push: branches: [main, develop] pull_request: branches: [main] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '18' cache: 'pnpm' - name: Setup pnpm uses: pnpm/action-setup@v2 with: version: 8 - name: Install dependencies run: | # 允许所有构建脚本(CI环境相对安全) pnpm config set ignore-scripts false # 安装依赖 pnpm install # 恢复安全设置(可选) pnpm config set ignore-scripts true - name: Build run: pnpm run build - name: Test run: pnpm run test
4.3 Docker 环境解决方案
Dockerfile:
dockerfile
# 使用 Node.js 官方镜像 FROM node:18-alpine # 安装 pnpm RUN npm install -g pnpm WORKDIR /app # 复制配置文件 COPY package.json pnpm-lock.yaml .npmrc ./ # 安装依赖,允许构建脚本 RUN pnpm config set ignore-scripts false && \ pnpm install --frozen-lockfile && \ pnpm config set ignore-scripts true # 复制源代码 COPY . . # 构建应用 RUN pnpm run build # 暴露端口 EXPOSE 3000 # 启动命令 CMD ["pnpm", "preview"]
五、常见问题排查
5.1 问题:pnpm approve-builds命令不存在
bash
# pnpm 6.x 版本需要安装插件 pnpm add -g @pnpm/plugin-commands-script-runners # 或升级 pnpm 到 7.x 或更高版本 npm install -g pnpm@latest # 检查版本 pnpm --version
5.2 问题:特定包仍然无法构建
针对 esbuild 的特殊处理:
bash
# 1. 清理缓存 pnpm store prune # 2. 删除 node_modules rm -rf node_modules # 3. 手动下载并构建 esbuild PNPM_IGNORE_SCRIPTS=false pnpm add esbuild # 4. 或使用替代安装方法 npm install esbuild --ignore-scripts=false
5.3 问题:跨平台兼容性
创建平台特定的安装脚本:
javascript
// scripts/install-platform.js const os = require('os'); const { execSync } = require('child_execute'); const platform = os.platform(); const arch = os.arch(); console.log(`平台: ${platform}-${arch}`); const PLATFORM_PACKAGES = { 'darwin-x64': ['@esbuild/darwin-x64'], 'darwin-arm64': ['@esbuild/darwin-arm64'], 'linux-x64': ['@esbuild/linux-x64'], 'linux-arm64': ['@esbuild/linux-arm64'], 'win32-x64': ['@esbuild/win32-x64'], 'win32-arm64': ['@esbuild/win32-arm64'] }; const key = `${platform}-${arch}`; const packages = PLATFORM_PACKAGES[key] || []; if (packages.length > 0) { console.log(`批准平台特定包: ${packages.join(', ')}`); packages.forEach(pkg => { try { execSync(`pnpm approve-builds --package ${pkg}`, { stdio: 'inherit' }); } catch (error) { console.log(`警告: 无法批准 ${pkg}`); } }); }六、安全考虑和最佳实践
6.1 安全建议
最小权限原则:只批准必要的包
审查包来源:只批准来自可信源的包
定期更新:保持包的最新版本
使用锁文件:确保依赖版本一致
6.2 推荐的批准列表
对于 Vue3 项目,通常只需要批准:
bash
# 核心构建工具 pnpm approve-builds --package esbuild # 平台特定 esbuild pnpm approve-builds --package @esbuild/darwin-x64 pnpm approve-builds --package @esbuild/linux-x64 pnpm approve-builds --package @esbuild/win32-x64 # 可选:如果使用这些包 # pnpm approve-builds --package sharp # pnpm approve-builds --package bcrypt6.3 完整的安全安装脚本
bash
#!/bin/bash # safe-install.sh set -e # 遇到错误时停止 echo "🔒 安全安装依赖..." # 1. 只安装,不运行脚本 echo "📦 安装依赖(跳过脚本)..." pnpm install --ignore-scripts # 2. 批准白名单中的包 echo "✅ 批准必要的构建脚本..." APPROVAL_LIST=( "esbuild" "@esbuild/darwin-x64" "@esbuild/linux-x64" "@esbuild/win32-x64" ) for package in "${APPROVAL_LIST[@]}"; do echo "处理: $package" if pnpm list "$package" 2>/dev/null | grep -q "$package"; then echo "批准: $package" pnpm approve-builds --package "$package" --yes || true else echo "跳过: $package (未安装)" fi done # 3. 运行批准的构建脚本 echo "🔄 运行构建脚本..." pnpm rebuild echo "🎉 安装完成!" echo "" echo "已批准的包:" printf '%s\n' "${APPROVAL_LIST[@]}"七、总结
7.1 快速解决方案
bash
# 最简单的方法(适合大多数 Vue3 项目) pnpm install --ignore-scripts=false # 或 pnpm config set ignore-scripts false pnpm install pnpm config set ignore-scripts true # 恢复安全设置
7.2 生产环境推荐
bash
# 1. 创建 .npmrc 文件 echo "ignore-scripts=false" >> .npmrc # 2. 使用 Docker 或 CI 脚本控制 # 3. 在安全的环境中构建
7.3 团队项目标准配置
创建项目初始化脚本:
bash
#!/bin/bash # init-project.sh echo "初始化 Vue3 + pnpm 项目..." # 创建必要的配置文件 cat > .npmrc << EOF # pnpm 配置 shamefully-hoist=true public-hoist-pattern[]=*esbuild* public-hoist-pattern[]=*vite* ignore-scripts=false strict-peer-dependencies=false EOF # 安装依赖 pnpm install echo "项目初始化完成!" echo "运行: pnpm run dev"
通过以上方法,您可以安全地解决 pnpm 的构建脚本阻止问题,同时保持项目的安全性。对于 Vue3 项目,主要需要关注esbuild相关包的构建脚本批准。