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.
@@ -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
  *
@@ -1335,6 +1335,7 @@
1335
1335
  * @private
1336
1336
  */
1337
1337
  const normalizeHash = hash => !hash || hash === "#" ? "" : hash.startsWith("#") ? hash : "#" + hash;
1338
+
1338
1339
  /**
1339
1340
  * This is a shortcut for creating `application/json` responses. Converts `data`
1340
1341
  * to JSON and sets the `Content-Type` header.
@@ -1699,25 +1700,12 @@
1699
1700
  // were marked for explicit hydration
1700
1701
  let loaderData = init.hydrationData ? init.hydrationData.loaderData : null;
1701
1702
  let errors = init.hydrationData ? init.hydrationData.errors : null;
1702
- let isRouteInitialized = m => {
1703
- // No loader, nothing to initialize
1704
- if (!m.route.loader) {
1705
- return true;
1706
- }
1707
- // Explicitly opting-in to running on hydration
1708
- if (typeof m.route.loader === "function" && m.route.loader.hydrate === true) {
1709
- return false;
1710
- }
1711
- // Otherwise, initialized if hydrated with data or an error
1712
- return loaderData && loaderData[m.route.id] !== undefined || errors && errors[m.route.id] !== undefined;
1713
- };
1714
-
1715
1703
  // If errors exist, don't consider routes below the boundary
1716
1704
  if (errors) {
1717
1705
  let idx = initialMatches.findIndex(m => errors[m.route.id] !== undefined);
1718
- initialized = initialMatches.slice(0, idx + 1).every(isRouteInitialized);
1706
+ initialized = initialMatches.slice(0, idx + 1).every(m => !shouldLoadRouteOnHydration(m.route, loaderData, errors));
1719
1707
  } else {
1720
- initialized = initialMatches.every(isRouteInitialized);
1708
+ initialized = initialMatches.every(m => !shouldLoadRouteOnHydration(m.route, loaderData, errors));
1721
1709
  }
1722
1710
  }
1723
1711
  let router;
@@ -1803,10 +1791,6 @@
1803
1791
  // we don't need to update UI state if they change
1804
1792
  let blockerFunctions = new Map();
1805
1793
 
1806
- // Map of pending patchRoutesOnNavigation() promises (keyed by path/matches) so
1807
- // that we only kick them off once for a given combo
1808
- let pendingPatchRoutes = new Map();
1809
-
1810
1794
  // Flag to ignore the next history update, so we can revert the URL change on
1811
1795
  // a POP navigation that was blocked by the user without touching router state
1812
1796
  let unblockBlockerHistoryUpdate = undefined;
@@ -2247,7 +2231,7 @@
2247
2231
  // Short circuit if it's only a hash change and not a revalidation or
2248
2232
  // mutation submission.
2249
2233
  //
2250
- // Ignore on initial page loads because since the initial load will always
2234
+ // Ignore on initial page loads because since the initial hydration will always
2251
2235
  // be "same hash". For example, on /page#hash and submit a <Form method="post">
2252
2236
  // which will default to a navigation to /page
2253
2237
  if (state.initialized && !isRevalidationRequired && isHashChangeOnly(state.location, location) && !(opts && opts.submission && isMutationMethod(opts.submission.formMethod))) {
@@ -2354,15 +2338,12 @@
2354
2338
  shortCircuited: true
2355
2339
  };
2356
2340
  } else if (discoverResult.type === "error") {
2357
- let {
2358
- boundaryId,
2359
- error
2360
- } = handleDiscoverRouteError(location.pathname, discoverResult);
2341
+ let boundaryId = findNearestBoundary(discoverResult.partialMatches).route.id;
2361
2342
  return {
2362
2343
  matches: discoverResult.partialMatches,
2363
2344
  pendingActionResult: [boundaryId, {
2364
2345
  type: ResultType.error,
2365
- error
2346
+ error: discoverResult.error
2366
2347
  }]
2367
2348
  };
2368
2349
  } else if (!discoverResult.matches) {
@@ -2487,15 +2468,12 @@
2487
2468
  shortCircuited: true
2488
2469
  };
2489
2470
  } else if (discoverResult.type === "error") {
2490
- let {
2491
- boundaryId,
2492
- error
2493
- } = handleDiscoverRouteError(location.pathname, discoverResult);
2471
+ let boundaryId = findNearestBoundary(discoverResult.partialMatches).route.id;
2494
2472
  return {
2495
2473
  matches: discoverResult.partialMatches,
2496
2474
  loaderData: {},
2497
2475
  errors: {
2498
- [boundaryId]: error
2476
+ [boundaryId]: discoverResult.error
2499
2477
  }
2500
2478
  };
2501
2479
  } else if (!discoverResult.matches) {
@@ -2618,17 +2596,9 @@
2618
2596
  errors
2619
2597
  } = processLoaderData(state, matches, loaderResults, pendingActionResult, revalidatingFetchers, fetcherResults);
2620
2598
 
2621
- // With "partial hydration", preserve SSR errors for routes that don't re-run
2599
+ // Preserve SSR errors during partial hydration
2622
2600
  if (initialHydration && state.errors) {
2623
- Object.entries(state.errors).filter(_ref2 => {
2624
- let [id] = _ref2;
2625
- return !matchesToLoad.some(m => m.route.id === id);
2626
- }).forEach(_ref3 => {
2627
- let [routeId, error] = _ref3;
2628
- errors = Object.assign(errors || {}, {
2629
- [routeId]: error
2630
- });
2631
- });
2601
+ errors = _extends({}, state.errors, errors);
2632
2602
  }
2633
2603
  let updatedFetchers = markFetchRedirectsDone();
2634
2604
  let didAbortFetchLoads = abortStaleFetchLoads(pendingNavigationLoadId);
@@ -2747,10 +2717,7 @@
2747
2717
  if (discoverResult.type === "aborted") {
2748
2718
  return;
2749
2719
  } else if (discoverResult.type === "error") {
2750
- let {
2751
- error
2752
- } = handleDiscoverRouteError(path, discoverResult);
2753
- setFetcherError(key, routeId, error, {
2720
+ setFetcherError(key, routeId, discoverResult.error, {
2754
2721
  flushSync
2755
2722
  });
2756
2723
  return;
@@ -2930,10 +2897,7 @@
2930
2897
  if (discoverResult.type === "aborted") {
2931
2898
  return;
2932
2899
  } else if (discoverResult.type === "error") {
2933
- let {
2934
- error
2935
- } = handleDiscoverRouteError(path, discoverResult);
2936
- setFetcherError(key, routeId, error, {
2900
+ setFetcherError(key, routeId, discoverResult.error, {
2937
2901
  flushSync
2938
2902
  });
2939
2903
  return;
@@ -3306,12 +3270,12 @@
3306
3270
  blockers
3307
3271
  });
3308
3272
  }
3309
- function shouldBlockNavigation(_ref4) {
3273
+ function shouldBlockNavigation(_ref2) {
3310
3274
  let {
3311
3275
  currentLocation,
3312
3276
  nextLocation,
3313
3277
  historyAction
3314
- } = _ref4;
3278
+ } = _ref2;
3315
3279
  if (blockerFunctions.size === 0) {
3316
3280
  return;
3317
3281
  }
@@ -3355,16 +3319,6 @@
3355
3319
  error
3356
3320
  };
3357
3321
  }
3358
- function handleDiscoverRouteError(pathname, discoverResult) {
3359
- return {
3360
- boundaryId: findNearestBoundary(discoverResult.partialMatches).route.id,
3361
- error: getInternalRouterError(400, {
3362
- type: "route-discovery",
3363
- pathname,
3364
- message: discoverResult.error != null && "message" in discoverResult.error ? discoverResult.error : String(discoverResult.error)
3365
- })
3366
- };
3367
- }
3368
3322
 
3369
3323
  // Opt in to capturing and reporting scroll positions during navigations,
3370
3324
  // used by the <ScrollRestoration> component
@@ -3441,12 +3395,26 @@
3441
3395
  };
3442
3396
  }
3443
3397
  async function discoverRoutes(matches, pathname, signal) {
3398
+ if (!patchRoutesOnNavigationImpl) {
3399
+ return {
3400
+ type: "success",
3401
+ matches
3402
+ };
3403
+ }
3444
3404
  let partialMatches = matches;
3445
3405
  while (true) {
3446
3406
  let isNonHMR = inFlightDataRoutes == null;
3447
3407
  let routesToUse = inFlightDataRoutes || dataRoutes;
3408
+ let localManifest = manifest;
3448
3409
  try {
3449
- await loadLazyRouteChildren(patchRoutesOnNavigationImpl, pathname, partialMatches, routesToUse, manifest, mapRouteProperties, pendingPatchRoutes, signal);
3410
+ await patchRoutesOnNavigationImpl({
3411
+ path: pathname,
3412
+ matches: partialMatches,
3413
+ patch: (routeId, children) => {
3414
+ if (signal.aborted) return;
3415
+ patchRoutesImpl(routeId, children, routesToUse, localManifest, mapRouteProperties);
3416
+ }
3417
+ });
3450
3418
  } catch (e) {
3451
3419
  return {
3452
3420
  type: "error",
@@ -3460,7 +3428,7 @@
3460
3428
  // trigger a re-run of memoized `router.routes` dependencies.
3461
3429
  // HMR will already update the identity and reflow when it lands
3462
3430
  // `inFlightDataRoutes` in `completeNavigation`
3463
- if (isNonHMR) {
3431
+ if (isNonHMR && !signal.aborted) {
3464
3432
  dataRoutes = [...dataRoutes];
3465
3433
  }
3466
3434
  }
@@ -4058,8 +4026,8 @@
4058
4026
  }
4059
4027
  let text = typeof opts.body === "string" ? opts.body : opts.body instanceof FormData || opts.body instanceof URLSearchParams ?
4060
4028
  // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#plain-text-form-data
4061
- Array.from(opts.body.entries()).reduce((acc, _ref5) => {
4062
- let [name, value] = _ref5;
4029
+ Array.from(opts.body.entries()).reduce((acc, _ref3) => {
4030
+ let [name, value] = _ref3;
4063
4031
  return "" + acc + name + "=" + value + "\n";
4064
4032
  }, "") : String(opts.body);
4065
4033
  return {
@@ -4149,26 +4117,37 @@
4149
4117
  };
4150
4118
  }
4151
4119
 
4152
- // Filter out all routes below any caught error as they aren't going to
4120
+ // Filter out all routes at/below any caught error as they aren't going to
4153
4121
  // render so we don't need to load them
4154
- function getLoaderMatchesUntilBoundary(matches, boundaryId) {
4155
- let boundaryMatches = matches;
4156
- if (boundaryId) {
4157
- let index = matches.findIndex(m => m.route.id === boundaryId);
4158
- if (index >= 0) {
4159
- boundaryMatches = matches.slice(0, index);
4160
- }
4122
+ function getLoaderMatchesUntilBoundary(matches, boundaryId, includeBoundary) {
4123
+ if (includeBoundary === void 0) {
4124
+ includeBoundary = false;
4161
4125
  }
4162
- return boundaryMatches;
4126
+ let index = matches.findIndex(m => m.route.id === boundaryId);
4127
+ if (index >= 0) {
4128
+ return matches.slice(0, includeBoundary ? index + 1 : index);
4129
+ }
4130
+ return matches;
4163
4131
  }
4164
- function getMatchesToLoad(history, state, matches, submission, location, isInitialLoad, isRevalidationRequired, cancelledFetcherLoads, fetchersQueuedForDeletion, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, pendingActionResult) {
4132
+ function getMatchesToLoad(history, state, matches, submission, location, initialHydration, isRevalidationRequired, cancelledFetcherLoads, fetchersQueuedForDeletion, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, pendingActionResult) {
4165
4133
  let actionResult = pendingActionResult ? isErrorResult(pendingActionResult[1]) ? pendingActionResult[1].error : pendingActionResult[1].data : undefined;
4166
4134
  let currentUrl = history.createURL(state.location);
4167
4135
  let nextUrl = history.createURL(location);
4168
4136
 
4169
4137
  // Pick navigation matches that are net-new or qualify for revalidation
4170
- let boundaryId = pendingActionResult && isErrorResult(pendingActionResult[1]) ? pendingActionResult[0] : undefined;
4171
- let boundaryMatches = boundaryId ? getLoaderMatchesUntilBoundary(matches, boundaryId) : matches;
4138
+ let boundaryMatches = matches;
4139
+ if (initialHydration && state.errors) {
4140
+ // On initial hydration, only consider matches up to _and including_ the boundary.
4141
+ // This is inclusive to handle cases where a server loader ran successfully,
4142
+ // a child server loader bubbled up to this route, but this route has
4143
+ // `clientLoader.hydrate` so we want to still run the `clientLoader` so that
4144
+ // we have a complete version of `loaderData`
4145
+ boundaryMatches = getLoaderMatchesUntilBoundary(matches, Object.keys(state.errors)[0], true);
4146
+ } else if (pendingActionResult && isErrorResult(pendingActionResult[1])) {
4147
+ // If an action threw an error, we call loaders up to, but not including the
4148
+ // boundary
4149
+ boundaryMatches = getLoaderMatchesUntilBoundary(matches, pendingActionResult[0]);
4150
+ }
4172
4151
 
4173
4152
  // Don't revalidate loaders by default after action 4xx/5xx responses
4174
4153
  // when the flag is enabled. They can still opt-into revalidation via
@@ -4186,13 +4165,8 @@
4186
4165
  if (route.loader == null) {
4187
4166
  return false;
4188
4167
  }
4189
- if (isInitialLoad) {
4190
- if (typeof route.loader !== "function" || route.loader.hydrate) {
4191
- return true;
4192
- }
4193
- return !state.loaderData.hasOwnProperty(route.id) && (
4194
- // Don't re-run if the loader ran and threw an error
4195
- !state.errors || state.errors[route.id] === undefined);
4168
+ if (initialHydration) {
4169
+ return shouldLoadRouteOnHydration(route, state.loaderData, state.errors);
4196
4170
  }
4197
4171
 
4198
4172
  // Always call the loader on new route instances
@@ -4226,10 +4200,11 @@
4226
4200
  let revalidatingFetchers = [];
4227
4201
  fetchLoadMatches.forEach((f, key) => {
4228
4202
  // Don't revalidate:
4229
- // - on initial load (shouldn't be any fetchers then anyway)
4230
- // - if fetcher no longer matches the URL
4231
- // - if fetcher was unmounted
4232
- if (isInitialLoad || !matches.some(m => m.route.id === f.routeId) || fetchersQueuedForDeletion.has(key)) {
4203
+ // - on initial hydration (shouldn't be any fetchers then anyway)
4204
+ // - if fetcher won't be present in the subsequent render
4205
+ // - no longer matches the URL (v7_fetcherPersist=false)
4206
+ // - was unmounted but persisted due to v7_fetcherPersist=true
4207
+ if (initialHydration || !matches.some(m => m.route.id === f.routeId) || fetchersQueuedForDeletion.has(key)) {
4233
4208
  return;
4234
4209
  }
4235
4210
  let fetcherMatches = matchRoutes(routesToUse, f.path, basename);
@@ -4295,6 +4270,32 @@
4295
4270
  });
4296
4271
  return [navigationMatches, revalidatingFetchers];
4297
4272
  }
4273
+ function shouldLoadRouteOnHydration(route, loaderData, errors) {
4274
+ // We dunno if we have a loader - gotta find out!
4275
+ if (route.lazy) {
4276
+ return true;
4277
+ }
4278
+
4279
+ // No loader, nothing to initialize
4280
+ if (!route.loader) {
4281
+ return false;
4282
+ }
4283
+ let hasData = loaderData != null && loaderData[route.id] !== undefined;
4284
+ let hasError = errors != null && errors[route.id] !== undefined;
4285
+
4286
+ // Don't run if we error'd during SSR
4287
+ if (!hasData && hasError) {
4288
+ return false;
4289
+ }
4290
+
4291
+ // Explicitly opting-in to running on hydration
4292
+ if (typeof route.loader === "function" && route.loader.hydrate === true) {
4293
+ return true;
4294
+ }
4295
+
4296
+ // Otherwise, run if we're not yet initialized with anything
4297
+ return !hasData && !hasError;
4298
+ }
4298
4299
  function isNewLoader(currentLoaderData, currentMatch, match) {
4299
4300
  let isNew =
4300
4301
  // [a] -> [a, b]
@@ -4328,34 +4329,6 @@
4328
4329
  }
4329
4330
  return arg.defaultShouldRevalidate;
4330
4331
  }
4331
-
4332
- /**
4333
- * Idempotent utility to execute patchRoutesOnNavigation() to lazily load route
4334
- * definitions and update the routes/routeManifest
4335
- */
4336
- async function loadLazyRouteChildren(patchRoutesOnNavigationImpl, path, matches, routes, manifest, mapRouteProperties, pendingRouteChildren, signal) {
4337
- let key = [path, ...matches.map(m => m.route.id)].join("-");
4338
- try {
4339
- let pending = pendingRouteChildren.get(key);
4340
- if (!pending) {
4341
- pending = patchRoutesOnNavigationImpl({
4342
- path,
4343
- matches,
4344
- patch: (routeId, children) => {
4345
- if (!signal.aborted) {
4346
- patchRoutesImpl(routeId, children, routes, manifest, mapRouteProperties);
4347
- }
4348
- }
4349
- });
4350
- pendingRouteChildren.set(key, pending);
4351
- }
4352
- if (pending && isPromise(pending)) {
4353
- await pending;
4354
- }
4355
- } finally {
4356
- pendingRouteChildren.delete(key);
4357
- }
4358
- }
4359
4332
  function patchRoutesImpl(routeId, children, routesToUse, manifest, mapRouteProperties) {
4360
4333
  var _childrenToPatch;
4361
4334
  let childrenToPatch;
@@ -4456,10 +4429,10 @@
4456
4429
  }
4457
4430
 
4458
4431
  // Default implementation of `dataStrategy` which fetches all loaders in parallel
4459
- async function defaultDataStrategy(_ref6) {
4432
+ async function defaultDataStrategy(_ref4) {
4460
4433
  let {
4461
4434
  matches
4462
- } = _ref6;
4435
+ } = _ref4;
4463
4436
  let matchesToLoad = matches.filter(m => m.shouldLoad);
4464
4437
  let results = await Promise.all(matchesToLoad.map(m => m.resolve()));
4465
4438
  return results.reduce((acc, result, i) => Object.assign(acc, {
@@ -4902,11 +4875,11 @@
4902
4875
  }
4903
4876
  function mergeLoaderData(loaderData, newLoaderData, matches, errors) {
4904
4877
  // Start with all new entries that are not being reset
4905
- let mergedLoaderData = Object.entries(newLoaderData).filter(_ref7 => {
4906
- let [, v] = _ref7;
4878
+ let mergedLoaderData = Object.entries(newLoaderData).filter(_ref5 => {
4879
+ let [, v] = _ref5;
4907
4880
  return v !== ResetLoaderDataSymbol;
4908
- }).reduce((merged, _ref8) => {
4909
- let [k, v] = _ref8;
4881
+ }).reduce((merged, _ref6) => {
4882
+ let [k, v] = _ref6;
4910
4883
  merged[k] = v;
4911
4884
  return merged;
4912
4885
  }, {});
@@ -4973,9 +4946,7 @@
4973
4946
  let errorMessage = "Unknown @remix-run/router error";
4974
4947
  if (status === 400) {
4975
4948
  statusText = "Bad Request";
4976
- if (type === "route-discovery") {
4977
- errorMessage = "Unable to match URL \"" + pathname + "\" - the `patchRoutesOnNavigation()` " + ("function threw the following error:\n" + message);
4978
- } else if (method && pathname && routeId) {
4949
+ if (method && pathname && routeId) {
4979
4950
  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.";
4980
4951
  } else if (type === "invalid-body") {
4981
4952
  errorMessage = "Unable to encode submission body";
@@ -5035,9 +5006,6 @@
5035
5006
  // /page#hash -> /page
5036
5007
  return false;
5037
5008
  }
5038
- function isPromise(val) {
5039
- return typeof val === "object" && val != null && "then" in val;
5040
- }
5041
5009
  function isDataStrategyResult(result) {
5042
5010
  return result != null && typeof result === "object" && "type" in result && "result" in result && (result.type === ResultType.data || result.type === ResultType.error);
5043
5011
  }
@@ -6012,7 +5980,7 @@
6012
5980
  return DataRouterStateHook;
6013
5981
  }(DataRouterStateHook$1 || {});
6014
5982
  function getDataRouterConsoleError$1(hookName) {
6015
- return hookName + " must be used within a data router. See https://reactrouter.com/routers/picking-a-router.";
5983
+ return hookName + " must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.";
6016
5984
  }
6017
5985
  function useDataRouterContext$2(hookName) {
6018
5986
  let ctx = React__namespace.useContext(DataRouterContext);
@@ -8482,8 +8450,10 @@
8482
8450
  handle: routeModule.handle,
8483
8451
  shouldRevalidate: needsRevalidation ? wrapShouldRevalidateForHdr(route.id, routeModule.shouldRevalidate, needsRevalidation) : routeModule.shouldRevalidate
8484
8452
  }));
8485
- let initialData = initialState == null || (_initialState$loaderD = initialState.loaderData) == null ? void 0 : _initialState$loaderD[route.id];
8486
- let initialError = initialState == null || (_initialState$errors = initialState.errors) == null ? void 0 : _initialState$errors[route.id];
8453
+ let hasInitialData = initialState && initialState.loaderData && route.id in initialState.loaderData;
8454
+ let initialData = hasInitialData ? initialState == null || (_initialState$loaderD = initialState.loaderData) == null ? void 0 : _initialState$loaderD[route.id] : undefined;
8455
+ let hasInitialError = initialState && initialState.errors && route.id in initialState.errors;
8456
+ let initialError = hasInitialError ? initialState == null || (_initialState$errors = initialState.errors) == null ? void 0 : _initialState$errors[route.id] : undefined;
8487
8457
  let isHydrationRequest = needsRevalidation == null && (((_routeModule$clientLo = routeModule.clientLoader) == null ? void 0 : _routeModule$clientLo.hydrate) === true || !route.hasLoader);
8488
8458
  dataRoute.loader = async (_ref, singleFetch) => {
8489
8459
  let {
@@ -8506,10 +8476,12 @@
8506
8476
 
8507
8477
  // On the first call, resolve with the server result
8508
8478
  if (isHydrationRequest) {
8509
- if (initialError !== undefined) {
8479
+ if (hasInitialData) {
8480
+ return initialData;
8481
+ }
8482
+ if (hasInitialError) {
8510
8483
  throw initialError;
8511
8484
  }
8512
- return initialData;
8513
8485
  }
8514
8486
 
8515
8487
  // Call the server loader for client-side navigations
@@ -10123,7 +10095,7 @@
10123
10095
  return DataRouterStateHook;
10124
10096
  }(DataRouterStateHook || {}); // Internal hooks
10125
10097
  function getDataRouterConsoleError(hookName) {
10126
- return hookName + " must be used within a data router. See https://reactrouter.com/routers/picking-a-router.";
10098
+ return hookName + " must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.";
10127
10099
  }
10128
10100
  function useDataRouterContext(hookName) {
10129
10101
  let ctx = React__namespace.useContext(DataRouterContext);
@@ -11665,6 +11637,13 @@
11665
11637
  return new Request(url.href, init);
11666
11638
  }
11667
11639
 
11640
+ function invariant(value, message) {
11641
+ if (value === false || value === null || typeof value === "undefined") {
11642
+ console.error("The following error is a bug in Remix; please open an issue! https://github.com/remix-run/remix/issues/new");
11643
+ throw new Error(message);
11644
+ }
11645
+ }
11646
+
11668
11647
  // NOTE: make sure to change the Route in remix-react/react-router-dev if you change this
11669
11648
 
11670
11649
  // NOTE: make sure to change the EntryRoute in react-router/react-router-dev if you change this
@@ -11712,7 +11691,29 @@
11712
11691
  path: route.path,
11713
11692
  // Need to use RR's version in the param typed here to permit the optional
11714
11693
  // context even though we know it'll always be provided in remix
11715
- loader: route.module.loader ? args => callRouteHandler(route.module.loader, args) : undefined,
11694
+ loader: route.module.loader ? async args => {
11695
+ // If we're prerendering, use the data passed in from prerendering
11696
+ // the .data route so we dom't call loaders twice
11697
+ if (args.request.headers.has("X-React-Router-Prerender-Data")) {
11698
+ let encoded = args.request.headers.get("X-React-Router-Prerender-Data");
11699
+ !encoded ? invariant(false, "Missing prerendered data for route") : void 0;
11700
+ let uint8array = new TextEncoder().encode(encoded);
11701
+ let stream = new ReadableStream({
11702
+ start(controller) {
11703
+ controller.enqueue(uint8array);
11704
+ controller.close();
11705
+ }
11706
+ });
11707
+ let decoded = await decodeViaTurboStream(stream, global);
11708
+ let data = decoded.value;
11709
+ !(data && route.id in data) ? invariant(false, "Unable to decode prerendered data") : void 0;
11710
+ let result = data[route.id];
11711
+ !("data" in result) ? invariant(false, "Unable to process prerendered data") : void 0;
11712
+ return result.data;
11713
+ }
11714
+ let val = await callRouteHandler(route.module.loader, args);
11715
+ return val;
11716
+ } : undefined,
11716
11717
  action: route.module.action ? args => callRouteHandler(route.module.action, args) : undefined,
11717
11718
  handle: route.module.handle
11718
11719
  };
@@ -12092,13 +12093,6 @@
12092
12093
  });
12093
12094
  }
12094
12095
 
12095
- function invariant(value, message) {
12096
- if (value === false || value === null || typeof value === "undefined") {
12097
- console.error("The following error is a bug in Remix; please open an issue! https://github.com/remix-run/remix/issues/new");
12098
- throw new Error(message);
12099
- }
12100
- }
12101
-
12102
12096
  function derive(build, mode) {
12103
12097
  let routes = createRoutes(build.routes);
12104
12098
  let dataRoutes = createStaticHandlerDataRoutes(build.routes, build.future);