1 changed files with 103 additions and 0 deletions
-
103build-all.mjs
@ -0,0 +1,103 @@ |
|||||
|
/** |
||||
|
* 前端并行构建脚本 |
||||
|
* |
||||
|
* 并行执行 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); |
||||
|
}); |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue