From 5d186f4614c124a0079fd624bef098fdc76cdfb8 Mon Sep 17 00:00:00 2001 From: Brenley Dueck Date: Sun, 17 Aug 2025 22:49:17 -0500 Subject: [PATCH 1/3] add fswatcher --- .../start/src/config/fs-routes/fs-watcher.ts | 59 +++++++++++++++++++ packages/start/src/config/fs-routes/index.ts | 37 ++++++------ 2 files changed, 76 insertions(+), 20 deletions(-) create mode 100644 packages/start/src/config/fs-routes/fs-watcher.ts diff --git a/packages/start/src/config/fs-routes/fs-watcher.ts b/packages/start/src/config/fs-routes/fs-watcher.ts new file mode 100644 index 000000000..2dfbc6c77 --- /dev/null +++ b/packages/start/src/config/fs-routes/fs-watcher.ts @@ -0,0 +1,59 @@ +import type { FSWatcher, ModuleGraph, ModuleNode, PluginOption, ViteDevServer } from "vite"; +import { moduleId } from "./index.js"; + +interface CompiledRouter { + removeRoute(path: string): void; + addRoute(path: string): void; + updateRoute(path: string): void; + addEventListener(event: "reload", handler: () => void): void; + removeEventListener(event: "reload", handler: () => void): void; +} + +function setupWatcher(watcher: FSWatcher, routes: CompiledRouter): void { + watcher.on("unlink", path => routes.removeRoute(path)); + watcher.on("add", path => routes.addRoute(path)); + watcher.on("change", path => routes.updateRoute(path)); +} + +function createRoutesReloader(server: ViteDevServer, routes: CompiledRouter): () => void { + routes.addEventListener("reload", handleRoutesReload); + return () => routes.removeEventListener("reload", handleRoutesReload); + + function handleRoutesReload(): void { + const { moduleGraph }: { moduleGraph: ModuleGraph } = server; + const mod: ModuleNode | undefined = moduleGraph.getModuleById(moduleId); + if (mod) { + const seen = new Set(); + moduleGraph.invalidateModule(mod, seen); + server.reloadModule(mod); + } + if (!server.config.server.hmr) { + server.ws.send({ type: "full-reload" }); + } + } +} + +export const fileSystemWatcher = (): PluginOption => { + let close: (() => void) | undefined; + + const plugin: PluginOption = { + name: "fs-watcher", + apply: "serve", + async configureServer(server: ViteDevServer) { + const router = (globalThis as any).ROUTERS["server"](server.config); + (globalThis as any).ROUTERS["server"] = router; + + const routerClient = (globalThis as any).ROUTERS["client"](server.config); + (globalThis as any).ROUTERS["client"] = routerClient; + + if (router) { + setupWatcher(server.watcher, router); + close = createRoutesReloader(server, router); + } + }, + closeBundle() { + close?.(); + } + }; + return plugin; +}; diff --git a/packages/start/src/config/fs-routes/index.ts b/packages/start/src/config/fs-routes/index.ts index 3bb743511..505d06f64 100644 --- a/packages/start/src/config/fs-routes/index.ts +++ b/packages/start/src/config/fs-routes/index.ts @@ -1,5 +1,6 @@ import { relative } from "node:path"; import type { PluginOption, ResolvedConfig } from "vite"; +import { fileSystemWatcher } from "./fs-watcher.js"; import { manifest } from "./manifest.js"; import type { BaseFileSystemRouter } from "./router.js"; import { treeShake } from "./tree-shake.js"; @@ -13,11 +14,8 @@ export interface FsRoutesArgs { handlers: Record<"client" | "server", string>; } -export function fsRoutes({ - routers, - handlers -}: FsRoutesArgs): Array { - (globalThis as any).ROUTERS = {}; +export function fsRoutes({ routers, handlers }: FsRoutesArgs): Array { + (globalThis as any).ROUTERS = routers; return [ manifest(handlers), @@ -34,11 +32,7 @@ export function fsRoutes({ if (id !== moduleId) return; const js = jsCode(); - const router = routers[this.environment.name as keyof typeof routers]( - this.environment.config - ); - - (globalThis as any).ROUTERS[this.environment.name] = router; + const router = (globalThis as any).ROUTERS[this.environment.name]; const routes = await router.getRoutes(); @@ -79,18 +73,20 @@ export function fsRoutes({ const code = ` ${js.getImportStatements()} -${this.environment.name === "server" - ? "" - : ` +${ + this.environment.name === "server" + ? "" + : ` function clientManifestImport(id) { return import(/* @vite-ignore */ globalThis.MANIFEST.inputs[id].output.path) }` - } +} export default ${routesCode}`; return code; } }, - treeShake() + treeShake(), + fileSystemWatcher() ]; } @@ -129,8 +125,8 @@ function jsCode() { return Object.keys(id).length > 0 ? `{ ${Object.keys(id) - .map(k => `${k} as ${id[k]}`) - .join(", ")} }` + .map(k => `${k} as ${id[k]}`) + .join(", ")} }` : ""; }; @@ -138,9 +134,10 @@ function jsCode() { return `${[...imports.keys()] .map( i => - `import ${imports.get(i).default - ? `${imports.get(i).default}${Object.keys(imports.get(i)).length > 1 ? ", " : ""}` - : "" + `import ${ + imports.get(i).default + ? `${imports.get(i).default}${Object.keys(imports.get(i)).length > 1 ? ", " : ""}` + : "" } ${getNamedExport(i)} from '${i}';` ) .join("\n")}`; From 70914c4f49b17d66b6176dd1d6cad6b59108f2dd Mon Sep 17 00:00:00 2001 From: Brenley Dueck Date: Mon, 18 Aug 2025 14:19:42 -0500 Subject: [PATCH 2/3] working route reload --- .../start/src/config/fs-routes/fs-watcher.ts | 59 +++++++++++++++---- 1 file changed, 48 insertions(+), 11 deletions(-) diff --git a/packages/start/src/config/fs-routes/fs-watcher.ts b/packages/start/src/config/fs-routes/fs-watcher.ts index 2dfbc6c77..9f9c1b8b5 100644 --- a/packages/start/src/config/fs-routes/fs-watcher.ts +++ b/packages/start/src/config/fs-routes/fs-watcher.ts @@ -1,4 +1,11 @@ -import type { FSWatcher, ModuleGraph, ModuleNode, PluginOption, ViteDevServer } from "vite"; +import type { + EnvironmentModuleNode, + FSWatcher, + ModuleGraph, + ModuleNode, + PluginOption, + ViteDevServer +} from "vite"; import { moduleId } from "./index.js"; interface CompiledRouter { @@ -15,18 +22,39 @@ function setupWatcher(watcher: FSWatcher, routes: CompiledRouter): void { watcher.on("change", path => routes.updateRoute(path)); } -function createRoutesReloader(server: ViteDevServer, routes: CompiledRouter): () => void { +function createRoutesReloader( + server: ViteDevServer, + routes: CompiledRouter, + environment: "client" | "server" +): () => void { routes.addEventListener("reload", handleRoutesReload); return () => routes.removeEventListener("reload", handleRoutesReload); function handleRoutesReload(): void { - const { moduleGraph }: { moduleGraph: ModuleGraph } = server; - const mod: ModuleNode | undefined = moduleGraph.getModuleById(moduleId); - if (mod) { - const seen = new Set(); - moduleGraph.invalidateModule(mod, seen); - server.reloadModule(mod); + if (environment === "server") { + // Handle server environment HMR reload + const serverEnv = server.environments.server; + if (serverEnv && serverEnv.moduleGraph) { + const mod: EnvironmentModuleNode | undefined = + serverEnv.moduleGraph.getModuleById(moduleId); + if (mod) { + const seen = new Set(); + serverEnv.moduleGraph.invalidateModule(mod, seen); + // For server environment, we don't use server.reloadModule as it's for client + // The runner.import will automatically get fresh modules on next request + } + } + } else { + // Handle client environment HMR reload (existing behavior) + const { moduleGraph }: { moduleGraph: ModuleGraph } = server; + const mod: ModuleNode | undefined = moduleGraph.getModuleById(moduleId); + if (mod) { + const seen = new Set(); + moduleGraph.invalidateModule(mod, seen); + server.reloadModule(mod); + } } + if (!server.config.server.hmr) { server.ws.send({ type: "full-reload" }); } @@ -34,7 +62,8 @@ function createRoutesReloader(server: ViteDevServer, routes: CompiledRouter): () } export const fileSystemWatcher = (): PluginOption => { - let close: (() => void) | undefined; + let closeClient: (() => void) | undefined; + let closeServer: (() => void) | undefined; const plugin: PluginOption = { name: "fs-watcher", @@ -46,13 +75,21 @@ export const fileSystemWatcher = (): PluginOption => { const routerClient = (globalThis as any).ROUTERS["client"](server.config); (globalThis as any).ROUTERS["client"] = routerClient; + // Setup client environment watcher (existing behavior) + if (routerClient) { + setupWatcher(server.watcher, routerClient); + closeClient = createRoutesReloader(server, routerClient, "client"); + } + + // Setup server environment watcher (new functionality) if (router) { setupWatcher(server.watcher, router); - close = createRoutesReloader(server, router); + closeServer = createRoutesReloader(server, router, "server"); } }, closeBundle() { - close?.(); + closeClient?.(); + closeServer?.(); } }; return plugin; From f15d6401bc8a6e80396617d72619a8f98f7694b6 Mon Sep 17 00:00:00 2001 From: Brenley Dueck Date: Mon, 18 Aug 2025 14:39:17 -0500 Subject: [PATCH 3/3] some cleanup --- .../start/src/config/fs-routes/fs-watcher.ts | 37 +++++-------------- packages/start/src/config/fs-routes/index.ts | 2 +- 2 files changed, 11 insertions(+), 28 deletions(-) diff --git a/packages/start/src/config/fs-routes/fs-watcher.ts b/packages/start/src/config/fs-routes/fs-watcher.ts index 9f9c1b8b5..1965e9c65 100644 --- a/packages/start/src/config/fs-routes/fs-watcher.ts +++ b/packages/start/src/config/fs-routes/fs-watcher.ts @@ -6,7 +6,7 @@ import type { PluginOption, ViteDevServer } from "vite"; -import { moduleId } from "./index.js"; +import { moduleId, RouterBuilder } from "./index.js"; interface CompiledRouter { removeRoute(path: string): void; @@ -40,12 +40,10 @@ function createRoutesReloader( if (mod) { const seen = new Set(); serverEnv.moduleGraph.invalidateModule(mod, seen); - // For server environment, we don't use server.reloadModule as it's for client - // The runner.import will automatically get fresh modules on next request } } } else { - // Handle client environment HMR reload (existing behavior) + // Handle client environment HMR reload const { moduleGraph }: { moduleGraph: ModuleGraph } = server; const mod: ModuleNode | undefined = moduleGraph.getModuleById(moduleId); if (mod) { @@ -61,35 +59,20 @@ function createRoutesReloader( } } -export const fileSystemWatcher = (): PluginOption => { - let closeClient: (() => void) | undefined; - let closeServer: (() => void) | undefined; - +export const fileSystemWatcher = ( + routers: Record<"client" | "server", RouterBuilder> +): PluginOption => { const plugin: PluginOption = { name: "fs-watcher", apply: "serve", async configureServer(server: ViteDevServer) { - const router = (globalThis as any).ROUTERS["server"](server.config); - (globalThis as any).ROUTERS["server"] = router; - - const routerClient = (globalThis as any).ROUTERS["client"](server.config); - (globalThis as any).ROUTERS["client"] = routerClient; + Object.keys(routers).forEach(environment => { + const router = routers[environment as keyof typeof routers](server.config); + (globalThis as any).ROUTERS[environment] = router; - // Setup client environment watcher (existing behavior) - if (routerClient) { - setupWatcher(server.watcher, routerClient); - closeClient = createRoutesReloader(server, routerClient, "client"); - } - - // Setup server environment watcher (new functionality) - if (router) { setupWatcher(server.watcher, router); - closeServer = createRoutesReloader(server, router, "server"); - } - }, - closeBundle() { - closeClient?.(); - closeServer?.(); + createRoutesReloader(server, router, environment as keyof typeof routers); + }); } }; return plugin; diff --git a/packages/start/src/config/fs-routes/index.ts b/packages/start/src/config/fs-routes/index.ts index 505d06f64..94a2da787 100644 --- a/packages/start/src/config/fs-routes/index.ts +++ b/packages/start/src/config/fs-routes/index.ts @@ -86,7 +86,7 @@ export default ${routesCode}`; } }, treeShake(), - fileSystemWatcher() + fileSystemWatcher(routers) ]; }