解决 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 bcrypt
6.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 相关包的构建脚本批准。