#!/usr/bin/env node /** * 开发启动器:先启动 server (tsx watch),等它就绪,再启动 client (vite)。 * Ctrl+C 时同时关闭两个进程。启动前自动清理旧进程。 */ const { spawn, execSync } = require("child_process"); const http = require("http"); const ROOT = __dirname + "/.."; const SERVER_PORT = 3001; const CLIENT_PORT = 9527; function killPort(port) { try { execSync(`lsof -ti :${port} | xargs kill -9 2>/dev/null`, { stdio: "ignore" }); } catch {} } function startProcess(label, cwd, ...args) { const child = spawn(...args, { cwd, stdio: "inherit" }); child.on("exit", (code) => { console.log(`[${label}] exited (${code})`); process.exit(code || 0); }); return child; } function waitForServer(url, maxRetries = 30) { return new Promise((resolve, reject) => { let tries = 0; function check() { http.get(url, (res) => { if (res.statusCode === 200) resolve(); else retry(); }).on("error", retry); } function retry() { if (++tries >= maxRetries) { reject(new Error(`Server did not start within ${maxRetries}s`)); return; } setTimeout(check, 1000); } check(); }); } async function main() { // 清理旧进程 killPort(SERVER_PORT); killPort(CLIENT_PORT); console.log("[dev] Starting server..."); startProcess("server", ROOT, "npm", ["run", "dev:server"]); console.log("[dev] Waiting for server..."); await waitForServer(`http://localhost:${SERVER_PORT}/api/v1/templates`); console.log("[dev] Server ready."); console.log("[dev] Starting client..."); startProcess("client", ROOT, "npm", ["run", "dev:client"]); } main().catch((err) => { console.error(err.message); process.exit(1); });