解决 pnpm 构建脚本被阻止(Ignored build scripts)的问题

解决 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 默认阻止包的构建脚本(如 postinstallpreinstall 等)

  • 依赖包行为 :某些包(如 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 安全建议

  1. 最小权限原则:只批准必要的包

  2. 审查包来源:只批准来自可信源的包

  3. 定期更新:保持包的最新版本

  4. 使用锁文件:确保依赖版本一致

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 相关包的构建脚本批准。

相关推荐
LYFlied4 天前
幽灵依赖详解
npm·pnpm·打包工具·yarn·工程化·包管理工具·幽灵依赖
LYFlied4 天前
前端项目包管理器怎么选?
前端·面试·npm·pnpm·yarn·工程化·包管理器
F2E_Zhangmo4 天前
pnpm如何对node_modules打补丁
webpack·npm·pnpm
宁雨桥12 天前
使用pnpm构建高效Monorepo:从零到一的完整指南
前端·pnpm·项目架构
这是个栗子1 个月前
【问题解决】用pnpm创建的 Vue3项目找不到 .eslintrc.js文件 及 后续的eslint配置的解决办法
javascript·vue.js·pnpm·eslint
gs801402 个月前
pnpm + webpack + vue 项目依赖缺失错误排查与解决
pnpm·1024程序员节
JinSoooo2 个月前
pnpm monorepo 联调:告别 --global 参数
开发语言·javascript·ecmascript·pnpm
奋飛2 个月前
Monorepo系列:Pnpm Workspace 搭建 Monorepo
pnpm·monorepo·pnpm workspace·catalogs·pnpm filter
前端架构师-老李2 个月前
npm、yarn、pnpm的对比和优略
前端·npm·node.js·pnpm·yarn