import { spawn } from 'child_process' import fs from 'fs' import path from 'path' const root = process.cwd() const electronBin = path.join(root, 'node_modules', '.bin', process.platform === 'win32' ? 'electron.cmd' : 'electron') const bunBin = process.execPath function run(command: string, args: string[]) { return spawn(command, args, { stdio: 'inherit', env: process.env }) } function runOnce(command: string, args: string[]) { return new Promise((resolve, reject) => { const proc = run(command, args) proc.on('exit', (code) => { if (code === 0) resolve() else reject(new Error(`exit ${code}`)) }) }) } async function runBuildOnce() { await runOnce(bunBin, ['run', 'build:lib']) } function startBuildWatch() { const args = [ 'build', 'src/index.ts', 'src/main.ts', 'src/desktop.ts', '--outdir', 'dist', '--target', 'node', '--format', 'esm', '--sourcemap', '--external', 'zstd-wasm-decoder', '--external', 'express', '--watch' ] return run(bunBin, args) } let electronProcess: ReturnType | null = null let restartTimer: NodeJS.Timeout | null = null let restarting = false function startElectron() { if (electronProcess) return electronProcess = run(electronBin, ['./scripts/electron-main.js']) electronProcess.on('exit', () => { electronProcess = null }) } async function stopElectron() { if (!electronProcess) return const proc = electronProcess await new Promise((resolve) => { proc.once('exit', () => resolve()) proc.kill('SIGTERM') setTimeout(() => { if (!proc.killed) { proc.kill('SIGKILL') } }, 3000) }) } function scheduleRestart() { if (restartTimer) clearTimeout(restartTimer) restartTimer = setTimeout(async () => { if (restarting) return restarting = true await stopElectron() startElectron() restarting = false }, 250) } function watchPath(target: string) { try { fs.watch(target, () => scheduleRestart()) } catch { return } } function setupWatchers() { watchPath(path.join(root, 'dist')) watchPath(path.join(root, 'desktop')) watchPath(path.join(root, 'scripts/electron-main.js')) } async function main() { await runBuildOnce() const buildWatcher = startBuildWatch() startElectron() setupWatchers() const cleanup = async () => { await stopElectron() buildWatcher.kill('SIGTERM') process.exit(0) } process.on('SIGINT', cleanup) process.on('SIGTERM', cleanup) } main().catch(() => process.exit(1))