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