react-router 7.0.0-pre.0 → 7.0.0-pre.1

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.
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * React Router v7.0.0-pre.0
2
+ * React Router v7.0.0-pre.1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1301,24 +1301,12 @@ function createRouter(init) {
1301
1301
  // were marked for explicit hydration
1302
1302
  let loaderData = init.hydrationData ? init.hydrationData.loaderData : null;
1303
1303
  let errors = init.hydrationData ? init.hydrationData.errors : null;
1304
- let isRouteInitialized = m => {
1305
- // No loader, nothing to initialize
1306
- if (!m.route.loader) {
1307
- return true;
1308
- }
1309
- // Explicitly opting-in to running on hydration
1310
- if (typeof m.route.loader === "function" && m.route.loader.hydrate === true) {
1311
- return false;
1312
- }
1313
- // Otherwise, initialized if hydrated with data or an error
1314
- return loaderData && loaderData[m.route.id] !== undefined || errors && errors[m.route.id] !== undefined;
1315
- };
1316
1304
  // If errors exist, don't consider routes below the boundary
1317
1305
  if (errors) {
1318
1306
  let idx = initialMatches.findIndex(m => errors[m.route.id] !== undefined);
1319
- initialized = initialMatches.slice(0, idx + 1).every(isRouteInitialized);
1307
+ initialized = initialMatches.slice(0, idx + 1).every(m => !shouldLoadRouteOnHydration(m.route, loaderData, errors));
1320
1308
  } else {
1321
- initialized = initialMatches.every(isRouteInitialized);
1309
+ initialized = initialMatches.every(m => !shouldLoadRouteOnHydration(m.route, loaderData, errors));
1322
1310
  }
1323
1311
  }
1324
1312
  let router;
@@ -1385,9 +1373,6 @@ function createRouter(init) {
1385
1373
  // Store blocker functions in a separate Map outside of router state since
1386
1374
  // we don't need to update UI state if they change
1387
1375
  let blockerFunctions = new Map();
1388
- // Map of pending patchRoutesOnNavigation() promises (keyed by path/matches) so
1389
- // that we only kick them off once for a given combo
1390
- let pendingPatchRoutes = new Map();
1391
1376
  // Flag to ignore the next history update, so we can revert the URL change on
1392
1377
  // a POP navigation that was blocked by the user without touching router state
1393
1378
  let unblockBlockerHistoryUpdate = undefined;
@@ -1801,7 +1786,7 @@ function createRouter(init) {
1801
1786
  // Short circuit if it's only a hash change and not a revalidation or
1802
1787
  // mutation submission.
1803
1788
  //
1804
- // Ignore on initial page loads because since the initial load will always
1789
+ // Ignore on initial page loads because since the initial hydration will always
1805
1790
  // be "same hash". For example, on /page#hash and submit a <Form method="post">
1806
1791
  // which will default to a navigation to /page
1807
1792
  if (state.initialized && !isRevalidationRequired && isHashChangeOnly(state.location, location) && !(opts && opts.submission && isMutationMethod(opts.submission.formMethod))) {
@@ -1901,15 +1886,12 @@ function createRouter(init) {
1901
1886
  shortCircuited: true
1902
1887
  };
1903
1888
  } else if (discoverResult.type === "error") {
1904
- let {
1905
- boundaryId,
1906
- error
1907
- } = handleDiscoverRouteError(location.pathname, discoverResult);
1889
+ let boundaryId = findNearestBoundary(discoverResult.partialMatches).route.id;
1908
1890
  return {
1909
1891
  matches: discoverResult.partialMatches,
1910
1892
  pendingActionResult: [boundaryId, {
1911
1893
  type: ResultType.error,
1912
- error
1894
+ error: discoverResult.error
1913
1895
  }]
1914
1896
  };
1915
1897
  } else if (!discoverResult.matches) {
@@ -2028,15 +2010,12 @@ function createRouter(init) {
2028
2010
  shortCircuited: true
2029
2011
  };
2030
2012
  } else if (discoverResult.type === "error") {
2031
- let {
2032
- boundaryId,
2033
- error
2034
- } = handleDiscoverRouteError(location.pathname, discoverResult);
2013
+ let boundaryId = findNearestBoundary(discoverResult.partialMatches).route.id;
2035
2014
  return {
2036
2015
  matches: discoverResult.partialMatches,
2037
2016
  loaderData: {},
2038
2017
  errors: {
2039
- [boundaryId]: error
2018
+ [boundaryId]: discoverResult.error
2040
2019
  }
2041
2020
  };
2042
2021
  } else if (!discoverResult.matches) {
@@ -2153,17 +2132,9 @@ function createRouter(init) {
2153
2132
  loaderData,
2154
2133
  errors
2155
2134
  } = processLoaderData(state, matches, loaderResults, pendingActionResult, revalidatingFetchers, fetcherResults);
2156
- // With "partial hydration", preserve SSR errors for routes that don't re-run
2135
+ // Preserve SSR errors during partial hydration
2157
2136
  if (initialHydration && state.errors) {
2158
- Object.entries(state.errors).filter(_ref2 => {
2159
- let [id] = _ref2;
2160
- return !matchesToLoad.some(m => m.route.id === id);
2161
- }).forEach(_ref3 => {
2162
- let [routeId, error] = _ref3;
2163
- errors = Object.assign(errors || {}, {
2164
- [routeId]: error
2165
- });
2166
- });
2137
+ errors = _extends({}, state.errors, errors);
2167
2138
  }
2168
2139
  let updatedFetchers = markFetchRedirectsDone();
2169
2140
  let didAbortFetchLoads = abortStaleFetchLoads(pendingNavigationLoadId);
@@ -2278,10 +2249,7 @@ function createRouter(init) {
2278
2249
  if (discoverResult.type === "aborted") {
2279
2250
  return;
2280
2251
  } else if (discoverResult.type === "error") {
2281
- let {
2282
- error
2283
- } = handleDiscoverRouteError(path, discoverResult);
2284
- setFetcherError(key, routeId, error, {
2252
+ setFetcherError(key, routeId, discoverResult.error, {
2285
2253
  flushSync
2286
2254
  });
2287
2255
  return;
@@ -2452,10 +2420,7 @@ function createRouter(init) {
2452
2420
  if (discoverResult.type === "aborted") {
2453
2421
  return;
2454
2422
  } else if (discoverResult.type === "error") {
2455
- let {
2456
- error
2457
- } = handleDiscoverRouteError(path, discoverResult);
2458
- setFetcherError(key, routeId, error, {
2423
+ setFetcherError(key, routeId, discoverResult.error, {
2459
2424
  flushSync
2460
2425
  });
2461
2426
  return;
@@ -2813,12 +2778,12 @@ function createRouter(init) {
2813
2778
  blockers
2814
2779
  });
2815
2780
  }
2816
- function shouldBlockNavigation(_ref4) {
2781
+ function shouldBlockNavigation(_ref2) {
2817
2782
  let {
2818
2783
  currentLocation,
2819
2784
  nextLocation,
2820
2785
  historyAction
2821
- } = _ref4;
2786
+ } = _ref2;
2822
2787
  if (blockerFunctions.size === 0) {
2823
2788
  return;
2824
2789
  }
@@ -2860,16 +2825,6 @@ function createRouter(init) {
2860
2825
  error
2861
2826
  };
2862
2827
  }
2863
- function handleDiscoverRouteError(pathname, discoverResult) {
2864
- return {
2865
- boundaryId: findNearestBoundary(discoverResult.partialMatches).route.id,
2866
- error: getInternalRouterError(400, {
2867
- type: "route-discovery",
2868
- pathname,
2869
- message: discoverResult.error != null && "message" in discoverResult.error ? discoverResult.error : String(discoverResult.error)
2870
- })
2871
- };
2872
- }
2873
2828
  // Opt in to capturing and reporting scroll positions during navigations,
2874
2829
  // used by the <ScrollRestoration> component
2875
2830
  function enableScrollRestoration(positions, getPosition, getKey) {
@@ -2944,12 +2899,26 @@ function createRouter(init) {
2944
2899
  };
2945
2900
  }
2946
2901
  async function discoverRoutes(matches, pathname, signal) {
2902
+ if (!patchRoutesOnNavigationImpl) {
2903
+ return {
2904
+ type: "success",
2905
+ matches
2906
+ };
2907
+ }
2947
2908
  let partialMatches = matches;
2948
2909
  while (true) {
2949
2910
  let isNonHMR = inFlightDataRoutes == null;
2950
2911
  let routesToUse = inFlightDataRoutes || dataRoutes;
2912
+ let localManifest = manifest;
2951
2913
  try {
2952
- await loadLazyRouteChildren(patchRoutesOnNavigationImpl, pathname, partialMatches, routesToUse, manifest, mapRouteProperties, pendingPatchRoutes, signal);
2914
+ await patchRoutesOnNavigationImpl({
2915
+ path: pathname,
2916
+ matches: partialMatches,
2917
+ patch: (routeId, children) => {
2918
+ if (signal.aborted) return;
2919
+ patchRoutesImpl(routeId, children, routesToUse, localManifest, mapRouteProperties);
2920
+ }
2921
+ });
2953
2922
  } catch (e) {
2954
2923
  return {
2955
2924
  type: "error",
@@ -2963,7 +2932,7 @@ function createRouter(init) {
2963
2932
  // trigger a re-run of memoized `router.routes` dependencies.
2964
2933
  // HMR will already update the identity and reflow when it lands
2965
2934
  // `inFlightDataRoutes` in `completeNavigation`
2966
- if (isNonHMR) {
2935
+ if (isNonHMR && !signal.aborted) {
2967
2936
  dataRoutes = [...dataRoutes];
2968
2937
  }
2969
2938
  }
@@ -3531,8 +3500,8 @@ function normalizeNavigateOptions(isFetcher, path, opts) {
3531
3500
  }
3532
3501
  let text = typeof opts.body === "string" ? opts.body : opts.body instanceof FormData || opts.body instanceof URLSearchParams ?
3533
3502
  // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#plain-text-form-data
3534
- Array.from(opts.body.entries()).reduce((acc, _ref5) => {
3535
- let [name, value] = _ref5;
3503
+ Array.from(opts.body.entries()).reduce((acc, _ref3) => {
3504
+ let [name, value] = _ref3;
3536
3505
  return "" + acc + name + "=" + value + "\n";
3537
3506
  }, "") : String(opts.body);
3538
3507
  return {
@@ -3620,25 +3589,36 @@ function normalizeNavigateOptions(isFetcher, path, opts) {
3620
3589
  submission
3621
3590
  };
3622
3591
  }
3623
- // Filter out all routes below any caught error as they aren't going to
3592
+ // Filter out all routes at/below any caught error as they aren't going to
3624
3593
  // render so we don't need to load them
3625
- function getLoaderMatchesUntilBoundary(matches, boundaryId) {
3626
- let boundaryMatches = matches;
3627
- if (boundaryId) {
3628
- let index = matches.findIndex(m => m.route.id === boundaryId);
3629
- if (index >= 0) {
3630
- boundaryMatches = matches.slice(0, index);
3631
- }
3594
+ function getLoaderMatchesUntilBoundary(matches, boundaryId, includeBoundary) {
3595
+ if (includeBoundary === void 0) {
3596
+ includeBoundary = false;
3632
3597
  }
3633
- return boundaryMatches;
3598
+ let index = matches.findIndex(m => m.route.id === boundaryId);
3599
+ if (index >= 0) {
3600
+ return matches.slice(0, includeBoundary ? index + 1 : index);
3601
+ }
3602
+ return matches;
3634
3603
  }
3635
- function getMatchesToLoad(history, state, matches, submission, location, isInitialLoad, isRevalidationRequired, cancelledFetcherLoads, fetchersQueuedForDeletion, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, pendingActionResult) {
3604
+ function getMatchesToLoad(history, state, matches, submission, location, initialHydration, isRevalidationRequired, cancelledFetcherLoads, fetchersQueuedForDeletion, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, pendingActionResult) {
3636
3605
  let actionResult = pendingActionResult ? isErrorResult(pendingActionResult[1]) ? pendingActionResult[1].error : pendingActionResult[1].data : undefined;
3637
3606
  let currentUrl = history.createURL(state.location);
3638
3607
  let nextUrl = history.createURL(location);
3639
3608
  // Pick navigation matches that are net-new or qualify for revalidation
3640
- let boundaryId = pendingActionResult && isErrorResult(pendingActionResult[1]) ? pendingActionResult[0] : undefined;
3641
- let boundaryMatches = boundaryId ? getLoaderMatchesUntilBoundary(matches, boundaryId) : matches;
3609
+ let boundaryMatches = matches;
3610
+ if (initialHydration && state.errors) {
3611
+ // On initial hydration, only consider matches up to _and including_ the boundary.
3612
+ // This is inclusive to handle cases where a server loader ran successfully,
3613
+ // a child server loader bubbled up to this route, but this route has
3614
+ // `clientLoader.hydrate` so we want to still run the `clientLoader` so that
3615
+ // we have a complete version of `loaderData`
3616
+ boundaryMatches = getLoaderMatchesUntilBoundary(matches, Object.keys(state.errors)[0], true);
3617
+ } else if (pendingActionResult && isErrorResult(pendingActionResult[1])) {
3618
+ // If an action threw an error, we call loaders up to, but not including the
3619
+ // boundary
3620
+ boundaryMatches = getLoaderMatchesUntilBoundary(matches, pendingActionResult[0]);
3621
+ }
3642
3622
  // Don't revalidate loaders by default after action 4xx/5xx responses
3643
3623
  // when the flag is enabled. They can still opt-into revalidation via
3644
3624
  // `shouldRevalidate` via `actionResult`
@@ -3655,13 +3635,8 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
3655
3635
  if (route.loader == null) {
3656
3636
  return false;
3657
3637
  }
3658
- if (isInitialLoad) {
3659
- if (typeof route.loader !== "function" || route.loader.hydrate) {
3660
- return true;
3661
- }
3662
- return !state.loaderData.hasOwnProperty(route.id) && (
3663
- // Don't re-run if the loader ran and threw an error
3664
- !state.errors || state.errors[route.id] === undefined);
3638
+ if (initialHydration) {
3639
+ return shouldLoadRouteOnHydration(route, state.loaderData, state.errors);
3665
3640
  }
3666
3641
  // Always call the loader on new route instances
3667
3642
  if (isNewLoader(state.loaderData, state.matches[index], match)) {
@@ -3692,10 +3667,11 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
3692
3667
  let revalidatingFetchers = [];
3693
3668
  fetchLoadMatches.forEach((f, key) => {
3694
3669
  // Don't revalidate:
3695
- // - on initial load (shouldn't be any fetchers then anyway)
3696
- // - if fetcher no longer matches the URL
3697
- // - if fetcher was unmounted
3698
- if (isInitialLoad || !matches.some(m => m.route.id === f.routeId) || fetchersQueuedForDeletion.has(key)) {
3670
+ // - on initial hydration (shouldn't be any fetchers then anyway)
3671
+ // - if fetcher won't be present in the subsequent render
3672
+ // - no longer matches the URL (v7_fetcherPersist=false)
3673
+ // - was unmounted but persisted due to v7_fetcherPersist=true
3674
+ if (initialHydration || !matches.some(m => m.route.id === f.routeId) || fetchersQueuedForDeletion.has(key)) {
3699
3675
  return;
3700
3676
  }
3701
3677
  let fetcherMatches = matchRoutes(routesToUse, f.path, basename);
@@ -3759,6 +3735,28 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
3759
3735
  });
3760
3736
  return [navigationMatches, revalidatingFetchers];
3761
3737
  }
3738
+ function shouldLoadRouteOnHydration(route, loaderData, errors) {
3739
+ // We dunno if we have a loader - gotta find out!
3740
+ if (route.lazy) {
3741
+ return true;
3742
+ }
3743
+ // No loader, nothing to initialize
3744
+ if (!route.loader) {
3745
+ return false;
3746
+ }
3747
+ let hasData = loaderData != null && loaderData[route.id] !== undefined;
3748
+ let hasError = errors != null && errors[route.id] !== undefined;
3749
+ // Don't run if we error'd during SSR
3750
+ if (!hasData && hasError) {
3751
+ return false;
3752
+ }
3753
+ // Explicitly opting-in to running on hydration
3754
+ if (typeof route.loader === "function" && route.loader.hydrate === true) {
3755
+ return true;
3756
+ }
3757
+ // Otherwise, run if we're not yet initialized with anything
3758
+ return !hasData && !hasError;
3759
+ }
3762
3760
  function isNewLoader(currentLoaderData, currentMatch, match) {
3763
3761
  let isNew =
3764
3762
  // [a] -> [a, b]
@@ -3790,33 +3788,6 @@ function shouldRevalidateLoader(loaderMatch, arg) {
3790
3788
  }
3791
3789
  return arg.defaultShouldRevalidate;
3792
3790
  }
3793
- /**
3794
- * Idempotent utility to execute patchRoutesOnNavigation() to lazily load route
3795
- * definitions and update the routes/routeManifest
3796
- */
3797
- async function loadLazyRouteChildren(patchRoutesOnNavigationImpl, path, matches, routes, manifest, mapRouteProperties, pendingRouteChildren, signal) {
3798
- let key = [path, ...matches.map(m => m.route.id)].join("-");
3799
- try {
3800
- let pending = pendingRouteChildren.get(key);
3801
- if (!pending) {
3802
- pending = patchRoutesOnNavigationImpl({
3803
- path,
3804
- matches,
3805
- patch: (routeId, children) => {
3806
- if (!signal.aborted) {
3807
- patchRoutesImpl(routeId, children, routes, manifest, mapRouteProperties);
3808
- }
3809
- }
3810
- });
3811
- pendingRouteChildren.set(key, pending);
3812
- }
3813
- if (pending && isPromise(pending)) {
3814
- await pending;
3815
- }
3816
- } finally {
3817
- pendingRouteChildren.delete(key);
3818
- }
3819
- }
3820
3791
  function patchRoutesImpl(routeId, children, routesToUse, manifest, mapRouteProperties) {
3821
3792
  var _childrenToPatch;
3822
3793
  let childrenToPatch;
@@ -3907,10 +3878,10 @@ async function loadLazyRouteModule(route, mapRouteProperties, manifest) {
3907
3878
  }));
3908
3879
  }
3909
3880
  // Default implementation of `dataStrategy` which fetches all loaders in parallel
3910
- async function defaultDataStrategy(_ref6) {
3881
+ async function defaultDataStrategy(_ref4) {
3911
3882
  let {
3912
3883
  matches
3913
- } = _ref6;
3884
+ } = _ref4;
3914
3885
  let matchesToLoad = matches.filter(m => m.shouldLoad);
3915
3886
  let results = await Promise.all(matchesToLoad.map(m => m.resolve()));
3916
3887
  return results.reduce((acc, result, i) => Object.assign(acc, {
@@ -4340,11 +4311,11 @@ function processLoaderData(state, matches, results, pendingActionResult, revalid
4340
4311
  }
4341
4312
  function mergeLoaderData(loaderData, newLoaderData, matches, errors) {
4342
4313
  // Start with all new entries that are not being reset
4343
- let mergedLoaderData = Object.entries(newLoaderData).filter(_ref7 => {
4344
- let [, v] = _ref7;
4314
+ let mergedLoaderData = Object.entries(newLoaderData).filter(_ref5 => {
4315
+ let [, v] = _ref5;
4345
4316
  return v !== ResetLoaderDataSymbol;
4346
- }).reduce((merged, _ref8) => {
4347
- let [k, v] = _ref8;
4317
+ }).reduce((merged, _ref6) => {
4318
+ let [k, v] = _ref6;
4348
4319
  merged[k] = v;
4349
4320
  return merged;
4350
4321
  }, {});
@@ -4409,9 +4380,7 @@ function getInternalRouterError(status, _temp5) {
4409
4380
  let errorMessage = "Unknown @remix-run/router error";
4410
4381
  if (status === 400) {
4411
4382
  statusText = "Bad Request";
4412
- if (type === "route-discovery") {
4413
- errorMessage = "Unable to match URL \"" + pathname + "\" - the `patchRoutesOnNavigation()` " + ("function threw the following error:\n" + message);
4414
- } else if (method && pathname && routeId) {
4383
+ if (method && pathname && routeId) {
4415
4384
  errorMessage = "You made a " + method + " request to \"" + pathname + "\" but " + ("did not provide a `loader` for route \"" + routeId + "\", ") + "so there is no way to handle the request.";
4416
4385
  } else if (type === "invalid-body") {
4417
4386
  errorMessage = "Unable to encode submission body";
@@ -4469,9 +4438,6 @@ function isHashChangeOnly(a, b) {
4469
4438
  // /page#hash -> /page
4470
4439
  return false;
4471
4440
  }
4472
- function isPromise(val) {
4473
- return typeof val === "object" && val != null && "then" in val;
4474
- }
4475
4441
  function isDataStrategyResult(result) {
4476
4442
  return result != null && typeof result === "object" && "type" in result && "result" in result && (result.type === ResultType.data || result.type === ResultType.error);
4477
4443
  }
@@ -5404,7 +5370,7 @@ var DataRouterStateHook$1;
5404
5370
  DataRouterStateHook["UseRouteId"] = "useRouteId";
5405
5371
  })(DataRouterStateHook$1 || (DataRouterStateHook$1 = {}));
5406
5372
  function getDataRouterConsoleError$1(hookName) {
5407
- return hookName + " must be used within a data router. See https://reactrouter.com/routers/picking-a-router.";
5373
+ return hookName + " must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.";
5408
5374
  }
5409
5375
  function useDataRouterContext$2(hookName) {
5410
5376
  let ctx = React.useContext(DataRouterContext);
@@ -7669,8 +7635,10 @@ function createClientRoutes(manifest, routeModulesCache, initialState, isSpaMode
7669
7635
  handle: routeModule.handle,
7670
7636
  shouldRevalidate: needsRevalidation ? wrapShouldRevalidateForHdr(route.id, routeModule.shouldRevalidate, needsRevalidation) : routeModule.shouldRevalidate
7671
7637
  }));
7672
- let initialData = initialState == null || (_initialState$loaderD = initialState.loaderData) == null ? void 0 : _initialState$loaderD[route.id];
7673
- let initialError = initialState == null || (_initialState$errors = initialState.errors) == null ? void 0 : _initialState$errors[route.id];
7638
+ let hasInitialData = initialState && initialState.loaderData && route.id in initialState.loaderData;
7639
+ let initialData = hasInitialData ? initialState == null || (_initialState$loaderD = initialState.loaderData) == null ? void 0 : _initialState$loaderD[route.id] : undefined;
7640
+ let hasInitialError = initialState && initialState.errors && route.id in initialState.errors;
7641
+ let initialError = hasInitialError ? initialState == null || (_initialState$errors = initialState.errors) == null ? void 0 : _initialState$errors[route.id] : undefined;
7674
7642
  let isHydrationRequest = needsRevalidation == null && (((_routeModule$clientLo = routeModule.clientLoader) == null ? void 0 : _routeModule$clientLo.hydrate) === true || !route.hasLoader);
7675
7643
  dataRoute.loader = async (_ref, singleFetch) => {
7676
7644
  let {
@@ -7692,10 +7660,12 @@ function createClientRoutes(manifest, routeModulesCache, initialState, isSpaMode
7692
7660
  preventInvalidServerHandlerCall("loader", route, isSpaMode);
7693
7661
  // On the first call, resolve with the server result
7694
7662
  if (isHydrationRequest) {
7695
- if (initialError !== undefined) {
7663
+ if (hasInitialData) {
7664
+ return initialData;
7665
+ }
7666
+ if (hasInitialError) {
7696
7667
  throw initialError;
7697
7668
  }
7698
- return initialData;
7699
7669
  }
7700
7670
  // Call the server loader for client-side navigations
7701
7671
  return fetchServerLoader(singleFetch);
@@ -9145,7 +9115,7 @@ var DataRouterStateHook;
9145
9115
  })(DataRouterStateHook || (DataRouterStateHook = {}));
9146
9116
  // Internal hooks
9147
9117
  function getDataRouterConsoleError(hookName) {
9148
- return hookName + " must be used within a data router. See https://reactrouter.com/routers/picking-a-router.";
9118
+ return hookName + " must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.";
9149
9119
  }
9150
9120
  function useDataRouterContext(hookName) {
9151
9121
  let ctx = React.useContext(DataRouterContext);
@@ -10566,6 +10536,13 @@ function stripRoutesParam(request) {
10566
10536
  return new Request(url.href, init);
10567
10537
  }
10568
10538
 
10539
+ function invariant(value, message) {
10540
+ if (value === false || value === null || typeof value === "undefined") {
10541
+ console.error("The following error is a bug in Remix; please open an issue! https://github.com/remix-run/remix/issues/new");
10542
+ throw new Error(message);
10543
+ }
10544
+ }
10545
+
10569
10546
  function groupRoutesByParentId(manifest) {
10570
10547
  let routes = {};
10571
10548
  Object.values(manifest).forEach(route => {
@@ -10607,7 +10584,29 @@ function createStaticHandlerDataRoutes(manifest, future, parentId, routesByParen
10607
10584
  path: route.path,
10608
10585
  // Need to use RR's version in the param typed here to permit the optional
10609
10586
  // context even though we know it'll always be provided in remix
10610
- loader: route.module.loader ? args => callRouteHandler(route.module.loader, args) : undefined,
10587
+ loader: route.module.loader ? async args => {
10588
+ // If we're prerendering, use the data passed in from prerendering
10589
+ // the .data route so we dom't call loaders twice
10590
+ if (args.request.headers.has("X-React-Router-Prerender-Data")) {
10591
+ let encoded = args.request.headers.get("X-React-Router-Prerender-Data");
10592
+ !encoded ? process.env.NODE_ENV !== "production" ? invariant(false, "Missing prerendered data for route") : invariant(false) : void 0;
10593
+ let uint8array = new TextEncoder().encode(encoded);
10594
+ let stream = new ReadableStream({
10595
+ start(controller) {
10596
+ controller.enqueue(uint8array);
10597
+ controller.close();
10598
+ }
10599
+ });
10600
+ let decoded = await decodeViaTurboStream(stream, global);
10601
+ let data = decoded.value;
10602
+ !(data && route.id in data) ? process.env.NODE_ENV !== "production" ? invariant(false, "Unable to decode prerendered data") : invariant(false) : void 0;
10603
+ let result = data[route.id];
10604
+ !("data" in result) ? process.env.NODE_ENV !== "production" ? invariant(false, "Unable to process prerendered data") : invariant(false) : void 0;
10605
+ return result.data;
10606
+ }
10607
+ let val = await callRouteHandler(route.module.loader, args);
10608
+ return val;
10609
+ } : undefined,
10611
10610
  action: route.module.action ? args => callRouteHandler(route.module.action, args) : undefined,
10612
10611
  handle: route.module.handle
10613
10612
  };
@@ -10976,13 +10975,6 @@ function encodeViaTurboStream(data, requestSignal, streamTimeout, serverMode) {
10976
10975
  });
10977
10976
  }
10978
10977
 
10979
- function invariant(value, message) {
10980
- if (value === false || value === null || typeof value === "undefined") {
10981
- console.error("The following error is a bug in Remix; please open an issue! https://github.com/remix-run/remix/issues/new");
10982
- throw new Error(message);
10983
- }
10984
- }
10985
-
10986
10978
  function derive(build, mode) {
10987
10979
  let routes = createRoutes(build.routes);
10988
10980
  let dataRoutes = createStaticHandlerDataRoutes(build.routes, build.future);