process.env.NODE_ENV = "development"; import * as Vite from "vite"; import ChildProcess, { type ChildProcessWithoutNullStreams } from "child_process"; import Path from "path"; import Chalk from "chalk"; import Chokidar from "chokidar"; import Electron from "electron"; import compileTs from "./private/tsc.ts"; // ^ Extension needed because no TSConfig in the root import FileSystem from "fs"; import { EOL } from "os"; import { fileURLToPath } from "url"; const __filename = fileURLToPath(import.meta.url); const __dirname = Path.dirname(__filename); let viteServer: Vite.ViteDevServer | null = null; let electronProcess: ChildProcessWithoutNullStreams | null = null; let electronProcessLocker = false; let rendererPort = 0; async function startRenderer() { viteServer = await Vite.createServer({ configFile: Path.join(__dirname, "..", "vite.config.ts"), mode: "development", }); return viteServer.listen(); } async function startElectron() { if (electronProcess) { // single instance lock return; } try { await compileTs(Path.join(__dirname, "..", "src", "main")); } catch { console.log(Chalk.redBright("Could not start Electron because of the above typescript error(s).")); electronProcessLocker = false; return; } const args = [Path.join(__dirname, "..", "build", "main", "main.js"), String(rendererPort)]; electronProcess = ChildProcess.spawn(String(Electron), args); electronProcessLocker = false; electronProcess!.stdout.on("data", data => { if (data == EOL) { return; } process.stdout.write(Chalk.blueBright(`[electron] `) + Chalk.white(data.toString())); }); electronProcess!.stderr.on("data", data => process.stderr.write(Chalk.blueBright(`[electron] `) + Chalk.white(data.toString())), ); electronProcess!.on("exit", () => stop()); } function restartElectron() { if (electronProcess) { electronProcess.removeAllListeners("exit"); electronProcess.kill(); electronProcess = null; } if (!electronProcessLocker) { electronProcessLocker = true; startElectron(); } } function copyStaticFiles() { copy("static"); } /* The working dir of Electron is build/main instead of src/main because of TS. tsc does not copy static files, so copy them over manually for dev server. */ function copy(path) { FileSystem.cpSync( Path.join(__dirname, "..", "src", "main", path), Path.join(__dirname, "..", "build", "main", path), { recursive: true }, ); } function stop() { viteServer!.close(); process.exit(); } async function start() { console.log(`${Chalk.greenBright("=======================================")}`); console.log(`${Chalk.greenBright("Starting Electron + Vite Dev Server...")}`); console.log(`${Chalk.greenBright("=======================================")}`); const devServer = await startRenderer(); rendererPort = devServer.config.server.port; copyStaticFiles(); startElectron(); const path = Path.join(__dirname, "..", "src", "main"); Chokidar.watch(path, { cwd: path, }).on("change", path => { console.log(Chalk.blueBright(`[electron] `) + `Change in ${path}. reloading... 🚀`); if (path.startsWith(Path.join("static", "/"))) { copy(path); } restartElectron(); }); } start();