HMR and watchers
Chokidar, invalidation, and what reloads when a file changes.
Chokidar
BionicJS relies on Vite's internal file watcher (Chokidar) to monitor the app/ directory for structural changes. The watcher is not custom - it is the same server.watcher that Vite's own plugins and HMR pipeline use.
1if (server.watcher) {
2 server.watcher.on("add", handleFileChange);
3 server.watcher.on("unlink", handleFileChange);
4}
BionicJS listens to add and unlink events - the two that matter for route discovery. Creating a page.tsx fires add; deleting one fires unlink. Editing an existing file does not fire either, and correctly triggers ordinary HMR instead of a route-tree rebuild.
| Path | Change | Handled by |
|---|---|---|
app/**/page.tsx | added or removed | Route-tree invalidation |
app/**/layout.tsx | added or removed | Route-tree invalidation |
app/** | content edit | Ordinary granular HMR |
server/**/*.ts | content edit | Nitro server reload |
ai/**/*.py | content edit | Python process restart |
Invalidation
When a new page.tsx or layout.tsx is added or deleted, the Vite plugin intercepts the event and invalidates the virtual:bionicjs-routes module in Vite's module graph. This forces Vite to request the file again, triggering the BionicJS generator to re-run and push the new route tree to the browser:
1const handleFileChange = (filepath: string) => {
2 if (
3 normalized.includes("/app/") &&
4 (normalized.endsWith("/page.tsx") || normalized.endsWith("/layout.tsx"))
5 ) {
6 const mod = server.moduleGraph.getModuleById(resolvedVirtualModuleId);
7 if (mod) {
8 server.moduleGraph.invalidateModule(mod);
9 server.ws?.send({ type: "full-reload" });
10 }
11 }
12};
The full lifecycle for creating a route:
- Create
app/contact/page.tsx. - Vite's watcher fires an
addevent. - The plugin detects the
page.tsxsuffix underapp/. - The virtual module is invalidated and a
full-reloadis broadcast. - The browser re-fetches
virtual:bionicjs-routes, regenerating the route tree. - The new
/contactroute is live.
This creates a seamless developer experience where routes appear in the browser the second the file is created - and disappear just as quickly, without restarting anything.
full-reload, not a fine-grained HMR update, because the entire route tree is regenerated - the browser needs a fresh copy of the module. Page content edits still get Vite's normal granular HMR.11 / 25