mirror of
https://github.com/nestriness/nestri.git
synced 2026-11-03 22:22:39 +02:00
fix(auth): reload the preview on every save, not just the first
bun --watch watches each imported file, and many editors save by writing a new file and renaming it over the old one. The watch does not follow the rename, so the first such save reloaded the preview and every later one was silently ignored. The preview now runs under a small supervisor that watches the source directories and restarts the server, which sees a save however it was made. It also stops the server when it is stopped itself, so the port is not left held.
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
"scripts": {
|
||||
"dev": "wrangler dev",
|
||||
"serve": "bun run src/server.ts",
|
||||
"preview": "bun --watch script/preview.ts",
|
||||
"preview": "bun script/preview-watch.ts",
|
||||
"deploy": "wrangler deploy"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Runs `preview.ts`, and runs it again whenever the sign-in UI's source changes.
|
||||
*
|
||||
* Not `bun --watch`, which watches each imported file. Many editors save by
|
||||
* writing a new file and renaming it over the old one, and a watch on a file
|
||||
* does not follow the rename: the first such save reloads, and every one after
|
||||
* it is silently ignored. A watch on the directory sees every save, however it
|
||||
* was made.
|
||||
*/
|
||||
|
||||
import { watch } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
|
||||
const ROOT = join(import.meta.dir, '..', '..', '..');
|
||||
const DIRS = [join(ROOT, 'packages/auth/src'), join(ROOT, 'apps/auth/src'), import.meta.dir];
|
||||
const SERVER = join(import.meta.dir, 'preview.ts');
|
||||
|
||||
let child = start();
|
||||
let timer: ReturnType<typeof setTimeout> | undefined;
|
||||
|
||||
function start() {
|
||||
return Bun.spawn(['bun', SERVER], { stdio: ['inherit', 'inherit', 'inherit'] });
|
||||
}
|
||||
|
||||
async function restart(file: string) {
|
||||
child.kill();
|
||||
// The port is free only once the old server has exited.
|
||||
await child.exited;
|
||||
console.log(`changed: ${file}`);
|
||||
child = start();
|
||||
}
|
||||
|
||||
for (const dir of DIRS) {
|
||||
watch(dir, { recursive: true }, (_event, file) => {
|
||||
if (!file || !/\.(ts|tsx)$/.test(file)) return;
|
||||
// One save can be several events: a write, then a rename.
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(() => restart(file), 60);
|
||||
});
|
||||
}
|
||||
|
||||
// Ctrl-C reaches the whole process group, but a plain `kill` reaches only
|
||||
// this process, and would leave the server holding the port.
|
||||
for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP'] as const) {
|
||||
process.on(signal, () => {
|
||||
child.kill();
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
@@ -8,9 +8,9 @@
|
||||
* screens from `ui/issuer.ts` — drawn by the real renderer with the real
|
||||
* theme. What differs from production is only how the page was reached.
|
||||
*
|
||||
* `bun --watch` restarts this process when anything it imports changes, which
|
||||
* includes `css.ts`, `render.tsx` and `base.tsx`. Each page polls for the
|
||||
* restart and reloads itself, so saving a file is the whole loop.
|
||||
* `preview-watch.ts` restarts this process when the UI's source changes —
|
||||
* the renderer, the stylesheet, the copy, the issuer's screens. Each page polls
|
||||
* for the restart and reloads itself, so saving a file is the whole loop.
|
||||
*
|
||||
* A state that is missing here is a state nobody will look at before it
|
||||
* ships. Adding a screen to the flow means adding it to `STATES`.
|
||||
|
||||
Reference in New Issue
Block a user