diff --git a/packages/react-router-dev/vite/rsc/plugin.ts b/packages/react-router-dev/vite/rsc/plugin.ts index 5e148737b9..2b7a6f0aa0 100644 --- a/packages/react-router-dev/vite/rsc/plugin.ts +++ b/packages/react-router-dev/vite/rsc/plugin.ts @@ -1,6 +1,7 @@ import type * as Vite from "vite"; import { init as initEsModuleLexer } from "es-module-lexer"; import * as babel from "@babel/core"; +import colors from "picocolors"; import { create } from "../virtual-module"; import * as Typegen from "../../typegen"; @@ -28,12 +29,14 @@ export function reactRouterRSCVitePlugin(): Vite.PluginOption[] { let typegenWatcherPromise: Promise | undefined; let viteCommand: Vite.ConfigEnv["command"]; let routeIdByFile: Map | undefined; + let logger: Vite.Logger; return [ { name: "react-router/rsc", async config(viteUserConfig, { command, mode }) { await initEsModuleLexer; + viteCommand = command; const rootDirectory = getRootDirectory(viteUserConfig); const watch = command === "serve"; @@ -58,6 +61,11 @@ export function reactRouterRSCVitePlugin(): Vite.PluginOption[] { ); } + const vite = await import("vite"); + logger = vite.createLogger(viteUserConfig.logLevel, { + prefix: "[react-router]", + }); + const rscEntries = getRscEntries(); return { @@ -147,6 +155,46 @@ export function reactRouterRSCVitePlugin(): Vite.PluginOption[] { }, }; }, + async configureServer(viteDevServer) { + configLoader.onChange( + async ({ + result, + configCodeChanged, + routeConfigCodeChanged, + configChanged, + routeConfigChanged, + }) => { + if (!result.ok) { + invalidateVirtualModules(viteDevServer); + logger.error(result.error, { + clear: true, + timestamp: true, + }); + return; + } + + // prettier-ignore + let message = + configChanged ? "Config changed." : + routeConfigChanged ? "Route config changed." : + configCodeChanged ? "Config saved." : + routeConfigCodeChanged ? " Route config saved." : + "Config saved"; + + logger.info(colors.green(message), { + clear: true, + timestamp: true, + }); + + // Update shared plugin config reference + config = result.value; + + if (configChanged || routeConfigChanged) { + invalidateVirtualModules(viteDevServer); + } + }, + ); + }, async buildEnd() { await configLoader.close(); }, @@ -377,6 +425,17 @@ const virtual = { basename: create("unstable_rsc/basename"), }; +function invalidateVirtualModules(viteDevServer: Vite.ViteDevServer) { + for (const vmod of Object.values(virtual)) { + for (const env of Object.values(viteDevServer.environments)) { + const mod = env.moduleGraph.getModuleById(vmod.resolvedId); + if (mod) { + env.moduleGraph.invalidateModule(mod); + } + } + } +} + function getRootDirectory(viteUserConfig: Vite.UserConfig) { return viteUserConfig.root ?? process.env.REACT_ROUTER_ROOT ?? process.cwd(); }