@@ -5,7 +5,6 @@ import { isNotFound } from './not-found'
5
5
import { rootRouteId } from './root'
6
6
import { isRedirect } from './redirect'
7
7
import type { NotFoundError } from './not-found'
8
- import type { ControlledPromise } from './utils'
9
8
import type { ParsedLocation } from './location'
10
9
import type {
11
10
AnyRoute ,
@@ -56,12 +55,6 @@ const _handleNotFound = (inner: InnerLoadContext, err: NotFoundError) => {
56
55
// First check if a specific route is requested to show the error
57
56
const routeCursor =
58
57
inner . router . routesById [ err . routeId ?? '' ] ?? inner . router . routeTree
59
- const matchesByRouteId : Record < string , AnyRouteMatch > = { }
60
-
61
- // Setup routesByRouteId object for quick access
62
- for ( const match of inner . matches ) {
63
- matchesByRouteId [ match . routeId ] = match
64
- }
65
58
66
59
// Ensure a NotFoundComponent exists on the route
67
60
if (
@@ -80,7 +73,7 @@ const _handleNotFound = (inner: InnerLoadContext, err: NotFoundError) => {
80
73
)
81
74
82
75
// Find the match for this route
83
- const matchForRoute = matchesByRouteId [ routeCursor . id ]
76
+ const matchForRoute = inner . matches . find ( ( m ) => m . routeId === routeCursor . id )
84
77
85
78
invariant ( matchForRoute , 'Could not find match for route: ' + routeCursor . id )
86
79
@@ -246,7 +239,7 @@ const isBeforeLoadSsr = (
246
239
existingMatch . ssr = parentOverride ( route . options . ssr )
247
240
return
248
241
}
249
- const { search, params } = inner . router . getMatch ( matchId ) !
242
+ const { search, params } = existingMatch
250
243
251
244
const ssrFnContext : SsrContextOptions < any , any , any > = {
252
245
search : makeMaybe ( search , existingMatch . searchError ) ,
@@ -280,8 +273,8 @@ const setupPendingTimeout = (
280
273
inner : InnerLoadContext ,
281
274
matchId : string ,
282
275
route : AnyRoute ,
276
+ match : AnyRouteMatch ,
283
277
) : void => {
284
- const match = inner . router . getMatch ( matchId ) !
285
278
if ( match . _nonReactive . pendingTimeout !== undefined ) return
286
279
287
280
const pendingMs =
@@ -324,20 +317,20 @@ const shouldExecuteBeforeLoad = (
324
317
)
325
318
return true
326
319
327
- setupPendingTimeout ( inner , matchId , route )
320
+ setupPendingTimeout ( inner , matchId , route , existingMatch )
328
321
329
322
const then = ( ) => {
330
- let shouldExecuteBeforeLoad = true
323
+ let result = true
331
324
const match = inner . router . getMatch ( matchId ) !
332
325
if ( match . status === 'error' ) {
333
- shouldExecuteBeforeLoad = true
326
+ result = true
334
327
} else if (
335
328
match . preload &&
336
329
( match . status === 'redirected' || match . status === 'notFound' )
337
330
) {
338
331
handleRedirectAndNotFound ( inner , match , match . error )
339
332
}
340
- return shouldExecuteBeforeLoad
333
+ return result
341
334
}
342
335
343
336
// Wait for the beforeLoad to resolve before we continue
@@ -354,7 +347,6 @@ const executeBeforeLoad = (
354
347
) : void | Promise < void > => {
355
348
const match = inner . router . getMatch ( matchId ) !
356
349
357
- match . _nonReactive . beforeLoadPromise = createControlledPromise < void > ( )
358
350
// explicitly capture the previous loadPromise
359
351
const prevLoadPromise = match . _nonReactive . loadPromise
360
352
match . _nonReactive . loadPromise = createControlledPromise < void > ( ( ) => {
@@ -371,7 +363,7 @@ const executeBeforeLoad = (
371
363
handleSerialError ( inner , index , searchError , 'VALIDATE_SEARCH' )
372
364
}
373
365
374
- setupPendingTimeout ( inner , matchId , route )
366
+ setupPendingTimeout ( inner , matchId , route , match )
375
367
376
368
const abortController = new AbortController ( )
377
369
@@ -415,6 +407,8 @@ const executeBeforeLoad = (
415
407
return
416
408
}
417
409
410
+ match . _nonReactive . beforeLoadPromise = createControlledPromise < void > ( )
411
+
418
412
const { search, params, cause } = match
419
413
const preload = resolvePreload ( inner , matchId )
420
414
const beforeLoadFnContext : BeforeLoadContextOptions < any , any , any , any , any > =
@@ -510,8 +504,8 @@ const handleBeforeLoad = (
510
504
: execute ( shouldExecuteBeforeLoadResult )
511
505
}
512
506
513
- const execute = ( shouldExecuteBeforeLoad : boolean ) => {
514
- if ( shouldExecuteBeforeLoad ) {
507
+ const execute = ( shouldExec : boolean ) => {
508
+ if ( shouldExec ) {
515
509
// If we are not in the middle of a load OR the previous load failed, start it
516
510
return executeBeforeLoad ( inner , matchId , index , route )
517
511
}
@@ -567,14 +561,6 @@ const executeHead = (
567
561
} )
568
562
}
569
563
570
- const potentialPendingMinPromise = (
571
- inner : InnerLoadContext ,
572
- matchId : string ,
573
- ) : void | ControlledPromise < void > => {
574
- const latestMatch = inner . router . getMatch ( matchId ) !
575
- return latestMatch . _nonReactive . minPendingPromise
576
- }
577
-
578
564
const getLoaderContext = (
579
565
inner : InnerLoadContext ,
580
566
matchId : string ,
@@ -592,7 +578,7 @@ const getLoaderContext = (
592
578
deps : loaderDeps ,
593
579
preload : ! ! preload ,
594
580
parentMatchPromise,
595
- abortController : abortController ,
581
+ abortController,
596
582
context,
597
583
location : inner . location ,
598
584
navigate : ( opts ) =>
@@ -618,12 +604,11 @@ const runLoader = async (
618
604
// before committing to the match and resolving
619
605
// the loadPromise
620
606
607
+ const match = inner . router . getMatch ( matchId ) !
608
+
621
609
// Actually run the loader and handle the result
622
610
try {
623
- if (
624
- ! inner . router . isServer ||
625
- inner . router . getMatch ( matchId ) ! . ssr === true
626
- ) {
611
+ if ( ! inner . router . isServer || match . ssr === true ) {
627
612
loadRouteChunk ( route )
628
613
}
629
614
@@ -641,7 +626,7 @@ const runLoader = async (
641
626
route . options . head ||
642
627
route . options . scripts ||
643
628
route . options . headers ||
644
- inner . router . getMatch ( matchId ) ! . _nonReactive . minPendingPromise
629
+ match . _nonReactive . minPendingPromise
645
630
)
646
631
647
632
if ( willLoadSomething ) {
@@ -675,7 +660,7 @@ const runLoader = async (
675
660
if ( route . _lazyPromise ) await route . _lazyPromise
676
661
const headResult = executeHead ( inner , matchId , route )
677
662
const head = headResult ? await headResult : undefined
678
- const pendingPromise = potentialPendingMinPromise ( inner , matchId )
663
+ const pendingPromise = match . _nonReactive . minPendingPromise
679
664
if ( pendingPromise ) await pendingPromise
680
665
681
666
// Last but not least, wait for the the components
@@ -692,7 +677,7 @@ const runLoader = async (
692
677
} catch ( e ) {
693
678
let error = e
694
679
695
- const pendingPromise = potentialPendingMinPromise ( inner , matchId )
680
+ const pendingPromise = match . _nonReactive . minPendingPromise
696
681
if ( pendingPromise ) await pendingPromise
697
682
698
683
handleRedirectAndNotFound ( inner , inner . router . getMatch ( matchId ) , e )
@@ -744,7 +729,6 @@ const loadRouteMatch = async (
744
729
let loaderIsRunningAsync = false
745
730
const route = inner . router . looseRoutesById [ routeId ] !
746
731
747
- const prevMatch = inner . router . getMatch ( matchId ) !
748
732
if ( shouldSkipLoader ( inner , matchId ) ) {
749
733
if ( inner . router . isServer ) {
750
734
const headResult = executeHead ( inner , matchId , route )
@@ -757,88 +741,92 @@ const loadRouteMatch = async (
757
741
}
758
742
return inner . router . getMatch ( matchId ) !
759
743
}
760
- }
761
- // there is a loaderPromise, so we are in the middle of a load
762
- else if ( prevMatch . _nonReactive . loaderPromise ) {
763
- // do not block if we already have stale data we can show
764
- // but only if the ongoing load is not a preload since error handling is different for preloads
765
- // and we don't want to swallow errors
766
- if ( prevMatch . status === 'success' && ! inner . sync && ! prevMatch . preload ) {
767
- return inner . router . getMatch ( matchId ) !
768
- }
769
- await prevMatch . _nonReactive . loaderPromise
770
- const match = inner . router . getMatch ( matchId ) !
771
- if ( match . error ) {
772
- handleRedirectAndNotFound ( inner , match , match . error )
773
- }
774
744
} else {
775
- // This is where all of the stale-while-revalidate magic happens
776
- const age = Date . now ( ) - inner . router . getMatch ( matchId ) ! . updatedAt
777
-
778
- const preload = resolvePreload ( inner , matchId )
779
-
780
- const staleAge = preload
781
- ? ( route . options . preloadStaleTime ??
782
- inner . router . options . defaultPreloadStaleTime ??
783
- 30_000 ) // 30 seconds for preloads by default
784
- : ( route . options . staleTime ?? inner . router . options . defaultStaleTime ?? 0 )
785
-
786
- const shouldReloadOption = route . options . shouldReload
787
-
788
- // Default to reloading the route all the time
789
- // Allow shouldReload to get the last say,
790
- // if provided.
791
- const shouldReload =
792
- typeof shouldReloadOption === 'function'
793
- ? shouldReloadOption ( getLoaderContext ( inner , matchId , index , route ) )
794
- : shouldReloadOption
795
-
796
- const nextPreload =
797
- ! ! preload && ! inner . router . state . matches . some ( ( d ) => d . id === matchId )
798
- const match = inner . router . getMatch ( matchId ) !
799
- match . _nonReactive . loaderPromise = createControlledPromise < void > ( )
800
- if ( nextPreload !== match . preload ) {
801
- inner . updateMatch ( matchId , ( prev ) => ( {
802
- ...prev ,
803
- preload : nextPreload ,
804
- } ) )
805
- }
806
-
807
- // If the route is successful and still fresh, just resolve
808
- const { status, invalid } = inner . router . getMatch ( matchId ) !
809
- loaderShouldRunAsync =
810
- status === 'success' && ( invalid || ( shouldReload ?? age > staleAge ) )
811
- if ( preload && route . options . preload === false ) {
812
- // Do nothing
813
- } else if ( loaderShouldRunAsync && ! inner . sync ) {
814
- loaderIsRunningAsync = true
815
- ; ( async ( ) => {
816
- try {
817
- await runLoader ( inner , matchId , index , route )
818
- const match = inner . router . getMatch ( matchId ) !
819
- match . _nonReactive . loaderPromise ?. resolve ( )
820
- match . _nonReactive . loadPromise ?. resolve ( )
821
- match . _nonReactive . loaderPromise = undefined
822
- } catch ( err ) {
823
- if ( isRedirect ( err ) ) {
824
- await inner . router . navigate ( err . options )
825
- }
826
- }
827
- } ) ( )
828
- } else if ( status !== 'success' || ( loaderShouldRunAsync && inner . sync ) ) {
829
- await runLoader ( inner , matchId , index , route )
745
+ const prevMatch = inner . router . getMatch ( matchId ) !
746
+ // there is a loaderPromise, so we are in the middle of a load
747
+ if ( prevMatch . _nonReactive . loaderPromise ) {
748
+ // do not block if we already have stale data we can show
749
+ // but only if the ongoing load is not a preload since error handling is different for preloads
750
+ // and we don't want to swallow errors
751
+ if ( prevMatch . status === 'success' && ! inner . sync && ! prevMatch . preload ) {
752
+ return prevMatch
753
+ }
754
+ await prevMatch . _nonReactive . loaderPromise
755
+ const match = inner . router . getMatch ( matchId ) !
756
+ if ( match . error ) {
757
+ handleRedirectAndNotFound ( inner , match , match . error )
758
+ }
830
759
} else {
831
- // if the loader did not run, still update head.
832
- // reason: parent's beforeLoad may have changed the route context
833
- // and only now do we know the route context (and that the loader would not run)
834
- const headResult = executeHead ( inner , matchId , route )
835
- if ( headResult ) {
836
- const head = await headResult
760
+ // This is where all of the stale-while-revalidate magic happens
761
+ const age = Date . now ( ) - prevMatch . updatedAt
762
+
763
+ const preload = resolvePreload ( inner , matchId )
764
+
765
+ const staleAge = preload
766
+ ? ( route . options . preloadStaleTime ??
767
+ inner . router . options . defaultPreloadStaleTime ??
768
+ 30_000 ) // 30 seconds for preloads by default
769
+ : ( route . options . staleTime ??
770
+ inner . router . options . defaultStaleTime ??
771
+ 0 )
772
+
773
+ const shouldReloadOption = route . options . shouldReload
774
+
775
+ // Default to reloading the route all the time
776
+ // Allow shouldReload to get the last say,
777
+ // if provided.
778
+ const shouldReload =
779
+ typeof shouldReloadOption === 'function'
780
+ ? shouldReloadOption ( getLoaderContext ( inner , matchId , index , route ) )
781
+ : shouldReloadOption
782
+
783
+ const nextPreload =
784
+ ! ! preload && ! inner . router . state . matches . some ( ( d ) => d . id === matchId )
785
+ const match = inner . router . getMatch ( matchId ) !
786
+ match . _nonReactive . loaderPromise = createControlledPromise < void > ( )
787
+ if ( nextPreload !== match . preload ) {
837
788
inner . updateMatch ( matchId , ( prev ) => ( {
838
789
...prev ,
839
- ... head ,
790
+ preload : nextPreload ,
840
791
} ) )
841
792
}
793
+
794
+ // If the route is successful and still fresh, just resolve
795
+ const { status, invalid } = match
796
+ loaderShouldRunAsync =
797
+ status === 'success' && ( invalid || ( shouldReload ?? age > staleAge ) )
798
+ if ( preload && route . options . preload === false ) {
799
+ // Do nothing
800
+ } else if ( loaderShouldRunAsync && ! inner . sync ) {
801
+ loaderIsRunningAsync = true
802
+ ; ( async ( ) => {
803
+ try {
804
+ await runLoader ( inner , matchId , index , route )
805
+ const match = inner . router . getMatch ( matchId ) !
806
+ match . _nonReactive . loaderPromise ?. resolve ( )
807
+ match . _nonReactive . loadPromise ?. resolve ( )
808
+ match . _nonReactive . loaderPromise = undefined
809
+ } catch ( err ) {
810
+ if ( isRedirect ( err ) ) {
811
+ await inner . router . navigate ( err . options )
812
+ }
813
+ }
814
+ } ) ( )
815
+ } else if ( status !== 'success' || ( loaderShouldRunAsync && inner . sync ) ) {
816
+ await runLoader ( inner , matchId , index , route )
817
+ } else {
818
+ // if the loader did not run, still update head.
819
+ // reason: parent's beforeLoad may have changed the route context
820
+ // and only now do we know the route context (and that the loader would not run)
821
+ const headResult = executeHead ( inner , matchId , route )
822
+ if ( headResult ) {
823
+ const head = await headResult
824
+ inner . updateMatch ( matchId , ( prev ) => ( {
825
+ ...prev ,
826
+ ...head ,
827
+ } ) )
828
+ }
829
+ }
842
830
}
843
831
}
844
832
const match = inner . router . getMatch ( matchId ) !
@@ -858,8 +846,10 @@ const loadRouteMatch = async (
858
846
isFetching : nextIsFetching ,
859
847
invalid : false ,
860
848
} ) )
849
+ return inner . router . getMatch ( matchId ) !
850
+ } else {
851
+ return match
861
852
}
862
- return inner . router . getMatch ( matchId ) !
863
853
}
864
854
865
855
export async function loadMatches ( arg : {
0 commit comments