From ab0e85b04840c3a3667f34a03d6691f087826a3c Mon Sep 17 00:00:00 2001 From: Mark Dalgleish Date: Thu, 1 May 2025 13:48:25 +1000 Subject: [PATCH] Skip unused `routes.ts` eval before Vite build --- .changeset/ten-pears-wash.md | 5 + packages/react-router-dev/config/config.ts | 123 ++++++++++++--------- packages/react-router-dev/vite/build.ts | 3 + 3 files changed, 77 insertions(+), 54 deletions(-) create mode 100644 .changeset/ten-pears-wash.md diff --git a/.changeset/ten-pears-wash.md b/.changeset/ten-pears-wash.md new file mode 100644 index 0000000000..d07d3284f8 --- /dev/null +++ b/.changeset/ten-pears-wash.md @@ -0,0 +1,5 @@ +--- +"@react-router/dev": patch +--- + +Skip unnecessary `routes.ts` evaluation before Vite build is started diff --git a/packages/react-router-dev/config/config.ts b/packages/react-router-dev/config/config.ts index 9815797627..5c130eb6cf 100644 --- a/packages/react-router-dev/config/config.ts +++ b/packages/react-router-dev/config/config.ts @@ -350,10 +350,12 @@ async function resolveConfig({ root, viteNodeContext, reactRouterConfigFile, + skipRoutes, }: { root: string; viteNodeContext: ViteNode.Context; reactRouterConfigFile?: string; + skipRoutes?: boolean; }): Promise> { let reactRouterUserConfig: ReactRouterConfig = {}; @@ -499,61 +501,67 @@ async function resolveConfig({ ); } - let routes: RouteManifest = { - root: { path: "", id: "root", file: rootRouteFile }, - }; + let routes: RouteManifest = {}; - let routeConfigFile = findEntry(appDirectory, "routes"); + if (!skipRoutes) { + routes = { + root: { path: "", id: "root", file: rootRouteFile }, + }; - try { - if (!routeConfigFile) { - let routeConfigDisplayPath = Path.relative( - root, - Path.join(appDirectory, "routes.ts") - ); - return err(`Route config file not found at "${routeConfigDisplayPath}".`); - } + let routeConfigFile = findEntry(appDirectory, "routes"); - setAppDirectory(appDirectory); - let routeConfigExport = ( - await viteNodeContext.runner.executeFile( - Path.join(appDirectory, routeConfigFile) - ) - ).default; - let routeConfig = await routeConfigExport; - - let result = validateRouteConfig({ - routeConfigFile, - routeConfig, - }); + try { + if (!routeConfigFile) { + let routeConfigDisplayPath = Path.relative( + root, + Path.join(appDirectory, "routes.ts") + ); + return err( + `Route config file not found at "${routeConfigDisplayPath}".` + ); + } - if (!result.valid) { - return err(result.message); - } + setAppDirectory(appDirectory); + let routeConfigExport = ( + await viteNodeContext.runner.executeFile( + Path.join(appDirectory, routeConfigFile) + ) + ).default; + let routeConfig = await routeConfigExport; + + let result = validateRouteConfig({ + routeConfigFile, + routeConfig, + }); - routes = { - ...routes, - ...configRoutesToRouteManifest(appDirectory, routeConfig), - }; - } catch (error: any) { - return err( - [ - colors.red(`Route config in "${routeConfigFile}" is invalid.`), - "", - error.loc?.file && error.loc?.column && error.frame - ? [ - Path.relative(appDirectory, error.loc.file) + - ":" + - error.loc.line + - ":" + - error.loc.column, - error.frame.trim?.(), - ] - : error.stack, - ] - .flat() - .join("\n") - ); + if (!result.valid) { + return err(result.message); + } + + routes = { + ...routes, + ...configRoutesToRouteManifest(appDirectory, routeConfig), + }; + } catch (error: any) { + return err( + [ + colors.red(`Route config in "${routeConfigFile}" is invalid.`), + "", + error.loc?.file && error.loc?.column && error.frame + ? [ + Path.relative(appDirectory, error.loc.file) + + ":" + + error.loc.line + + ":" + + error.loc.column, + error.frame.trim?.(), + ] + : error.stack, + ] + .flat() + .join("\n") + ); + } } let future: FutureConfig = { @@ -613,10 +621,12 @@ export async function createConfigLoader({ rootDirectory: root, watch, mode, + skipRoutes, }: { watch: boolean; rootDirectory?: string; mode: string; + skipRoutes?: boolean; }): Promise { root = Path.normalize(root ?? process.env.REACT_ROUTER_ROOT ?? process.cwd()); @@ -641,7 +651,7 @@ export async function createConfigLoader({ updateReactRouterConfigFile(); let getConfig = () => - resolveConfig({ root, viteNodeContext, reactRouterConfigFile }); + resolveConfig({ root, viteNodeContext, reactRouterConfigFile, skipRoutes }); let appDirectory: string; @@ -740,9 +750,11 @@ export async function createConfigLoader({ filepath )); - let routeConfigFile = findEntry(appDirectory, "routes", { - absolute: true, - }); + let routeConfigFile = !skipRoutes + ? findEntry(appDirectory, "routes", { + absolute: true, + }) + : undefined; let routeConfigCodeChanged = routeConfigFile !== undefined && isEntryFileDependency( @@ -793,13 +805,16 @@ export async function createConfigLoader({ export async function loadConfig({ rootDirectory, mode, + skipRoutes, }: { rootDirectory: string; mode: string; + skipRoutes?: boolean; }) { let configLoader = await createConfigLoader({ rootDirectory, mode, + skipRoutes, watch: false, }); let config = await configLoader.getConfig(); diff --git a/packages/react-router-dev/vite/build.ts b/packages/react-router-dev/vite/build.ts index a3ef0f060d..52ba04ece1 100644 --- a/packages/react-router-dev/vite/build.ts +++ b/packages/react-router-dev/vite/build.ts @@ -38,6 +38,9 @@ export async function build(root: string, viteBuildOptions: ViteBuildOptions) { let configResult = await loadConfig({ rootDirectory: root, mode: viteBuildOptions.mode ?? "production", + // In this scope we only need future flags, so we can skip evaluating + // routes.ts until we're within the Vite build context + skipRoutes: true, }); if (!configResult.ok) {