/** * 前端并行构建脚本 * * 并行执行 client/ (SDK) 和 frontend/ (管理后台) 的 npm install + build, * 替代 pom.xml 中两个串行的 frontend-maven-plugin。 * * 用法: node build-all.mjs [--skip-install] * --skip-install 跳过 npm install(仅执行 build),热构建加速 */ import { spawn } from 'node:child_process'; import { resolve, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; import { existsSync } from 'node:fs'; const __dirname = dirname(fileURLToPath(import.meta.url)); const ROOT = resolve(__dirname); // 优先使用 Maven 安装的本地 Node.js/npm,保持版本一致 const isWindows = process.platform === 'win32'; const LOCAL_NPM = isWindows ? resolve(ROOT, 'node', 'npm.cmd') : resolve(ROOT, 'node', 'npm'); const NPM_EXE = existsSync(LOCAL_NPM) ? LOCAL_NPM : 'npm'; const SKIP_INSTALL = process.argv.includes('--skip-install'); const PROJECTS = [ { name: 'client', dir: resolve(ROOT, 'client') }, { name: 'frontend', dir: resolve(ROOT, 'frontend') }, ]; /** * 在指定目录执行 npm 命令,返回 Promise。 * 将 stdout/stderr 实时转发到控制台,带项目前缀。 */ function runNpm(cwd, args, label) { return new Promise((resolvePromise, reject) => { const child = spawn(NPM_EXE, args, { cwd, shell: isWindows, // Windows 需要 shell 才能找到 npm.cmd stdio: ['ignore', 'pipe', 'pipe'], env: { ...process.env }, }); child.stdout.on('data', (data) => { process.stdout.write(`[${label}] ${data}`); }); child.stderr.on('data', (data) => { process.stderr.write(`[${label}] ${data}`); }); child.on('close', (code) => { if (code === 0) { console.log(`[${label}] ✓ 完成`); resolvePromise(); } else { reject(new Error(`[${label}] 失败,退出码: ${code}`)); } }); child.on('error', (err) => { reject(new Error(`[${label}] 启动失败: ${err.message}`)); }); }); } async function main() { const startTime = Date.now(); // ===== Phase 1: npm install(两个项目并行) ===== if (!SKIP_INSTALL) { console.log('\n=== Phase 1: npm install ==='); const installResults = await Promise.allSettled( PROJECTS.map((p) => runNpm(p.dir, ['install', '--prefer-offline', '--no-audit', '--no-fund'], p.name) ) ); for (const r of installResults) { if (r.status === 'rejected') throw r.reason; } } else { console.log('\n=== Phase 1: 跳过 npm install(--skip-install) ==='); } // ===== Phase 2: npm run build(两个项目并行) ===== console.log('\n=== Phase 2: npm run build ==='); const buildResults = await Promise.allSettled( PROJECTS.map((p) => runNpm(p.dir, ['run', 'build'], p.name)) ); for (const r of buildResults) { if (r.status === 'rejected') throw r.reason; } const elapsed = ((Date.now() - startTime) / 1000).toFixed(1); console.log(`\n✓ 全部前端构建完成 (${elapsed}s)`); } main().catch((err) => { console.error(`\n✗ 前端构建失败: ${err.message}`); process.exit(1); });