@@ -18,9 +18,8 @@ import {
1818import { createRequestInit } from "./data" ;
1919import type { AssetsManifest , EntryContext } from "./entry" ;
2020import { escapeHtml } from "./markup" ;
21- import type { RouteModule , RouteModules } from "./routeModules" ;
21+ import type { RouteModules } from "./routeModules" ;
2222import invariant from "./invariant" ;
23- import type { EntryRoute } from "./routes" ;
2423
2524export const SingleFetchRedirectSymbol = Symbol ( "SingleFetchRedirect" ) ;
2625
@@ -316,22 +315,16 @@ async function singleFetchLoaderNavigationStrategy(
316315 matches : DataStrategyFunctionArgs [ "matches" ] ,
317316 basename : string | undefined
318317) {
319- // Track which routes need a server load - in case we need to tack on a
320- // `_routes` param
318+ // Track which routes need a server load for use in a `_routes` param
321319 let routesParams = new Set < string > ( ) ;
322320
323- // We only add `_routes` when one or more routes opts out of a load via
324- // `shouldRevalidate` or `clientLoader`
321+ // Only add `_routes` when at least 1 route opts out via `shouldRevalidate`/`clientLoader`
325322 let foundOptOutRoute = false ;
326323
327- // Deferreds for each route so we can be sure they've all loaded via
328- // `match.resolve()`, and a singular promise that can tell us all routes
329- // have been resolved
324+ // Deferreds per-route so we can be sure they've all loaded via `match.resolve()`
330325 let routeDfds = matches . map ( ( ) => createDeferred < void > ( ) ) ;
331- let routesLoadedPromise = Promise . all ( routeDfds . map ( ( d ) => d . promise ) ) ;
332326
333- // Deferred that we'll use for the call to the server that each match can
334- // await and parse out it's specific result
327+ // Deferred we'll use for the singleular call to the server
335328 let singleFetchDfd = createDeferred < SingleFetchResults > ( ) ;
336329
337330 // Base URL and RequestInit for calls to the server
@@ -347,10 +340,8 @@ async function singleFetchLoaderNavigationStrategy(
347340 routeDfds [ i ] . resolve ( ) ;
348341
349342 let manifestRoute = manifest . routes [ m . route . id ] ;
343+ invariant ( manifestRoute , "No manifest route found for dataStrategy" ) ;
350344
351- // Note: If this logic changes for routes that should not participate
352- // in Single Fetch, make sure you update getLowestLoadingIndex above
353- // as well
354345 if ( ! m . shouldLoad ) {
355346 // If we're not yet initialized and this is the initial load, respect
356347 // `shouldLoad` because we're only dealing with `clientLoader.hydrate`
@@ -364,7 +355,6 @@ async function singleFetchLoaderNavigationStrategy(
364355 // via `shouldRevalidate`
365356 if (
366357 m . route . id in router . state . loaderData &&
367- manifestRoute &&
368358 m . route . shouldRevalidate
369359 ) {
370360 if ( manifestRoute . hasLoader ) {
@@ -378,7 +368,7 @@ async function singleFetchLoaderNavigationStrategy(
378368
379369 // When a route has a client loader, it opts out of the singular call and
380370 // calls it's server loader via `serverLoader()` using a `?_routes` param
381- if ( manifestRoute && manifestRoute . hasClientLoader ) {
371+ if ( manifestRoute . hasClientLoader ) {
382372 if ( manifestRoute . hasLoader ) {
383373 foundOptOutRoute = true ;
384374 }
@@ -422,7 +412,7 @@ async function singleFetchLoaderNavigationStrategy(
422412 ) ;
423413
424414 // Wait for all routes to resolve above before we make the HTTP call
425- await routesLoadedPromise ;
415+ await Promise . all ( routeDfds . map ( ( d ) => d . promise ) ) ;
426416
427417 // We can skip the server call:
428418 // - On initial hydration - only clientLoaders can pass through via `clientLoader.hydrate`
@@ -437,24 +427,18 @@ async function singleFetchLoaderNavigationStrategy(
437427 ) {
438428 singleFetchDfd . resolve ( { } ) ;
439429 } else {
440- try {
441- // When one or more routes have opted out, we add a _routes param to
442- // limit the loaders to those that have a server loader and did not
443- // opt out
444- if ( ssr && foundOptOutRoute && routesParams . size > 0 ) {
445- url . searchParams . set (
446- "_routes" ,
447- matches
448- . filter ( ( m ) => routesParams . has ( m . route . id ) )
449- . map ( ( m ) => m . route . id )
450- . join ( "," )
451- ) ;
452- }
430+ // When routes have opted out, add a `_routes` param to filter server loaders
431+ // Skipped in `ssr:false` because we expect to be loading static `.data` files
432+ if ( ssr && foundOptOutRoute && routesParams . size > 0 ) {
433+ let routes = [ ...routesParams . keys ( ) ] . join ( "," ) ;
434+ url . searchParams . set ( "_routes" , routes ) ;
435+ }
453436
437+ try {
454438 let data = await fetchAndDecode ( url , init ) ;
455439 singleFetchDfd . resolve ( data . data as SingleFetchResults ) ;
456440 } catch ( e ) {
457- singleFetchDfd . reject ( e as Error ) ;
441+ singleFetchDfd . reject ( e ) ;
458442 }
459443 }
460444
@@ -673,17 +657,18 @@ function unwrapSingleFetchResult(result: SingleFetchResult, routeId: string) {
673657 }
674658}
675659
660+ type Deferred = ReturnType < typeof createDeferred > ;
676661function createDeferred < T = unknown > ( ) {
677662 let resolve : ( val ?: any ) => Promise < void > ;
678- let reject : ( error ?: Error ) => Promise < void > ;
663+ let reject : ( error ?: unknown ) => Promise < void > ;
679664 let promise = new Promise < T > ( ( res , rej ) => {
680665 resolve = async ( val : T ) => {
681666 res ( val ) ;
682667 try {
683668 await promise ;
684669 } catch ( e ) { }
685670 } ;
686- reject = async ( error ?: Error ) => {
671+ reject = async ( error ?: unknown ) => {
687672 rej ( error ) ;
688673 try {
689674 await promise ;
0 commit comments