From feb9173734c4f053f1e208063146b79ed5ca8a3e Mon Sep 17 00:00:00 2001 From: Mark Dalgleish Date: Fri, 5 Sep 2025 11:40:52 +1000 Subject: [PATCH] Validate unsupported config in RSC Framework Mode --- packages/react-router-dev/config/config.ts | 21 +++++++++++++++- packages/react-router-dev/vite/rsc/plugin.ts | 26 +++++++++++++++++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/packages/react-router-dev/config/config.ts b/packages/react-router-dev/config/config.ts index 951714b7a4..68ef25cb12 100644 --- a/packages/react-router-dev/config/config.ts +++ b/packages/react-router-dev/config/config.ts @@ -83,6 +83,8 @@ type ServerBundlesBuildManifest = BaseBuildManifest & { type ServerModuleFormat = "esm" | "cjs"; +type ValidateConfigFunction = (config: ReactRouterConfig) => string | void; + interface FutureConfig { /** * Enable route middleware @@ -356,11 +358,13 @@ async function resolveConfig({ viteNodeContext, reactRouterConfigFile, skipRoutes, + validateConfig, }: { root: string; viteNodeContext: ViteNode.Context; reactRouterConfigFile?: string; skipRoutes?: boolean; + validateConfig?: ValidateConfigFunction; }): Promise> { let reactRouterUserConfig: ReactRouterConfig = {}; @@ -383,6 +387,13 @@ async function resolveConfig({ } reactRouterUserConfig = configModule.default; + + if (validateConfig) { + const error = validateConfig(reactRouterUserConfig); + if (error) { + return err(error); + } + } } catch (error) { return err(`Error loading ${reactRouterConfigFile}: ${error}`); } @@ -631,11 +642,13 @@ export async function createConfigLoader({ watch, mode, skipRoutes, + validateConfig, }: { watch: boolean; rootDirectory?: string; mode: string; skipRoutes?: boolean; + validateConfig?: ValidateConfigFunction; }): Promise { root = Path.normalize(root ?? process.env.REACT_ROUTER_ROOT ?? process.cwd()); @@ -660,7 +673,13 @@ export async function createConfigLoader({ updateReactRouterConfigFile(); let getConfig = () => - resolveConfig({ root, viteNodeContext, reactRouterConfigFile, skipRoutes }); + resolveConfig({ + root, + viteNodeContext, + reactRouterConfigFile, + skipRoutes, + validateConfig, + }); let appDirectory: string; diff --git a/packages/react-router-dev/vite/rsc/plugin.ts b/packages/react-router-dev/vite/rsc/plugin.ts index 9da90a9415..da9cb27168 100644 --- a/packages/react-router-dev/vite/rsc/plugin.ts +++ b/packages/react-router-dev/vite/rsc/plugin.ts @@ -43,7 +43,31 @@ export function reactRouterRSCVitePlugin(): Vite.PluginOption[] { const rootDirectory = getRootDirectory(viteUserConfig); const watch = command === "serve"; - configLoader = await createConfigLoader({ rootDirectory, mode, watch }); + configLoader = await createConfigLoader({ + rootDirectory, + mode, + watch, + validateConfig: (userConfig) => { + let errors: string[] = []; + if (userConfig.buildEnd) errors.push("buildEnd"); + if (userConfig.prerender) errors.push("prerender"); + if (userConfig.presets?.length) errors.push("presets"); + if (userConfig.routeDiscovery) errors.push("routeDiscovery"); + if (userConfig.serverBundles) errors.push("serverBundles"); + if (userConfig.ssr === false) errors.push("ssr: false"); + if (userConfig.future?.unstable_splitRouteModules) + errors.push("future.unstable_splitRouteModules"); + if (userConfig.future?.unstable_viteEnvironmentApi === false) + errors.push("future.unstable_viteEnvironmentApi: false"); + if (userConfig.future?.v8_middleware === false) + errors.push("future.v8_middleware: false"); + if (userConfig.future?.unstable_subResourceIntegrity) + errors.push("future.unstable_subResourceIntegrity"); + if (errors.length) { + return `RSC Framework Mode does not currently support the following React Router config:\n${errors.map((x) => ` - ${x}`).join("\n")}\n`; + } + }, + }); const configResult = await configLoader.getConfig(); if (!configResult.ok) throw new Error(configResult.error);