@@ -456,19 +456,37 @@ export const reactRouterVitePlugin: ReactRouterVitePlugin = () => {
456456 : // Otherwise, all routes are imported as usual
457457 ctx . reactRouterConfig . routes ;
458458
459+ let isSpaMode =
460+ ! ctx . reactRouterConfig . ssr && ctx . reactRouterConfig . prerender == null ;
461+
462+ let routeIdsToImport = new Set ( Object . keys ( routes ) ) ;
463+ if ( isSpaMode ) {
464+ // In SPA mode, we only pre-render the top-level index route; for all
465+ // other routes we stub out their imports, as they (and their deps) may
466+ // not be compatible with server-side rendering. This also helps keep
467+ // the build fast
468+ routeIdsToImport = getRootRouteIds ( routes ) ;
469+ }
470+
459471 return `
460472 import * as entryServer from ${ JSON . stringify (
461473 resolveFileUrl ( ctx , ctx . entryServerFilePath )
462474 ) } ;
463475 ${ Object . keys ( routes )
464476 . map ( ( key , index ) => {
465477 let route = routes [ key ] ! ;
466- return `import * as route${ index } from ${ JSON . stringify (
467- resolveFileUrl (
468- ctx ,
469- resolveRelativeRouteFilePath ( route , ctx . reactRouterConfig )
470- )
471- ) } ;`;
478+ if ( routeIdsToImport . has ( key ) ) {
479+ return `import * as route${ index } from ${ JSON . stringify (
480+ resolveFileUrl (
481+ ctx ,
482+ resolveRelativeRouteFilePath ( route , ctx . reactRouterConfig )
483+ )
484+ ) } ;`;
485+ } else {
486+ // we're not importing the route since we won't be rendering
487+ // it via SSR; just stub it out
488+ return `const route${ index } = { default: () => null };` ;
489+ }
472490 } )
473491 . join ( "\n" ) }
474492 export { default as assets } from ${ JSON . stringify (
@@ -482,9 +500,7 @@ export const reactRouterVitePlugin: ReactRouterVitePlugin = () => {
482500 ) } ;
483501 export const basename = ${ JSON . stringify ( ctx . reactRouterConfig . basename ) } ;
484502 export const future = ${ JSON . stringify ( ctx . reactRouterConfig . future ) } ;
485- export const isSpaMode = ${
486- ! ctx . reactRouterConfig . ssr && ctx . reactRouterConfig . prerender == null
487- } ;
503+ export const isSpaMode = ${ isSpaMode } ;
488504 export const publicPath = ${ JSON . stringify ( ctx . publicPath ) } ;
489505 export const entry = { module: entryServer };
490506 export const routes = {
@@ -1198,7 +1214,7 @@ export const reactRouterVitePlugin: ReactRouterVitePlugin = () => {
11981214 ctx . reactRouterConfig . prerender !== false
11991215 ) {
12001216 // If we have prerender routes, that takes precedence over SPA mode
1201- // which is ssr:false and only the rot route being rendered
1217+ // which is ssr:false and only the root route being rendered
12021218 await handlePrerender (
12031219 viteConfig ,
12041220 ctx . reactRouterConfig ,
@@ -2087,13 +2103,20 @@ function validatePrerenderedHtml(html: string, prefix: string) {
20872103 }
20882104}
20892105
2090- type ServerRoute = ServerBuild [ "routes" ] [ string ] & {
2091- children : ServerRoute [ ] ;
2106+ // Just the minimum necessary to unflatten manifests generically (e.g. RouteManifest vs. ServerBuild["routes"])
2107+ type ServerRoute = {
2108+ id : string ;
2109+ parentId ?: string ;
2110+ path ?: string ;
2111+ caseSensitive ?: boolean ;
2112+ index ?: boolean ;
20922113} ;
20932114
2094- // Note: Duplicated from react-router/lib/server-runtime
2095- function groupRoutesByParentId ( manifest : ServerBuild [ "routes" ] ) {
2096- let routes : Record < string , Omit < ServerRoute , "children" > [ ] > = { } ;
2115+ // Note: Adapted from react-router/lib/server-runtime
2116+ function groupRoutesByParentId < T extends ServerRoute > (
2117+ manifest : Record < string , T | undefined >
2118+ ) {
2119+ let routes : Record < string , T [ ] > = { } ;
20972120
20982121 Object . values ( manifest ) . forEach ( ( route ) => {
20992122 if ( route ) {
@@ -2108,36 +2131,52 @@ function groupRoutesByParentId(manifest: ServerBuild["routes"]) {
21082131 return routes ;
21092132}
21102133
2111- // Note: Duplicated from react-router/lib/server-runtime
2112- function createPrerenderRoutes (
2113- manifest : ServerBuild [ "routes" ] ,
2134+ // Note: Adapted from react-router/lib/server-runtime
2135+ function createRoutes < T extends ServerRoute , T2 extends Partial < T > = T > (
2136+ manifest : Record < string , T | undefined > ,
2137+ mapRoute : ( route : T ) => T2 = ( route ) => route as unknown as T2 ,
21142138 parentId : string = "" ,
2115- routesByParentId : Record <
2116- string ,
2117- Omit < ServerRoute , "children" > [ ]
2118- > = groupRoutesByParentId ( manifest )
2119- ) : DataRouteObject [ ] {
2120- return ( routesByParentId [ parentId ] || [ ] ) . map ( ( route ) => {
2121- let commonRoute = {
2122- // Always include root due to default boundaries
2123- hasErrorBoundary :
2124- route . id === "root" || route . module . ErrorBoundary != null ,
2125- id : route . id ,
2126- path : route . path ,
2127- loader : route . module . loader ? ( ) => null : undefined ,
2128- action : undefined ,
2129- handle : route . module . handle ,
2139+ routesByParentId = groupRoutesByParentId ( manifest ) ,
2140+ ) {
2141+ return ( routesByParentId [ parentId ] || [ ] ) . map ( ( route ) : T2 => {
2142+ return {
2143+ ...( route . index
2144+ ? { index : true }
2145+ : {
2146+ caseSensitive : route . caseSensitive ,
2147+ children : createRoutes (
2148+ manifest ,
2149+ mapRoute ,
2150+ route . id ,
2151+ routesByParentId ,
2152+ ) ,
2153+ } ) ,
2154+ ...mapRoute ( route ) ,
21302155 } ;
2131-
2132- return route . index
2133- ? {
2134- index : true ,
2135- ...commonRoute ,
2136- }
2137- : {
2138- caseSensitive : route . caseSensitive ,
2139- children : createPrerenderRoutes ( manifest , route . id , routesByParentId ) ,
2140- ...commonRoute ,
2141- } ;
21422156 } ) ;
21432157}
2158+
2159+ function createPrerenderRoutes (
2160+ manifest : ServerBuild [ "routes" ]
2161+ ) : DataRouteObject [ ] {
2162+ return createRoutes ( manifest , ( route ) => ( {
2163+ // Always include root due to default boundaries
2164+ hasErrorBoundary : route . id === "root" || route . module . ErrorBoundary != null ,
2165+ id : route . id ,
2166+ path : route . path ,
2167+ loader : route . module . loader ? ( ) => null : undefined ,
2168+ action : undefined ,
2169+ handle : route . module . handle ,
2170+ } ) ) ;
2171+ }
2172+
2173+ /**
2174+ * Return the route ids associated with the top-level index route
2175+ *
2176+ * i.e. "root", the top-level index route's id, and (if applicable) the ids of
2177+ * any top-level layout/path-less routes in between
2178+ */
2179+ function getRootRouteIds ( manifest : RouteManifest ) : Set < string > {
2180+ const matches = matchRoutes ( createRoutes ( manifest ) , "/" ) ;
2181+ return new Set ( matches ?. filter ( Boolean ) . map ( ( m ) => m . route . id ) || [ ] ) ;
2182+ }
0 commit comments