139 lines
3.3 KiB
TypeScript
139 lines
3.3 KiB
TypeScript
#!/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<typeof spawn> | null = null;
|
|
let isRebuilding = false;
|
|
let pendingRestart = false;
|
|
let debounceTimer: NodeJS.Timeout | null = null;
|
|
let watcherReady = false;
|
|
|
|
function killServer() {
|
|
return new Promise<void>((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);
|
|
});
|