@tanstack/react-router 1.28.1 → 1.28.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/cjs/Matches.cjs +45 -23
  2. package/dist/cjs/Matches.cjs.map +1 -1
  3. package/dist/cjs/Matches.d.cts +5 -6
  4. package/dist/cjs/RouterProvider.cjs +2 -2
  5. package/dist/cjs/RouterProvider.cjs.map +1 -1
  6. package/dist/cjs/fileRoute.d.cts +4 -4
  7. package/dist/cjs/index.cjs +0 -1
  8. package/dist/cjs/index.cjs.map +1 -1
  9. package/dist/cjs/index.d.cts +1 -1
  10. package/dist/cjs/link.cjs.map +1 -1
  11. package/dist/cjs/link.d.cts +2 -0
  12. package/dist/cjs/redirects.cjs.map +1 -1
  13. package/dist/cjs/redirects.d.cts +3 -1
  14. package/dist/cjs/route.cjs.map +1 -1
  15. package/dist/cjs/route.d.cts +2 -2
  16. package/dist/cjs/router.cjs +374 -327
  17. package/dist/cjs/router.cjs.map +1 -1
  18. package/dist/cjs/router.d.cts +2 -2
  19. package/dist/cjs/utils.cjs +20 -2
  20. package/dist/cjs/utils.cjs.map +1 -1
  21. package/dist/cjs/utils.d.cts +6 -1
  22. package/dist/esm/Matches.d.ts +5 -6
  23. package/dist/esm/Matches.js +46 -24
  24. package/dist/esm/Matches.js.map +1 -1
  25. package/dist/esm/RouterProvider.js +2 -2
  26. package/dist/esm/RouterProvider.js.map +1 -1
  27. package/dist/esm/fileRoute.d.ts +4 -4
  28. package/dist/esm/index.d.ts +1 -1
  29. package/dist/esm/index.js +1 -2
  30. package/dist/esm/link.d.ts +2 -0
  31. package/dist/esm/link.js.map +1 -1
  32. package/dist/esm/redirects.d.ts +3 -1
  33. package/dist/esm/redirects.js.map +1 -1
  34. package/dist/esm/route.d.ts +2 -2
  35. package/dist/esm/route.js.map +1 -1
  36. package/dist/esm/router.d.ts +2 -2
  37. package/dist/esm/router.js +375 -328
  38. package/dist/esm/router.js.map +1 -1
  39. package/dist/esm/utils.d.ts +6 -1
  40. package/dist/esm/utils.js +20 -2
  41. package/dist/esm/utils.js.map +1 -1
  42. package/package.json +4 -2
  43. package/src/Matches.tsx +73 -35
  44. package/src/RouterProvider.tsx +2 -0
  45. package/src/index.tsx +0 -1
  46. package/src/link.tsx +2 -0
  47. package/src/redirects.ts +4 -2
  48. package/src/route.ts +2 -7
  49. package/src/router.ts +498 -426
  50. package/src/utils.ts +31 -2
@@ -33,6 +33,7 @@ class Router {
33
33
  this.latestLoadPromise = Promise.resolve();
34
34
  this.subscribers = /* @__PURE__ */ new Set();
35
35
  this.injectedHtml = [];
36
+ this.isServer = typeof document === "undefined";
36
37
  this.startReactTransition = (fn) => fn();
37
38
  this.update = (newOptions) => {
38
39
  if (newOptions.notFoundRoute) {
@@ -288,7 +289,7 @@ class Router {
288
289
  });
289
290
  const matches = [];
290
291
  matchedRoutes.forEach((route2, index) => {
291
- var _a, _b, _c, _d, _e, _f;
292
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
292
293
  const parentMatch = matches[index - 1];
293
294
  const [preMatchSearch, searchError] = (() => {
294
295
  const parentSearch = (parentMatch == null ? void 0 : parentMatch.search) ?? locationSearch;
@@ -327,36 +328,55 @@ class Router {
327
328
  }) + loaderDepsHash;
328
329
  const existingMatch = RouterProvider.getRouteMatch(this.state, matchId);
329
330
  const cause = this.state.matches.find((d) => d.id === matchId) ? "stay" : "enter";
330
- const match = existingMatch ? {
331
- ...existingMatch,
332
- cause,
333
- params: routeParams
334
- } : {
335
- id: matchId,
336
- routeId: route2.id,
337
- params: routeParams,
338
- pathname: path.joinPaths([this.basepath, interpolatedPath]),
339
- updatedAt: Date.now(),
340
- search: {},
341
- searchError: void 0,
342
- status: "pending",
343
- showPending: false,
344
- isFetching: false,
345
- error: void 0,
346
- paramsError: parseErrors[index],
347
- loadPromise: Promise.resolve(),
348
- routeContext: void 0,
349
- context: void 0,
350
- abortController: new AbortController(),
351
- fetchCount: 0,
352
- cause,
353
- loaderDeps,
354
- invalid: false,
355
- preload: false,
356
- links: (_d = (_c = route2.options).links) == null ? void 0 : _d.call(_c),
357
- scripts: (_f = (_e = route2.options).scripts) == null ? void 0 : _f.call(_e),
358
- staticData: route2.options.staticData || {}
359
- };
331
+ let match;
332
+ if (existingMatch) {
333
+ match = {
334
+ ...existingMatch,
335
+ cause,
336
+ params: routeParams
337
+ };
338
+ } else {
339
+ const status = route2.options.loader || route2.options.beforeLoad ? "pending" : "success";
340
+ const loadPromise = utils.createControlledPromise();
341
+ if (status === "success") {
342
+ loadPromise.resolve();
343
+ }
344
+ match = {
345
+ id: matchId,
346
+ routeId: route2.id,
347
+ params: routeParams,
348
+ pathname: path.joinPaths([this.basepath, interpolatedPath]),
349
+ updatedAt: Date.now(),
350
+ search: {},
351
+ searchError: void 0,
352
+ status: "pending",
353
+ isFetching: false,
354
+ error: void 0,
355
+ paramsError: parseErrors[index],
356
+ loaderPromise: Promise.resolve(),
357
+ loadPromise,
358
+ routeContext: void 0,
359
+ context: void 0,
360
+ abortController: new AbortController(),
361
+ fetchCount: 0,
362
+ cause,
363
+ loaderDeps,
364
+ invalid: false,
365
+ preload: false,
366
+ links: (_d = (_c = route2.options).links) == null ? void 0 : _d.call(_c),
367
+ scripts: (_f = (_e = route2.options).scripts) == null ? void 0 : _f.call(_e),
368
+ staticData: route2.options.staticData || {}
369
+ };
370
+ }
371
+ if (match.status === "success") {
372
+ match.meta = (_h = (_g = route2.options).meta) == null ? void 0 : _h.call(_g, {
373
+ params: match.params,
374
+ loaderData: match.loaderData
375
+ });
376
+ match.headers = (_j = (_i = route2.options).headers) == null ? void 0 : _j.call(_i, {
377
+ loaderData: match.loaderData
378
+ });
379
+ }
360
380
  if (!(opts == null ? void 0 : opts.preload)) {
361
381
  match.globalNotFound = globalNotFoundRouteId === route2.id;
362
382
  }
@@ -378,25 +398,20 @@ class Router {
378
398
  };
379
399
  this.buildLocation = (opts) => {
380
400
  const build = (dest = {}, matches) => {
381
- var _a, _b, _c;
382
- const relevantMatches = this.state.pendingMatches || this.state.matches;
383
- const fromSearch = (
384
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
385
- ((_a = relevantMatches[relevantMatches.length - 1]) == null ? void 0 : _a.search) || this.latestLocation.search
386
- );
387
- const fromMatches = this.matchRoutes(
388
- this.latestLocation.pathname,
389
- fromSearch
390
- );
401
+ var _a, _b, _c, _d;
402
+ const fromPath = dest.from || this.latestLocation.pathname;
403
+ let fromSearch = ((_a = dest._fromLocation) == null ? void 0 : _a.search) || this.latestLocation.search;
404
+ const fromMatches = this.matchRoutes(fromPath, fromSearch);
405
+ fromSearch = ((_b = utils.last(fromMatches)) == null ? void 0 : _b.search) || this.latestLocation.search;
391
406
  const stayingMatches = matches == null ? void 0 : matches.filter(
392
407
  (d) => fromMatches.find((e) => e.routeId === d.routeId)
393
408
  );
394
- const fromRoute = this.looseRoutesById[(_b = utils.last(fromMatches)) == null ? void 0 : _b.routeId];
409
+ const fromRoute = this.looseRoutesById[(_c = utils.last(fromMatches)) == null ? void 0 : _c.routeId];
395
410
  let pathname = dest.to ? this.resolvePathWithBase(
396
411
  dest.from ?? this.latestLocation.pathname,
397
412
  `${dest.to}`
398
413
  ) : this.resolvePathWithBase(fromRoute == null ? void 0 : fromRoute.fullPath, fromRoute == null ? void 0 : fromRoute.fullPath);
399
- const prevParams = { ...(_c = utils.last(fromMatches)) == null ? void 0 : _c.params };
414
+ const prevParams = { ...(_d = utils.last(fromMatches)) == null ? void 0 : _d.params };
400
415
  let nextParams = (dest.params ?? true) === true ? prevParams : { ...prevParams, ...utils.functionalUpdate(dest.params, prevParams) };
401
416
  if (Object.keys(nextParams).length > 0) {
402
417
  matches == null ? void 0 : matches.map((d) => this.looseRoutesById[d.routeId].options.stringifyParams).filter(Boolean).forEach((fn) => {
@@ -563,275 +578,302 @@ class Router {
563
578
  matches,
564
579
  preload
565
580
  }) => {
566
- var _a, _b;
567
581
  let latestPromise;
568
582
  let firstBadMatchIndex;
569
- const updateMatch = (match, opts) => {
570
- var _a2;
571
- const isPending = (_a2 = this.state.pendingMatches) == null ? void 0 : _a2.find(
572
- (d) => d.id === match.id
573
- );
574
- const isMatched = this.state.matches.find((d) => d.id === match.id);
583
+ const updateMatch = (id, updater, opts) => {
584
+ var _a;
585
+ let updated;
586
+ const isPending = (_a = this.state.pendingMatches) == null ? void 0 : _a.find((d) => d.id === id);
587
+ const isMatched = this.state.matches.find((d) => d.id === id);
575
588
  const matchesKey = isPending ? "pendingMatches" : isMatched ? "matches" : "cachedMatches";
576
589
  this.__store.setState((s) => {
577
- var _a3, _b2;
590
+ var _a2, _b;
578
591
  return {
579
592
  ...s,
580
- [matchesKey]: (opts == null ? void 0 : opts.remove) ? (_a3 = s[matchesKey]) == null ? void 0 : _a3.filter((d) => d.id !== match.id) : (_b2 = s[matchesKey]) == null ? void 0 : _b2.map((d) => d.id === match.id ? match : d)
593
+ [matchesKey]: (opts == null ? void 0 : opts.remove) ? (_a2 = s[matchesKey]) == null ? void 0 : _a2.filter((d) => d.id !== id) : (_b = s[matchesKey]) == null ? void 0 : _b.map(
594
+ (d) => d.id === id ? updated = updater(d) : d
595
+ )
581
596
  };
582
597
  });
598
+ return updated;
583
599
  };
584
600
  const handleMatchSpecialError = (match, err) => {
585
- match = {
586
- ...match,
601
+ updateMatch(match.id, (prev) => ({
602
+ ...prev,
587
603
  status: redirects.isRedirect(err) ? "redirected" : notFound.isNotFound(err) ? "notFound" : "error",
588
604
  isFetching: false,
589
605
  error: err
590
- };
591
- updateMatch(match);
606
+ }));
592
607
  if (!err.routeId) {
593
608
  err.routeId = match.routeId;
594
609
  }
595
610
  throw err;
596
611
  };
597
- for (let [index, match] of matches.entries()) {
598
- const parentMatch = matches[index - 1];
599
- const route2 = this.looseRoutesById[match.routeId];
600
- const abortController = new AbortController();
601
- const handleSerialError = (err, code) => {
602
- var _a2, _b2;
603
- err.routerCode = code;
604
- firstBadMatchIndex = firstBadMatchIndex ?? index;
605
- if (redirects.isRedirect(err) || notFound.isNotFound(err)) {
606
- handleMatchSpecialError(match, err);
607
- }
608
- try {
609
- (_b2 = (_a2 = route2.options).onError) == null ? void 0 : _b2.call(_a2, err);
610
- } catch (errorHandlerErr) {
611
- err = errorHandlerErr;
612
- if (redirects.isRedirect(err) || notFound.isNotFound(err)) {
613
- handleMatchSpecialError(match, errorHandlerErr);
614
- }
615
- }
616
- matches[index] = match = {
617
- ...match,
618
- error: err,
619
- status: "error",
620
- updatedAt: Date.now(),
621
- abortController: new AbortController()
622
- };
623
- };
624
- if (match.paramsError) {
625
- handleSerialError(match.paramsError, "PARSE_PARAMS");
626
- }
627
- if (match.searchError) {
628
- handleSerialError(match.searchError, "VALIDATE_SEARCH");
629
- }
630
- try {
631
- const parentContext = (parentMatch == null ? void 0 : parentMatch.context) ?? this.options.context ?? {};
632
- const pendingMs = route2.options.pendingMs ?? this.options.defaultPendingMs;
633
- const pendingPromise = typeof pendingMs === "number" && pendingMs <= 0 ? Promise.resolve() : new Promise((r) => setTimeout(r, pendingMs));
634
- const beforeLoadContext = await ((_b = (_a = route2.options).beforeLoad) == null ? void 0 : _b.call(_a, {
635
- search: match.search,
636
- abortController,
637
- params: match.params,
638
- preload: !!preload,
639
- context: parentContext,
640
- location,
641
- navigate: (opts) => this.navigate({ ...opts, from: match.pathname }),
642
- buildLocation: this.buildLocation,
643
- cause: preload ? "preload" : match.cause
644
- })) ?? {};
645
- if (redirects.isRedirect(beforeLoadContext) || notFound.isNotFound(beforeLoadContext)) {
646
- handleSerialError(beforeLoadContext, "BEFORE_LOAD");
647
- }
648
- const context = {
649
- ...parentContext,
650
- ...beforeLoadContext
651
- };
652
- matches[index] = match = {
653
- ...match,
654
- routeContext: utils.replaceEqualDeep(match.routeContext, beforeLoadContext),
655
- context: utils.replaceEqualDeep(match.context, context),
656
- abortController,
657
- pendingPromise
658
- };
659
- } catch (err) {
660
- handleSerialError(err, "BEFORE_LOAD");
661
- break;
662
- }
663
- }
664
- const validResolvedMatches = matches.slice(0, firstBadMatchIndex);
665
- const matchPromises = [];
666
- validResolvedMatches.forEach((match, index) => {
667
- matchPromises.push(
668
- // eslint-disable-next-line no-async-promise-executor
669
- new Promise(async (resolve, reject) => {
670
- var _a2;
671
- const parentMatchPromise = matchPromises[index - 1];
672
- const route2 = this.looseRoutesById[match.routeId];
673
- const handleError = (err) => {
674
- if (redirects.isRedirect(err) || notFound.isNotFound(err)) {
675
- handleMatchSpecialError(match, err);
676
- }
677
- };
678
- let loadPromise;
679
- matches[index] = match = {
680
- ...match,
681
- showPending: false
682
- };
683
- let didShowPending = false;
684
- const pendingMs = route2.options.pendingMs ?? this.options.defaultPendingMs;
685
- const pendingMinMs = route2.options.pendingMinMs ?? this.options.defaultPendingMinMs;
686
- const loaderContext = {
687
- params: match.params,
688
- deps: match.loaderDeps,
689
- preload: !!preload,
690
- parentMatchPromise,
691
- abortController: match.abortController,
692
- context: match.context,
693
- location,
694
- navigate: (opts) => this.navigate({ ...opts, from: match.pathname }),
695
- cause: preload ? "preload" : match.cause,
696
- route: route2
697
- };
698
- const fetch = async () => {
699
- var _a3, _b2, _c, _d, _e, _f, _g, _h, _i, _j;
700
- try {
701
- if (match.isFetching) {
702
- loadPromise = (_a3 = RouterProvider.getRouteMatch(this.state, match.id)) == null ? void 0 : _a3.loadPromise;
703
- } else {
612
+ try {
613
+ await new Promise((resolveAll, rejectAll) => {
614
+ ;
615
+ (async () => {
616
+ var _a, _b;
617
+ try {
618
+ for (let [index, match] of matches.entries()) {
619
+ const parentMatch = matches[index - 1];
620
+ const route2 = this.looseRoutesById[match.routeId];
621
+ const abortController = new AbortController();
622
+ const handleSerialError = (err, code) => {
623
+ var _a2, _b2;
624
+ err.routerCode = code;
625
+ firstBadMatchIndex = firstBadMatchIndex ?? index;
626
+ if (redirects.isRedirect(err) || notFound.isNotFound(err)) {
627
+ handleMatchSpecialError(match, err);
628
+ }
629
+ try {
630
+ (_b2 = (_a2 = route2.options).onError) == null ? void 0 : _b2.call(_a2, err);
631
+ } catch (errorHandlerErr) {
632
+ err = errorHandlerErr;
633
+ if (redirects.isRedirect(err) || notFound.isNotFound(err)) {
634
+ handleMatchSpecialError(match, errorHandlerErr);
635
+ }
636
+ }
704
637
  matches[index] = match = {
705
638
  ...match,
706
- isFetching: true,
707
- fetchCount: match.fetchCount + 1
639
+ error: err,
640
+ status: "error",
641
+ updatedAt: Date.now(),
642
+ abortController: new AbortController()
708
643
  };
709
- const lazyPromise = ((_b2 = route2.lazyFn) == null ? void 0 : _b2.call(route2).then((lazyRoute) => {
710
- Object.assign(route2.options, lazyRoute.options);
711
- })) || Promise.resolve();
712
- const componentsPromise = lazyPromise.then(
713
- () => Promise.all(
714
- componentTypes.map(async (type) => {
715
- const component = route2.options[type];
716
- if (component == null ? void 0 : component.preload) {
717
- await component.preload();
718
- }
719
- })
720
- )
721
- );
722
- const loaderPromise = (_d = (_c = route2.options).loader) == null ? void 0 : _d.call(_c, loaderContext);
723
- loadPromise = Promise.all([
724
- componentsPromise,
725
- loaderPromise,
726
- lazyPromise
727
- ]).then((d) => d[1]);
728
- }
729
- matches[index] = match = {
730
- ...match,
731
- loadPromise
732
644
  };
733
- updateMatch(match);
734
- const loaderData = await loadPromise;
735
- if (latestPromise = checkLatest())
736
- return await latestPromise;
737
- handleError(loaderData);
738
- if (didShowPending && pendingMinMs) {
739
- await new Promise((r) => setTimeout(r, pendingMinMs));
645
+ if (match.paramsError) {
646
+ handleSerialError(match.paramsError, "PARSE_PARAMS");
740
647
  }
741
- if (latestPromise = checkLatest())
742
- return await latestPromise;
743
- const [meta, headers] = await Promise.all([
744
- (_f = (_e = route2.options).meta) == null ? void 0 : _f.call(_e, {
745
- params: match.params,
746
- loaderData
747
- }),
748
- (_h = (_g = route2.options).headers) == null ? void 0 : _h.call(_g, {
749
- loaderData
750
- })
751
- ]);
752
- matches[index] = match = {
753
- ...match,
754
- error: void 0,
755
- status: "success",
756
- isFetching: false,
757
- updatedAt: Date.now(),
758
- loaderData,
759
- loadPromise: void 0,
760
- meta,
761
- headers
762
- };
763
- } catch (e) {
764
- let error = e;
765
- if (latestPromise = checkLatest())
766
- return await latestPromise;
767
- handleError(e);
768
- try {
769
- (_j = (_i = route2.options).onError) == null ? void 0 : _j.call(_i, e);
770
- } catch (onErrorError) {
771
- error = onErrorError;
772
- handleError(onErrorError);
648
+ if (match.searchError) {
649
+ handleSerialError(match.searchError, "VALIDATE_SEARCH");
773
650
  }
774
- matches[index] = match = {
775
- ...match,
776
- error,
777
- status: "error",
778
- isFetching: false
779
- };
780
- }
781
- updateMatch(match);
782
- };
783
- const age = Date.now() - match.updatedAt;
784
- const staleAge = preload ? route2.options.preloadStaleTime ?? this.options.defaultPreloadStaleTime ?? 3e4 : route2.options.staleTime ?? this.options.defaultStaleTime ?? 0;
785
- const shouldReloadOption = route2.options.shouldReload;
786
- const shouldReload = typeof shouldReloadOption === "function" ? shouldReloadOption(loaderContext) : shouldReloadOption;
787
- matches[index] = match = {
788
- ...match,
789
- preload: !!preload && !this.state.matches.find((d) => d.id === match.id)
790
- };
791
- if (match.status === "success" && (match.invalid || (shouldReload ?? age > staleAge))) {
792
- (async () => {
793
651
  try {
794
- await fetch();
795
- } catch (err) {
796
- console.info("Background Fetching Error", err);
797
- if (redirects.isRedirect(err)) {
798
- (this.state.pendingMatches || this.state.matches).find((d) => d.id === match.id);
799
- handleError(err);
800
- invariant(
801
- false,
802
- "You need to redirect from a background fetch? This is not supported yet. File an issue."
803
- );
804
- }
805
- }
806
- })();
807
- return resolve();
808
- }
809
- const shouldPending = !preload && route2.options.loader && typeof pendingMs === "number" && (route2.options.pendingComponent ?? this.options.defaultPendingComponent);
810
- if (match.status !== "success") {
811
- try {
812
- if (shouldPending) {
813
- (_a2 = match.pendingPromise) == null ? void 0 : _a2.then(async () => {
814
- if (latestPromise = checkLatest())
815
- return latestPromise;
816
- didShowPending = true;
817
- matches[index] = match = {
818
- ...match,
819
- showPending: true
820
- };
821
- updateMatch(match);
822
- resolve();
652
+ const parentContext = (parentMatch == null ? void 0 : parentMatch.context) ?? this.options.context ?? {};
653
+ const pendingMs = route2.options.pendingMs ?? this.options.defaultPendingMs;
654
+ const pendingPromise = typeof pendingMs !== "number" || pendingMs <= 0 ? Promise.resolve() : new Promise((r) => {
655
+ if (pendingMs !== Infinity)
656
+ setTimeout(r, pendingMs);
823
657
  });
658
+ const shouldPending = !this.isServer && !preload && (route2.options.loader || route2.options.beforeLoad) && typeof pendingMs === "number" && (route2.options.pendingComponent ?? this.options.defaultPendingComponent);
659
+ if (shouldPending) {
660
+ pendingPromise.then(async () => {
661
+ if (latestPromise = checkLatest())
662
+ return latestPromise;
663
+ resolveAll();
664
+ });
665
+ }
666
+ const beforeLoadContext = await ((_b = (_a = route2.options).beforeLoad) == null ? void 0 : _b.call(_a, {
667
+ search: match.search,
668
+ abortController,
669
+ params: match.params,
670
+ preload: !!preload,
671
+ context: parentContext,
672
+ location,
673
+ navigate: (opts) => this.navigate({ ...opts, from: match.pathname }),
674
+ buildLocation: this.buildLocation,
675
+ cause: preload ? "preload" : match.cause
676
+ })) ?? {};
677
+ if (redirects.isRedirect(beforeLoadContext) || notFound.isNotFound(beforeLoadContext)) {
678
+ handleSerialError(beforeLoadContext, "BEFORE_LOAD");
679
+ }
680
+ const context = {
681
+ ...parentContext,
682
+ ...beforeLoadContext
683
+ };
684
+ matches[index] = match = {
685
+ ...match,
686
+ routeContext: utils.replaceEqualDeep(
687
+ match.routeContext,
688
+ beforeLoadContext
689
+ ),
690
+ context: utils.replaceEqualDeep(match.context, context),
691
+ abortController
692
+ };
693
+ } catch (err) {
694
+ handleSerialError(err, "BEFORE_LOAD");
695
+ break;
824
696
  }
825
- await fetch();
826
- } catch (err) {
827
- reject(err);
828
697
  }
698
+ const validResolvedMatches = matches.slice(0, firstBadMatchIndex);
699
+ const matchPromises = [];
700
+ await Promise.all(
701
+ validResolvedMatches.map(async (match, index) => {
702
+ const parentMatchPromise = matchPromises[index - 1];
703
+ const route2 = this.looseRoutesById[match.routeId];
704
+ const handleError = (err) => {
705
+ if (redirects.isRedirect(err) || notFound.isNotFound(err)) {
706
+ handleMatchSpecialError(match, err);
707
+ }
708
+ };
709
+ const loaderContext = {
710
+ params: match.params,
711
+ deps: match.loaderDeps,
712
+ preload: !!preload,
713
+ parentMatchPromise,
714
+ abortController: match.abortController,
715
+ context: match.context,
716
+ location,
717
+ navigate: (opts) => this.navigate({ ...opts, from: match.pathname }),
718
+ cause: preload ? "preload" : match.cause,
719
+ route: route2
720
+ };
721
+ const fetch = async () => {
722
+ var _a2, _b2, _c, _d, _e, _f, _g, _h, _i;
723
+ const existing = RouterProvider.getRouteMatch(this.state, match.id);
724
+ let lazyPromise = Promise.resolve();
725
+ let componentsPromise = Promise.resolve();
726
+ let loaderPromise = existing.loaderPromise;
727
+ let loadPromise = existing.loadPromise;
728
+ const potentialPendingMinPromise = async () => {
729
+ const latestMatch = RouterProvider.getRouteMatch(this.state, match.id);
730
+ if (latestMatch == null ? void 0 : latestMatch.minPendingPromise) {
731
+ await latestMatch.minPendingPromise;
732
+ if (latestPromise = checkLatest())
733
+ return await latestPromise;
734
+ updateMatch(latestMatch.id, (prev) => ({
735
+ ...prev,
736
+ minPendingPromise: void 0
737
+ }));
738
+ }
739
+ };
740
+ try {
741
+ if (!match.isFetching) {
742
+ matches[index] = match = {
743
+ ...match,
744
+ isFetching: true,
745
+ fetchCount: match.fetchCount + 1
746
+ };
747
+ lazyPromise = ((_a2 = route2.lazyFn) == null ? void 0 : _a2.call(route2).then((lazyRoute) => {
748
+ Object.assign(route2.options, lazyRoute.options);
749
+ })) || Promise.resolve();
750
+ componentsPromise = lazyPromise.then(
751
+ () => Promise.all(
752
+ componentTypes.map(async (type) => {
753
+ const component = route2.options[type];
754
+ if (component == null ? void 0 : component.preload) {
755
+ await component.preload();
756
+ }
757
+ })
758
+ )
759
+ );
760
+ await lazyPromise;
761
+ if (latestPromise = checkLatest())
762
+ return await latestPromise;
763
+ loaderPromise = (_c = (_b2 = route2.options).loader) == null ? void 0 : _c.call(_b2, loaderContext);
764
+ const previousResolve = loadPromise.resolve;
765
+ loadPromise = utils.createControlledPromise(
766
+ // Resolve the old when we we resolve the new one
767
+ previousResolve
768
+ );
769
+ }
770
+ matches[index] = match = updateMatch(match.id, (prev) => ({
771
+ ...prev,
772
+ loaderPromise,
773
+ loadPromise
774
+ }));
775
+ const loaderData = await loaderPromise;
776
+ if (latestPromise = checkLatest())
777
+ return await latestPromise;
778
+ handleError(loaderData);
779
+ if (latestPromise = checkLatest())
780
+ return await latestPromise;
781
+ await potentialPendingMinPromise();
782
+ if (latestPromise = checkLatest())
783
+ return await latestPromise;
784
+ const meta = (_e = (_d = route2.options).meta) == null ? void 0 : _e.call(_d, {
785
+ params: match.params,
786
+ loaderData
787
+ });
788
+ const headers = (_g = (_f = route2.options).headers) == null ? void 0 : _g.call(_f, {
789
+ loaderData
790
+ });
791
+ matches[index] = match = updateMatch(match.id, (prev) => ({
792
+ ...prev,
793
+ error: void 0,
794
+ status: "success",
795
+ isFetching: false,
796
+ updatedAt: Date.now(),
797
+ loaderData,
798
+ meta,
799
+ headers
800
+ }));
801
+ } catch (e) {
802
+ let error = e;
803
+ if (latestPromise = checkLatest())
804
+ return await latestPromise;
805
+ await potentialPendingMinPromise();
806
+ if (latestPromise = checkLatest())
807
+ return await latestPromise;
808
+ handleError(e);
809
+ try {
810
+ (_i = (_h = route2.options).onError) == null ? void 0 : _i.call(_h, e);
811
+ } catch (onErrorError) {
812
+ error = onErrorError;
813
+ handleError(onErrorError);
814
+ }
815
+ matches[index] = match = updateMatch(match.id, (prev) => ({
816
+ ...prev,
817
+ error,
818
+ status: "error",
819
+ isFetching: false
820
+ }));
821
+ }
822
+ await componentsPromise;
823
+ if (latestPromise = checkLatest())
824
+ return await latestPromise;
825
+ loadPromise.resolve();
826
+ };
827
+ const age = Date.now() - match.updatedAt;
828
+ const staleAge = preload ? route2.options.preloadStaleTime ?? this.options.defaultPreloadStaleTime ?? 3e4 : route2.options.staleTime ?? this.options.defaultStaleTime ?? 0;
829
+ const shouldReloadOption = route2.options.shouldReload;
830
+ const shouldReload = typeof shouldReloadOption === "function" ? shouldReloadOption(loaderContext) : shouldReloadOption;
831
+ matches[index] = match = {
832
+ ...match,
833
+ preload: !!preload && !this.state.matches.find((d) => d.id === match.id)
834
+ };
835
+ const fetchWithRedirect = async () => {
836
+ try {
837
+ await fetch();
838
+ } catch (err) {
839
+ if (latestPromise = checkLatest())
840
+ return await latestPromise;
841
+ if (redirects.isRedirect(err)) {
842
+ const redirect = this.resolveRedirect(err);
843
+ if (!preload && !this.isServer) {
844
+ this.navigate({ ...redirect, replace: true });
845
+ }
846
+ throw redirect;
847
+ } else if (notFound.isNotFound(err)) {
848
+ if (!preload)
849
+ this.handleNotFound(matches, err);
850
+ throw err;
851
+ }
852
+ handleError(err);
853
+ }
854
+ };
855
+ if (match.status === "success" && (match.invalid || (shouldReload ?? age > staleAge))) {
856
+ fetchWithRedirect();
857
+ return;
858
+ }
859
+ if (match.status !== "success") {
860
+ await fetchWithRedirect();
861
+ }
862
+ })
863
+ );
864
+ if (latestPromise = checkLatest())
865
+ return await latestPromise;
866
+ resolveAll();
867
+ } catch (err) {
868
+ rejectAll(err);
829
869
  }
830
- resolve();
831
- })
832
- );
833
- });
834
- await Promise.all(matchPromises);
870
+ })();
871
+ });
872
+ } catch (err) {
873
+ if (redirects.isRedirect(err) || notFound.isNotFound(err)) {
874
+ throw err;
875
+ }
876
+ }
835
877
  return matches;
836
878
  };
837
879
  this.invalidate = () => {
@@ -852,53 +894,57 @@ class Router {
852
894
  return this.load();
853
895
  };
854
896
  this.load = async () => {
855
- const promise = new Promise(async (resolve, reject) => {
856
- const next = this.latestLocation;
857
- const prevLocation = this.state.resolvedLocation;
858
- const pathDidChange = prevLocation.href !== next.href;
859
- let latestPromise;
860
- this.cancelMatches();
861
- this.emit({
862
- type: "onBeforeLoad",
863
- fromLocation: prevLocation,
864
- toLocation: next,
865
- pathChanged: pathDidChange
866
- });
867
- let pendingMatches;
868
- const previousMatches = this.state.matches;
869
- this.__store.batch(() => {
870
- this.cleanCache();
871
- pendingMatches = this.matchRoutes(next.pathname, next.search, {
872
- debug: true
873
- });
874
- this.__store.setState((s) => ({
875
- ...s,
876
- isLoading: true,
877
- location: next,
878
- pendingMatches,
879
- cachedMatches: s.cachedMatches.filter((d) => {
880
- return !pendingMatches.find((e) => e.id === d.id);
881
- })
882
- }));
883
- });
897
+ let resolveLoad;
898
+ let rejectLoad;
899
+ const promise = new Promise((resolve, reject) => {
900
+ resolveLoad = resolve;
901
+ rejectLoad = reject;
902
+ });
903
+ this.latestLoadPromise = promise;
904
+ let latestPromise;
905
+ (async () => {
884
906
  try {
907
+ const next = this.latestLocation;
908
+ const prevLocation = this.state.resolvedLocation;
909
+ const pathDidChange = prevLocation.href !== next.href;
910
+ this.cancelMatches();
911
+ this.emit({
912
+ type: "onBeforeLoad",
913
+ fromLocation: prevLocation,
914
+ toLocation: next,
915
+ pathChanged: pathDidChange
916
+ });
917
+ let pendingMatches;
918
+ const previousMatches = this.state.matches;
919
+ this.__store.batch(() => {
920
+ this.cleanCache();
921
+ pendingMatches = this.matchRoutes(next.pathname, next.search);
922
+ this.__store.setState((s) => ({
923
+ ...s,
924
+ isLoading: true,
925
+ location: next,
926
+ pendingMatches,
927
+ cachedMatches: s.cachedMatches.filter((d) => {
928
+ return !pendingMatches.find((e) => e.id === d.id);
929
+ })
930
+ }));
931
+ });
885
932
  let redirect;
886
933
  let notFound$1;
887
934
  try {
888
- await this.loadMatches({
935
+ const loadMatchesPromise = this.loadMatches({
889
936
  matches: pendingMatches,
890
937
  location: next,
891
938
  checkLatest: () => this.checkLatest(promise)
892
939
  });
940
+ if (previousMatches.length || this.isServer) {
941
+ await loadMatchesPromise;
942
+ }
893
943
  } catch (err) {
894
944
  if (redirects.isRedirect(err)) {
895
- redirect = this.resolveRedirect(err);
896
- if (!utils.isServer) {
897
- this.navigate({ ...redirect, replace: true });
898
- }
945
+ redirect = err;
899
946
  } else if (notFound.isNotFound(err)) {
900
947
  notFound$1 = err;
901
- this.handleNotFound(pendingMatches, err);
902
948
  }
903
949
  }
904
950
  if (latestPromise = this.checkLatest(promise)) {
@@ -944,16 +990,15 @@ class Router {
944
990
  toLocation: next,
945
991
  pathChanged: pathDidChange
946
992
  });
947
- resolve();
993
+ resolveLoad();
948
994
  } catch (err) {
949
995
  if (latestPromise = this.checkLatest(promise)) {
950
996
  return latestPromise;
951
997
  }
952
- console.log("Load Error", err);
953
- reject(err);
998
+ console.error("Load Error", err);
999
+ rejectLoad(err);
954
1000
  }
955
- });
956
- this.latestLoadPromise = promise;
1001
+ })();
957
1002
  return this.latestLoadPromise;
958
1003
  };
959
1004
  this.resolveRedirect = (err) => {
@@ -1017,7 +1062,11 @@ class Router {
1017
1062
  return matches;
1018
1063
  } catch (err) {
1019
1064
  if (redirects.isRedirect(err)) {
1020
- return await this.preloadRoute(err);
1065
+ return await this.preloadRoute({
1066
+ _fromDest: next,
1067
+ from: next.pathname,
1068
+ ...err
1069
+ });
1021
1070
  }
1022
1071
  console.error(err);
1023
1072
  return void 0;
@@ -1158,8 +1207,7 @@ class Router {
1158
1207
  this.__store.setState((s) => {
1159
1208
  return {
1160
1209
  ...s,
1161
- matches,
1162
- lastUpdated: Date.now()
1210
+ matches
1163
1211
  };
1164
1212
  });
1165
1213
  };
@@ -1234,7 +1282,6 @@ function getInitialRouterState(location) {
1234
1282
  matches: [],
1235
1283
  pendingMatches: [],
1236
1284
  cachedMatches: [],
1237
- lastUpdated: 0,
1238
1285
  statusCode: 200
1239
1286
  };
1240
1287
  }