#!/usr/bin/env bun /** * Development watch script for Bun * * Watches src/ for changes, rebuilds on change, and restarts the server. * This is necessary because we must compile TypeScript first due to circular * TypeORM entity dependencies that Bun's TS loader cannot handle directly. */ import { watch } from "fs"; import { spawn } from "child_process"; import consola from "consola"; let serverProcess: ReturnType | null = null; let isRebuilding = false; let pendingRestart = false; let debounceTimer: NodeJS.Timeout | null = null; let watcherReady = false; function killServer() { return new Promise((resolve) => { if (serverProcess) { consola.info("Stopping server..."); serverProcess.kill(); serverProcess = null; // Wait for port to be fully released (longer on Windows) setTimeout(resolve, 2000); } else { resolve(); } }); } function startServer() { consola.info("Starting server..."); serverProcess = spawn("bun", ["dist/app.js"], { stdio: "inherit", shell: true, }); serverProcess.on("error", (err) => { consola.error("Server process error:", err); }); serverProcess.on("exit", (code) => { if (code !== null && code !== 0 && code !== 143) { consola.error(`Server exited with code ${code}`); } }); // Enable watcher after initial server start if (!watcherReady) { setTimeout(() => { watcherReady = true; consola.success("👀 Watching for file changes..."); }, 1000); } } async function rebuild() { if (isRebuilding) { pendingRestart = true; return; } isRebuilding = true; pendingRestart = false; consola.info("Rebuilding..."); await killServer(); const buildProcess = spawn("bun", ["run", "build"], { stdio: "inherit", shell: true, }); buildProcess.on("exit", (code) => { isRebuilding = false; if (code === 0) { consola.success("Build complete!"); startServer(); if (pendingRestart) { consola.info("Change detected during build, rebuilding again..."); setTimeout(() => rebuild(), 100); } } else { consola.error(`Build failed with code ${code}`); } }); } // Initial build and start consola.info("🔄 Development mode - watching for changes..."); rebuild(); // Watch src/ for changes (including subdirectories) const watcher = watch( "./src", { recursive: true }, (eventType, filename) => { if (!watcherReady) return; // Ignore changes during initial build if (filename && filename.endsWith(".ts")) { // Ignore test files and declaration files if (filename.endsWith(".spec.ts") || filename.endsWith(".d.ts")) { return; } // Debounce: wait 500ms for multiple rapid changes if (debounceTimer) { clearTimeout(debounceTimer); } debounceTimer = setTimeout(() => { consola.info(`File changed: ${filename}`); rebuild(); }, 500); } } ); // Cleanup on exit process.on("SIGINT", () => { consola.info("\nShutting down..."); if (debounceTimer) clearTimeout(debounceTimer); killServer(); watcher.close(); process.exit(0); }); process.on("SIGTERM", () => { if (debounceTimer) clearTimeout(debounceTimer); killServer(); watcher.close(); process.exit(0); });