2026-09-11 11:53:30 +00:00
|
|
|
import {spawn, spawnSync} from "node:child_process"
|
|
|
|
|
import {createRequire} from "node:module"
|
|
|
|
|
import {dirname, resolve} from "node:path"
|
|
|
|
|
import {fileURLToPath} from "node:url"
|
|
|
|
|
|
2026-09-24 02:47:46 +00:00
|
|
|
const root = resolve(dirname(fileURLToPath(import.meta.url)), "../..")
|
2026-09-11 11:53:30 +00:00
|
|
|
const require = createRequire(import.meta.url)
|
|
|
|
|
const windows = process.platform === "win32"
|
|
|
|
|
let server
|
|
|
|
|
let child
|
|
|
|
|
let forceShutdown
|
|
|
|
|
|
|
|
|
|
const terminate = (signal = "SIGTERM") => {
|
|
|
|
|
if (child?.pid) {
|
|
|
|
|
if (windows) {
|
|
|
|
|
if (typeof child.exitCode !== "number" && !child.signalCode) {
|
|
|
|
|
const result = spawnSync("taskkill", ["/PID", String(child.pid), "/T", "/F"])
|
|
|
|
|
if (result.error || result.status !== 0) {
|
|
|
|
|
console.error(result.error || result.stderr.toString().trim())
|
|
|
|
|
process.exitCode = 1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
try {
|
|
|
|
|
// cap run owns shell/npm descendants, so stopping just its PID leaves Electron running.
|
|
|
|
|
process.kill(-child.pid, signal)
|
|
|
|
|
} catch (error) {
|
|
|
|
|
if (error.code !== "ESRCH") {
|
|
|
|
|
console.error(error)
|
|
|
|
|
process.exitCode = 1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const onSignal = signal => {
|
|
|
|
|
process.exitCode = signal === "SIGINT" ? 130 : 143
|
|
|
|
|
terminate()
|
|
|
|
|
forceShutdown ??= setTimeout(() => terminate("SIGKILL"), 5000)
|
|
|
|
|
forceShutdown.unref()
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-11 12:29:23 +00:00
|
|
|
// Vite also exits on SIGTERM; clean up descendants even if its handler exits first.
|
|
|
|
|
const onExit = () => terminate("SIGKILL")
|
2026-09-11 11:53:30 +00:00
|
|
|
|
2026-09-11 12:29:23 +00:00
|
|
|
try {
|
2026-09-11 11:53:30 +00:00
|
|
|
process.chdir(root)
|
|
|
|
|
process.env.NODE_ENV = "development"
|
|
|
|
|
process.env.FLOTILLA_DESKTOP = "1"
|
|
|
|
|
const {createServer} = await import("vite")
|
|
|
|
|
|
|
|
|
|
server = await createServer({
|
|
|
|
|
mode: "development",
|
|
|
|
|
server: {host: "127.0.0.1", strictPort: true},
|
|
|
|
|
})
|
|
|
|
|
|
2026-09-11 12:29:23 +00:00
|
|
|
await server.listen()
|
|
|
|
|
server.printUrls()
|
|
|
|
|
child = spawn(
|
|
|
|
|
process.execPath,
|
|
|
|
|
[
|
|
|
|
|
// Capacitor 8.3.4 otherwise logs rejected platform hooks without failing the process.
|
|
|
|
|
"--unhandled-rejections=strict",
|
|
|
|
|
require.resolve("@capacitor/cli/bin/capacitor"),
|
|
|
|
|
"run",
|
|
|
|
|
"@capawesome/capacitor-electron",
|
|
|
|
|
],
|
|
|
|
|
{
|
|
|
|
|
cwd: root,
|
|
|
|
|
stdio: "inherit",
|
|
|
|
|
detached: !windows,
|
|
|
|
|
env: {...process.env, FLOTILLA_DESKTOP_DEV_URL: server.resolvedUrls.local[0]},
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
process.on("SIGINT", onSignal)
|
|
|
|
|
process.on("SIGTERM", onSignal)
|
|
|
|
|
process.on("exit", onExit)
|
|
|
|
|
const code = await new Promise((resolve, reject) => {
|
|
|
|
|
child.once("error", reject)
|
|
|
|
|
child.once("close", (code, signal) => resolve(signal ? 1 : code))
|
|
|
|
|
})
|
|
|
|
|
process.exitCode ||= code ?? 1
|
2026-09-11 11:53:30 +00:00
|
|
|
} catch (error) {
|
2026-09-11 12:29:23 +00:00
|
|
|
console.error(error)
|
|
|
|
|
process.exitCode = 1
|
2026-09-11 11:53:30 +00:00
|
|
|
} finally {
|
|
|
|
|
terminate("SIGKILL")
|
|
|
|
|
await server?.close()
|
|
|
|
|
clearTimeout(forceShutdown)
|
|
|
|
|
process.removeListener("SIGINT", onSignal)
|
|
|
|
|
process.removeListener("SIGTERM", onSignal)
|
2026-09-11 12:29:23 +00:00
|
|
|
process.removeListener("exit", onExit)
|
2026-09-11 11:53:30 +00:00
|
|
|
}
|