fix(fabro-web): restore local watch rebuilds

Replace the unsupported Bun.watch call in the SPA build script with
node:fs.watch so `bun run dev` keeps running in local development.
Add a regression test that verifies watch mode stays alive until
interrupted.
This commit is contained in:
Bryan Helmkamp 2026-04-08 15:27:27 -04:00
parent 5d5a84d229
commit 73be1daca7
No known key found for this signature in database
2 changed files with 57 additions and 6 deletions

View file

@ -0,0 +1,32 @@
import { test, expect } from "bun:test";
const root = Bun.fileURLToPath(new URL("..", import.meta.url));
test("watch mode keeps running until interrupted", async () => {
const process = Bun.spawn([
"bun",
"run",
"scripts/build.ts",
"--watch",
], {
cwd: root,
stdout: "pipe",
stderr: "pipe",
});
const result = await Promise.race([
process.exited.then((code) => ({ kind: "exited" as const, code })),
Bun.sleep(1000).then(() => ({ kind: "running" as const })),
]);
if (result.kind === "exited") {
const stderr = await new Response(process.stderr).text();
const stdout = await new Response(process.stdout).text();
throw new Error(
`watch process exited unexpectedly with code ${result.code}\nstdout:\n${stdout}\nstderr:\n${stderr}`,
);
}
process.kill("SIGINT");
expect(await process.exited).toBe(0);
});

View file

@ -1,3 +1,4 @@
import { watch as fsWatch } from "node:fs";
import { cp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
import { join, relative } from "node:path";
@ -79,19 +80,37 @@ async function main() {
}
await buildOnce();
const watcher = Bun.watch({
paths: [join(rootPath, "app"), publicDir, templatePath],
async onChange() {
let building = false;
let rebuildQueued = false;
async function rebuild() {
if (building) {
rebuildQueued = true;
return;
}
building = true;
do {
rebuildQueued = false;
try {
await buildOnce();
} catch (error) {
console.error(error);
}
},
});
} while (rebuildQueued);
building = false;
}
const watchers = [
fsWatch(join(rootPath, "app"), { recursive: true }, rebuild),
fsWatch(publicDir, { recursive: true }, rebuild),
fsWatch(templatePath, rebuild),
];
process.on("SIGINT", () => {
watcher.stop();
for (const watcher of watchers) {
watcher.close();
}
process.exit(0);
});
}