const fs = require("node:fs"); const path = require("node:path"); const net = require("node:net"); const { spawn, spawnSync } = require("node:child_process"); const projectRoot = path.resolve(__dirname, ".."); const serverUrl = process.env.VITE_DEV_SERVER_URL || "http://127.0.0.1:3354"; const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm"; const requiredNodeMajor = 20; const checkOnly = process.argv.includes("--check"); function log(message = "") { process.stdout.write(`${message}\n`); } function fail(message) { process.stderr.write(`\n[start-local] ${message}\n`); process.exit(1); } function parseServerUrl(url) { try { return new URL(url); } catch { fail(`VITE_DEV_SERVER_URL 无效:${url}`); } } function checkCommand(command, args, label) { const result = spawnSync(command, args, { cwd: projectRoot, encoding: "utf8", stdio: "pipe", shell: process.platform === "win32", }); if (result.error || result.status !== 0) { fail(`未检测到 ${label},请先安装后再运行。`); } return (result.stdout || result.stderr || "").trim(); } function checkNodeVersion() { const major = Number(process.versions.node.split(".")[0]); if (!Number.isFinite(major) || major < requiredNodeMajor) { fail(`当前 Node.js 为 ${process.version},建议使用 .nvmrc 指定的 Node.js ${requiredNodeMajor}。`); } } function checkDependencies() { const viteBin = path.join(projectRoot, "node_modules", ".bin", process.platform === "win32" ? "vite.cmd" : "vite"); const electronPackage = path.join(projectRoot, "node_modules", "electron", "package.json"); if (!fs.existsSync(viteBin) || !fs.existsSync(electronPackage)) { fail("依赖尚未安装,请先执行:npm install"); } } function assertPortAvailable(host, port) { return new Promise((resolve, reject) => { const server = net.createServer(); server.once("error", error => { if (error.code === "EADDRINUSE") { reject(new Error(`端口 ${port} 已被占用,请先关闭已有 Vite/Electron 开发进程。`)); return; } reject(error); }); server.once("listening", () => { server.close(resolve); }); server.listen(port, host); }); } async function main() { process.chdir(projectRoot); const parsedUrl = parseServerUrl(serverUrl); const host = parsedUrl.hostname || "127.0.0.1"; const port = Number(parsedUrl.port || 3344); log("========================================"); log(" 本地快速启动:Vite + Electron"); log("========================================"); log(`项目目录:${projectRoot}`); log(`开发地址:${serverUrl}`); log(""); checkNodeVersion(); const npmVersion = checkCommand(npmCommand, ["--version"], "npm"); checkDependencies(); await assertPortAvailable(host, port); log(`Node.js:${process.version}`); log(`npm:${npmVersion}`); log(""); if (checkOnly) { log("自检通过:可以执行 npm run start:local 或双击 start-local.bat 启动。"); return; } log("正在启动应用,关闭此窗口即可停止开发服务。"); log(""); const child = spawn(npmCommand, ["run", "dev:electron"], { cwd: projectRoot, stdio: "inherit", env: { ...process.env, VITE_DEV_SERVER_URL: serverUrl, }, shell: process.platform === "win32", }); child.on("exit", code => { process.exit(code ?? 0); }); child.on("error", error => { fail(`启动失败:${error.message}`); }); } main().catch(error => { fail(error.message); });