#!/usr/bin/env node "use strict"; /** * dev:hot 启动入口。 * * 目标: * - 不改动 desktop:hot 的默认行为。 * - 使用 cargo-watch 自动重编译并重启 mnote-web。 * - 通过 MNOTE_WEB_DEV_HOT_RELOAD 启用页面端轻量 reload 轮询。 */ const { spawn } = require("node:child_process"); const fs = require("node:fs"); const path = require("node:path"); const rootDir = path.resolve(__dirname, ".."); const desktopHotPath = path.join(rootDir, "scripts", "desktop-hot.js"); function cargoWatchCommand(env = process.env) { const extraArgs = String(env.MNOTE_WEB_DEV_HOT_CARGO_WATCH_ARGS || "").trim(); const watchArgs = [ "-w crates/mnote-web/src", "-w crates/mnote-web/browser", "-w crates/mnote-web/Cargo.toml", "-w Cargo.toml", "-x \"run -p mnote-web --bin mnote-web\"", ]; if (extraArgs) watchArgs.unshift(extraArgs); return `cargo watch ${watchArgs.join(" ")}`; } function bindHost(bindAddr) { const value = String(bindAddr || "").trim(); if (!value) return ""; const ipv6Match = value.match(/^\[([^\]]+)\]:(\d+)$/); if (ipv6Match) return ipv6Match[1]; const lastColon = value.lastIndexOf(":"); if (lastColon <= 0) return ""; return value.slice(0, lastColon); } function bindPort(bindAddr, fallbackPort) { const value = String(bindAddr || "").trim(); const match = value.match(/:(\d+)$/); if (!match) return fallbackPort; const port = Number(match[1]); return Number.isFinite(port) && port > 0 ? Math.floor(port) : fallbackPort; } function devHotBindAddr(env = process.env) { const frontendPort = bindPort(env.MNOTE_WEB_BIND, Number(env.FRONTEND_PORT || 3000)); const host = bindHost(env.MNOTE_WEB_BIND); if (!host || host === "127.0.0.1" || host === "localhost" || host === "::1") { return `0.0.0.0:${frontendPort}`; } return String(env.MNOTE_WEB_BIND).trim(); } function buildDevHotEnv(baseEnv = process.env) { return { ...baseEnv, MNOTE_WEB_DEV_HOT_RELOAD: "1", MNOTE_WEB_BIND: devHotBindAddr(baseEnv), MNOTE_WEB_CMD: String(baseEnv.MNOTE_WEB_CMD || "").trim() || cargoWatchCommand(baseEnv), }; } function main() { if (!fs.existsSync(desktopHotPath)) { throw new Error(`缺少 desktop-hot 入口:${desktopHotPath}`); } const child = spawn(process.execPath, [desktopHotPath], { cwd: rootDir, env: buildDevHotEnv(process.env), stdio: "inherit", }); child.on("exit", (code, signal) => { if (signal) { process.kill(process.pid, signal); return; } process.exit(code ?? 0); }); child.on("error", (error) => { console.error(`[dev-hot] 启动失败:${error.message}`); process.exit(1); }); } if (require.main === module) { main(); } module.exports = { buildDevHotEnv, cargoWatchCommand, devHotBindAddr, };