#!/usr/bin/env node // Build the management console (vite) and the public website (next) in // parallel. Each child inherits stdio with a prefix so logs stay readable. // Exits non-zero if any build fails. Does NOT build the Rust backend. import { spawn } from 'node:child_process'; import { fileURLToPath } from 'node:url'; import { dirname, resolve } from 'node:path'; const root = resolve(dirname(fileURLToPath(import.meta.url)), '..'); /** Run a command, prefixing each output line with [label]. */ function run(label, cmd, args, cwd) { return new Promise((resolveJob) => { const started = Date.now(); const child = spawn(cmd, args, { cwd, shell: false }); const pipe = (stream, sink) => { let buf = ''; stream.on('data', (chunk) => { buf += chunk.toString(); const lines = buf.split('\n'); buf = lines.pop() ?? ''; for (const line of lines) sink.write(`[${label}] ${line}\n`); }); stream.on('end', () => { if (buf) sink.write(`[${label}] ${buf}\n`); }); }; pipe(child.stdout, process.stdout); pipe(child.stderr, process.stderr); child.on('close', (code) => { const secs = ((Date.now() - started) / 1000).toFixed(1); process.stdout.write(`[${label}] ${code === 0 ? '✓ 完成' : '✗ 失败'}(${secs}s)\n`); resolveJob({ label, code: code ?? 1 }); }); child.on('error', (err) => { process.stderr.write(`[${label}] 启动失败: ${err.message}\n`); resolveJob({ label, code: 1 }); }); }); } const jobs = [ run('web', 'npx', ['vite', 'build'], root), run('website', 'npm', ['--prefix', 'website', 'run', 'build'], root), ]; const results = await Promise.all(jobs); const failed = results.filter((r) => r.code !== 0); if (failed.length > 0) { process.stderr.write(`\n构建失败: ${failed.map((r) => r.label).join(', ')}\n`); process.exit(1); } process.stdout.write('\n前端 + 官网构建完成。\n');