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..1965e9c65 --- /dev/null +++ b/packages/start/src/config/fs-routes/fs-watcher.ts @@ -0,0 +1,79 @@ +import type { + EnvironmentModuleNode, + FSWatcher, + ModuleGraph, + ModuleNode, + PluginOption, + ViteDevServer +} from "vite"; +import { moduleId, RouterBuilder } 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, + environment: "client" | "server" +): () => void { + routes.addEventListener("reload", handleRoutesReload); + return () => routes.removeEventListener("reload", handleRoutesReload); + + function handleRoutesReload(): void { + 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); + } + } + } else { + // Handle client environment HMR reload + 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 = ( + routers: Record<"client" | "server", RouterBuilder> +): PluginOption => { + const plugin: PluginOption = { + name: "fs-watcher", + apply: "serve", + async configureServer(server: ViteDevServer) { + Object.keys(routers).forEach(environment => { + const router = routers[environment as keyof typeof routers](server.config); + (globalThis as any).ROUTERS[environment] = router; + + setupWatcher(server.watcher, router); + 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 9f721a99e..20ff16b46 100644 --- a/packages/start/src/config/fs-routes/index.ts +++ b/packages/start/src/config/fs-routes/index.ts @@ -1,5 +1,7 @@ import { relative } from "node:path"; import type { PluginOption, ResolvedConfig } from "vite"; + +import { fileSystemWatcher } from "./fs-watcher.js"; import type { BaseFileSystemRouter } from "./router.js"; import { treeShake } from "./tree-shake.js"; @@ -14,11 +16,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 [ { @@ -34,11 +33,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(); @@ -91,7 +86,8 @@ export default ${routesCode}`; return code; } }, - treeShake() + treeShake(), + fileSystemWatcher(routers) ]; }