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

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 (46) hide show
  1. package/CHANGELOG.md +50 -2
  2. package/dist/dom-export.mjs +11 -3
  3. package/dist/dom-export.mjs.map +1 -1
  4. package/dist/index.d.ts +3 -4
  5. package/dist/index.mjs +1504 -356
  6. package/dist/index.mjs.map +1 -1
  7. package/dist/lib/components.d.ts +6 -6
  8. package/dist/lib/dom/global.d.ts +1 -1
  9. package/dist/lib/dom/lib.d.ts +3 -2
  10. package/dist/lib/dom/ssr/components.d.ts +0 -1
  11. package/dist/lib/dom/ssr/data.d.ts +0 -5
  12. package/dist/lib/dom/ssr/entry.d.ts +2 -1
  13. package/dist/lib/dom/ssr/routeModules.d.ts +64 -22
  14. package/dist/lib/dom/ssr/routes.d.ts +2 -5
  15. package/dist/lib/hooks.d.ts +4 -3
  16. package/dist/lib/router/router.d.ts +4 -0
  17. package/dist/lib/router/utils.d.ts +1 -9
  18. package/dist/lib/server-runtime/build.d.ts +1 -1
  19. package/dist/lib/server-runtime/cookies.d.ts +5 -5
  20. package/dist/lib/server-runtime/data.d.ts +1 -5
  21. package/dist/lib/server-runtime/routeModules.d.ts +3 -175
  22. package/dist/lib/server-runtime/routes.d.ts +2 -22
  23. package/dist/lib/server-runtime/sessions.d.ts +4 -4
  24. package/dist/lib/types.d.ts +21 -9
  25. package/dist/lib/types.mjs +1 -1
  26. package/dist/main-dom-export.js +1 -1
  27. package/dist/main.js +1 -1
  28. package/dist/react-router-dom.development.js +3 -3
  29. package/dist/react-router-dom.development.js.map +1 -1
  30. package/dist/react-router-dom.production.min.js +2 -2
  31. package/dist/react-router-dom.production.min.js.map +1 -1
  32. package/dist/react-router.development.js +318 -295
  33. package/dist/react-router.development.js.map +1 -1
  34. package/dist/react-router.production.min.js +2 -2
  35. package/dist/react-router.production.min.js.map +1 -1
  36. package/dist/umd/react-router-dom.development.js +3 -3
  37. package/dist/umd/react-router-dom.development.js.map +1 -1
  38. package/dist/umd/react-router-dom.production.min.js +2 -2
  39. package/dist/umd/react-router-dom.production.min.js.map +1 -1
  40. package/dist/umd/react-router.development.js +327 -317
  41. package/dist/umd/react-router.development.js.map +1 -1
  42. package/dist/umd/react-router.production.min.js +2 -2
  43. package/dist/umd/react-router.production.min.js.map +1 -1
  44. package/package.json +4 -4
  45. package/dist/lib/server-runtime/jsonify.d.ts +0 -33
  46. package/dist/lib/server-runtime/responses.d.ts +0 -37
@@ -1,5 +1,5 @@
1
1
  /**
2
- * React Router v7.0.0-pre.0
2
+ * React Router v7.0.0-pre.2
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -250,7 +250,7 @@ function createHashHistory(options = {}) {
250
250
  pathname = "/",
251
251
  search = "",
252
252
  hash = ""
253
- } = parsePath(window.location.hash.substr(1));
253
+ } = parsePath(window.location.hash.substring(1));
254
254
 
255
255
  // Hash URL should always have a leading / just like window.location.pathname
256
256
  // does, so if an app ends up at a route like /#something then we add a
@@ -313,7 +313,7 @@ function warning(cond, message) {
313
313
  }
314
314
  }
315
315
  function createKey$1() {
316
- return Math.random().toString(36).substr(2, 8);
316
+ return Math.random().toString(36).substring(2, 10);
317
317
  }
318
318
 
319
319
  /**
@@ -371,13 +371,13 @@ function parsePath(path) {
371
371
  if (path) {
372
372
  let hashIndex = path.indexOf("#");
373
373
  if (hashIndex >= 0) {
374
- parsedPath.hash = path.substr(hashIndex);
375
- path = path.substr(0, hashIndex);
374
+ parsedPath.hash = path.substring(hashIndex);
375
+ path = path.substring(0, hashIndex);
376
376
  }
377
377
  let searchIndex = path.indexOf("?");
378
378
  if (searchIndex >= 0) {
379
- parsedPath.search = path.substr(searchIndex);
380
- path = path.substr(0, searchIndex);
379
+ parsedPath.search = path.substring(searchIndex);
380
+ path = path.substring(0, searchIndex);
381
381
  }
382
382
  if (path) {
383
383
  parsedPath.pathname = path;
@@ -1237,25 +1237,6 @@ const normalizeSearch = search => !search || search === "?" ? "" : search.starts
1237
1237
  * @private
1238
1238
  */
1239
1239
  const normalizeHash = hash => !hash || hash === "#" ? "" : hash.startsWith("#") ? hash : "#" + hash;
1240
- /**
1241
- * This is a shortcut for creating `application/json` responses. Converts `data`
1242
- * to JSON and sets the `Content-Type` header.
1243
- *
1244
- * @category Utils
1245
- */
1246
- const json$1 = (data, init = {}) => {
1247
- let responseInit = typeof init === "number" ? {
1248
- status: init
1249
- } : init;
1250
- let headers = new Headers(responseInit.headers);
1251
- if (!headers.has("Content-Type")) {
1252
- headers.set("Content-Type", "application/json; charset=utf-8");
1253
- }
1254
- return new Response(JSON.stringify(data), {
1255
- ...responseInit,
1256
- headers
1257
- });
1258
- };
1259
1240
  class DataWithResponseInit {
1260
1241
  type = "DataWithResponseInit";
1261
1242
  constructor(data, init) {
@@ -1275,6 +1256,10 @@ function data(data, init) {
1275
1256
  status: init
1276
1257
  } : init);
1277
1258
  }
1259
+
1260
+ // This is now only used by the Await component and will eventually probably
1261
+ // go away in favor of the format used by `React.use`
1262
+
1278
1263
  /**
1279
1264
  * A redirect response. Sets the status code and the `Location` header.
1280
1265
  * Defaults to "302 Found".
@@ -1458,7 +1443,7 @@ const validMutationMethodsArr = ["POST", "PUT", "PATCH", "DELETE"];
1458
1443
  const validMutationMethods = new Set(validMutationMethodsArr);
1459
1444
  const validRequestMethodsArr = ["GET", ...validMutationMethodsArr];
1460
1445
  const validRequestMethods = new Set(validRequestMethodsArr);
1461
- const redirectStatusCodes$1 = new Set([301, 302, 303, 307, 308]);
1446
+ const redirectStatusCodes = new Set([301, 302, 303, 307, 308]);
1462
1447
  const redirectPreserveMethodStatusCodes = new Set([307, 308]);
1463
1448
  const IDLE_NAVIGATION = {
1464
1449
  state: "idle",
@@ -1596,25 +1581,12 @@ function createRouter(init) {
1596
1581
  // were marked for explicit hydration
1597
1582
  let loaderData = init.hydrationData ? init.hydrationData.loaderData : null;
1598
1583
  let errors = init.hydrationData ? init.hydrationData.errors : null;
1599
- let isRouteInitialized = m => {
1600
- // No loader, nothing to initialize
1601
- if (!m.route.loader) {
1602
- return true;
1603
- }
1604
- // Explicitly opting-in to running on hydration
1605
- if (typeof m.route.loader === "function" && m.route.loader.hydrate === true) {
1606
- return false;
1607
- }
1608
- // Otherwise, initialized if hydrated with data or an error
1609
- return loaderData && loaderData[m.route.id] !== undefined || errors && errors[m.route.id] !== undefined;
1610
- };
1611
-
1612
1584
  // If errors exist, don't consider routes below the boundary
1613
1585
  if (errors) {
1614
1586
  let idx = initialMatches.findIndex(m => errors[m.route.id] !== undefined);
1615
- initialized = initialMatches.slice(0, idx + 1).every(isRouteInitialized);
1587
+ initialized = initialMatches.slice(0, idx + 1).every(m => !shouldLoadRouteOnHydration(m.route, loaderData, errors));
1616
1588
  } else {
1617
- initialized = initialMatches.every(isRouteInitialized);
1589
+ initialized = initialMatches.every(m => !shouldLoadRouteOnHydration(m.route, loaderData, errors));
1618
1590
  }
1619
1591
  }
1620
1592
  let router;
@@ -1700,10 +1672,6 @@ function createRouter(init) {
1700
1672
  // we don't need to update UI state if they change
1701
1673
  let blockerFunctions = new Map();
1702
1674
 
1703
- // Map of pending patchRoutesOnNavigation() promises (keyed by path/matches) so
1704
- // that we only kick them off once for a given combo
1705
- let pendingPatchRoutes = new Map();
1706
-
1707
1675
  // Flag to ignore the next history update, so we can revert the URL change on
1708
1676
  // a POP navigation that was blocked by the user without touching router state
1709
1677
  let unblockBlockerHistoryUpdate = undefined;
@@ -2145,7 +2113,7 @@ function createRouter(init) {
2145
2113
  // Short circuit if it's only a hash change and not a revalidation or
2146
2114
  // mutation submission.
2147
2115
  //
2148
- // Ignore on initial page loads because since the initial load will always
2116
+ // Ignore on initial page loads because since the initial hydration will always
2149
2117
  // be "same hash". For example, on /page#hash and submit a <Form method="post">
2150
2118
  // which will default to a navigation to /page
2151
2119
  if (state.initialized && !isRevalidationRequired && isHashChangeOnly(state.location, location) && !(opts && opts.submission && isMutationMethod(opts.submission.formMethod))) {
@@ -2249,15 +2217,12 @@ function createRouter(init) {
2249
2217
  shortCircuited: true
2250
2218
  };
2251
2219
  } else if (discoverResult.type === "error") {
2252
- let {
2253
- boundaryId,
2254
- error
2255
- } = handleDiscoverRouteError(location.pathname, discoverResult);
2220
+ let boundaryId = findNearestBoundary(discoverResult.partialMatches).route.id;
2256
2221
  return {
2257
2222
  matches: discoverResult.partialMatches,
2258
2223
  pendingActionResult: [boundaryId, {
2259
2224
  type: ResultType.error,
2260
- error
2225
+ error: discoverResult.error
2261
2226
  }]
2262
2227
  };
2263
2228
  } else if (!discoverResult.matches) {
@@ -2383,15 +2348,12 @@ function createRouter(init) {
2383
2348
  shortCircuited: true
2384
2349
  };
2385
2350
  } else if (discoverResult.type === "error") {
2386
- let {
2387
- boundaryId,
2388
- error
2389
- } = handleDiscoverRouteError(location.pathname, discoverResult);
2351
+ let boundaryId = findNearestBoundary(discoverResult.partialMatches).route.id;
2390
2352
  return {
2391
2353
  matches: discoverResult.partialMatches,
2392
2354
  loaderData: {},
2393
2355
  errors: {
2394
- [boundaryId]: error
2356
+ [boundaryId]: discoverResult.error
2395
2357
  }
2396
2358
  };
2397
2359
  } else if (!discoverResult.matches) {
@@ -2516,13 +2478,12 @@ function createRouter(init) {
2516
2478
  errors
2517
2479
  } = processLoaderData(state, matches, loaderResults, pendingActionResult, revalidatingFetchers, fetcherResults);
2518
2480
 
2519
- // With "partial hydration", preserve SSR errors for routes that don't re-run
2481
+ // Preserve SSR errors during partial hydration
2520
2482
  if (initialHydration && state.errors) {
2521
- Object.entries(state.errors).filter(([id]) => !matchesToLoad.some(m => m.route.id === id)).forEach(([routeId, error]) => {
2522
- errors = Object.assign(errors || {}, {
2523
- [routeId]: error
2524
- });
2525
- });
2483
+ errors = {
2484
+ ...state.errors,
2485
+ ...errors
2486
+ };
2526
2487
  }
2527
2488
  let updatedFetchers = markFetchRedirectsDone();
2528
2489
  let didAbortFetchLoads = abortStaleFetchLoads(pendingNavigationLoadId);
@@ -2642,10 +2603,7 @@ function createRouter(init) {
2642
2603
  if (discoverResult.type === "aborted") {
2643
2604
  return;
2644
2605
  } else if (discoverResult.type === "error") {
2645
- let {
2646
- error
2647
- } = handleDiscoverRouteError(path, discoverResult);
2648
- setFetcherError(key, routeId, error, {
2606
+ setFetcherError(key, routeId, discoverResult.error, {
2649
2607
  flushSync
2650
2608
  });
2651
2609
  return;
@@ -2825,10 +2783,7 @@ function createRouter(init) {
2825
2783
  if (discoverResult.type === "aborted") {
2826
2784
  return;
2827
2785
  } else if (discoverResult.type === "error") {
2828
- let {
2829
- error
2830
- } = handleDiscoverRouteError(path, discoverResult);
2831
- setFetcherError(key, routeId, error, {
2786
+ setFetcherError(key, routeId, discoverResult.error, {
2832
2787
  flushSync
2833
2788
  });
2834
2789
  return;
@@ -3014,7 +2969,7 @@ function createRouter(init) {
3014
2969
  return dataResults;
3015
2970
  }
3016
2971
  for (let [routeId, result] of Object.entries(results)) {
3017
- if (isRedirectDataStrategyResultResult(result)) {
2972
+ if (isRedirectDataStrategyResult(result)) {
3018
2973
  let response = result.result;
3019
2974
  dataResults[routeId] = {
3020
2975
  type: ResultType.redirect,
@@ -3027,8 +2982,6 @@ function createRouter(init) {
3027
2982
  return dataResults;
3028
2983
  }
3029
2984
  async function callLoadersAndMaybeResolveData(state, matches, matchesToLoad, fetchersToLoad, request) {
3030
- state.matches;
3031
-
3032
2985
  // Kick off loaders and fetchers in parallel
3033
2986
  let loaderResultsPromise = callDataStrategy("loader", state, request, matchesToLoad, matches, null);
3034
2987
  let fetcherResultsPromise = Promise.all(fetchersToLoad.map(async f => {
@@ -3243,16 +3196,6 @@ function createRouter(init) {
3243
3196
  error
3244
3197
  };
3245
3198
  }
3246
- function handleDiscoverRouteError(pathname, discoverResult) {
3247
- return {
3248
- boundaryId: findNearestBoundary(discoverResult.partialMatches).route.id,
3249
- error: getInternalRouterError(400, {
3250
- type: "route-discovery",
3251
- pathname,
3252
- message: discoverResult.error != null && "message" in discoverResult.error ? discoverResult.error : String(discoverResult.error)
3253
- })
3254
- };
3255
- }
3256
3199
 
3257
3200
  // Opt in to capturing and reporting scroll positions during navigations,
3258
3201
  // used by the <ScrollRestoration> component
@@ -3329,12 +3272,26 @@ function createRouter(init) {
3329
3272
  };
3330
3273
  }
3331
3274
  async function discoverRoutes(matches, pathname, signal) {
3275
+ if (!patchRoutesOnNavigationImpl) {
3276
+ return {
3277
+ type: "success",
3278
+ matches
3279
+ };
3280
+ }
3332
3281
  let partialMatches = matches;
3333
3282
  while (true) {
3334
3283
  let isNonHMR = inFlightDataRoutes == null;
3335
3284
  let routesToUse = inFlightDataRoutes || dataRoutes;
3285
+ let localManifest = manifest;
3336
3286
  try {
3337
- await loadLazyRouteChildren(patchRoutesOnNavigationImpl, pathname, partialMatches, routesToUse, manifest, mapRouteProperties, pendingPatchRoutes, signal);
3287
+ await patchRoutesOnNavigationImpl({
3288
+ path: pathname,
3289
+ matches: partialMatches,
3290
+ patch: (routeId, children) => {
3291
+ if (signal.aborted) return;
3292
+ patchRoutesImpl(routeId, children, routesToUse, localManifest, mapRouteProperties);
3293
+ }
3294
+ });
3338
3295
  } catch (e) {
3339
3296
  return {
3340
3297
  type: "error",
@@ -3348,7 +3305,7 @@ function createRouter(init) {
3348
3305
  // trigger a re-run of memoized `router.routes` dependencies.
3349
3306
  // HMR will already update the identity and reflow when it lands
3350
3307
  // `inFlightDataRoutes` in `completeNavigation`
3351
- if (isNonHMR) {
3308
+ if (isNonHMR && !signal.aborted) {
3352
3309
  dataRoutes = [...dataRoutes];
3353
3310
  }
3354
3311
  }
@@ -3528,7 +3485,7 @@ function createStaticHandler$1(routes, opts) {
3528
3485
  };
3529
3486
  }
3530
3487
  let result = await queryImpl(request, location, matches, requestContext, dataStrategy || null, skipLoaderErrorBubbling === true, null);
3531
- if (isResponse$2(result)) {
3488
+ if (isResponse(result)) {
3532
3489
  return result;
3533
3490
  }
3534
3491
 
@@ -3601,7 +3558,7 @@ function createStaticHandler$1(routes, opts) {
3601
3558
  });
3602
3559
  }
3603
3560
  let result = await queryImpl(request, location, matches, requestContext, dataStrategy || null, false, match);
3604
- if (isResponse$2(result)) {
3561
+ if (isResponse(result)) {
3605
3562
  return result;
3606
3563
  }
3607
3564
  let error = result.errors ? Object.values(result.errors)[0] : undefined;
@@ -3630,7 +3587,7 @@ function createStaticHandler$1(routes, opts) {
3630
3587
  return _result;
3631
3588
  }
3632
3589
  let result = await loadRouteData(request, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, routeMatch);
3633
- return isResponse$2(result) ? result : {
3590
+ return isResponse(result) ? result : {
3634
3591
  ...result,
3635
3592
  actionData: null,
3636
3593
  actionHeaders: {}
@@ -3639,7 +3596,7 @@ function createStaticHandler$1(routes, opts) {
3639
3596
  // If the user threw/returned a Response in callLoaderOrAction for a
3640
3597
  // `queryRoute` call, we throw the `DataStrategyResult` to bail out early
3641
3598
  // and then return or throw the raw Response here accordingly
3642
- if (isDataStrategyResult(e) && isResponse$2(e.result)) {
3599
+ if (isDataStrategyResult(e) && isResponse(e.result)) {
3643
3600
  if (e.type === ResultType.error) {
3644
3601
  throw e.result;
3645
3602
  }
@@ -3647,7 +3604,7 @@ function createStaticHandler$1(routes, opts) {
3647
3604
  }
3648
3605
  // Redirects are always returned since they don't propagate to catch
3649
3606
  // boundaries
3650
- if (isRedirectResponse$1(e)) {
3607
+ if (isRedirectResponse(e)) {
3651
3608
  return e;
3652
3609
  }
3653
3610
  throw e;
@@ -3807,12 +3764,12 @@ function createStaticHandler$1(routes, opts) {
3807
3764
  return;
3808
3765
  }
3809
3766
  let result = results[match.route.id];
3810
- if (isRedirectDataStrategyResultResult(result)) {
3767
+ if (isRedirectDataStrategyResult(result)) {
3811
3768
  let response = result.result;
3812
3769
  // Throw redirects and let the server handle them with an HTTP redirect
3813
3770
  throw normalizeRelativeRoutingRedirectResponse(response, request, match.route.id, matches, basename);
3814
3771
  }
3815
- if (isResponse$2(result.result) && isRouteRequest) {
3772
+ if (isResponse(result.result) && isRouteRequest) {
3816
3773
  // For SSR single-route requests, we want to hand Responses back
3817
3774
  // directly without unwrapping
3818
3775
  throw result;
@@ -4041,26 +3998,34 @@ function normalizeNavigateOptions(isFetcher, path, opts) {
4041
3998
  };
4042
3999
  }
4043
4000
 
4044
- // Filter out all routes below any caught error as they aren't going to
4001
+ // Filter out all routes at/below any caught error as they aren't going to
4045
4002
  // render so we don't need to load them
4046
- function getLoaderMatchesUntilBoundary(matches, boundaryId) {
4047
- let boundaryMatches = matches;
4048
- if (boundaryId) {
4049
- let index = matches.findIndex(m => m.route.id === boundaryId);
4050
- if (index >= 0) {
4051
- boundaryMatches = matches.slice(0, index);
4052
- }
4003
+ function getLoaderMatchesUntilBoundary(matches, boundaryId, includeBoundary = false) {
4004
+ let index = matches.findIndex(m => m.route.id === boundaryId);
4005
+ if (index >= 0) {
4006
+ return matches.slice(0, includeBoundary ? index + 1 : index);
4053
4007
  }
4054
- return boundaryMatches;
4008
+ return matches;
4055
4009
  }
4056
- function getMatchesToLoad(history, state, matches, submission, location, isInitialLoad, isRevalidationRequired, cancelledFetcherLoads, fetchersQueuedForDeletion, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, pendingActionResult) {
4010
+ function getMatchesToLoad(history, state, matches, submission, location, initialHydration, isRevalidationRequired, cancelledFetcherLoads, fetchersQueuedForDeletion, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, pendingActionResult) {
4057
4011
  let actionResult = pendingActionResult ? isErrorResult(pendingActionResult[1]) ? pendingActionResult[1].error : pendingActionResult[1].data : undefined;
4058
4012
  let currentUrl = history.createURL(state.location);
4059
4013
  let nextUrl = history.createURL(location);
4060
4014
 
4061
4015
  // Pick navigation matches that are net-new or qualify for revalidation
4062
- let boundaryId = pendingActionResult && isErrorResult(pendingActionResult[1]) ? pendingActionResult[0] : undefined;
4063
- let boundaryMatches = boundaryId ? getLoaderMatchesUntilBoundary(matches, boundaryId) : matches;
4016
+ let boundaryMatches = matches;
4017
+ if (initialHydration && state.errors) {
4018
+ // On initial hydration, only consider matches up to _and including_ the boundary.
4019
+ // This is inclusive to handle cases where a server loader ran successfully,
4020
+ // a child server loader bubbled up to this route, but this route has
4021
+ // `clientLoader.hydrate` so we want to still run the `clientLoader` so that
4022
+ // we have a complete version of `loaderData`
4023
+ boundaryMatches = getLoaderMatchesUntilBoundary(matches, Object.keys(state.errors)[0], true);
4024
+ } else if (pendingActionResult && isErrorResult(pendingActionResult[1])) {
4025
+ // If an action threw an error, we call loaders up to, but not including the
4026
+ // boundary
4027
+ boundaryMatches = getLoaderMatchesUntilBoundary(matches, pendingActionResult[0]);
4028
+ }
4064
4029
 
4065
4030
  // Don't revalidate loaders by default after action 4xx/5xx responses
4066
4031
  // when the flag is enabled. They can still opt-into revalidation via
@@ -4078,13 +4043,8 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
4078
4043
  if (route.loader == null) {
4079
4044
  return false;
4080
4045
  }
4081
- if (isInitialLoad) {
4082
- if (typeof route.loader !== "function" || route.loader.hydrate) {
4083
- return true;
4084
- }
4085
- return !state.loaderData.hasOwnProperty(route.id) && (
4086
- // Don't re-run if the loader ran and threw an error
4087
- !state.errors || state.errors[route.id] === undefined);
4046
+ if (initialHydration) {
4047
+ return shouldLoadRouteOnHydration(route, state.loaderData, state.errors);
4088
4048
  }
4089
4049
 
4090
4050
  // Always call the loader on new route instances
@@ -4118,10 +4078,11 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
4118
4078
  let revalidatingFetchers = [];
4119
4079
  fetchLoadMatches.forEach((f, key) => {
4120
4080
  // Don't revalidate:
4121
- // - on initial load (shouldn't be any fetchers then anyway)
4122
- // - if fetcher no longer matches the URL
4123
- // - if fetcher was unmounted
4124
- if (isInitialLoad || !matches.some(m => m.route.id === f.routeId) || fetchersQueuedForDeletion.has(key)) {
4081
+ // - on initial hydration (shouldn't be any fetchers then anyway)
4082
+ // - if fetcher won't be present in the subsequent render
4083
+ // - no longer matches the URL (v7_fetcherPersist=false)
4084
+ // - was unmounted but persisted due to v7_fetcherPersist=true
4085
+ if (initialHydration || !matches.some(m => m.route.id === f.routeId) || fetchersQueuedForDeletion.has(key)) {
4125
4086
  return;
4126
4087
  }
4127
4088
  let fetcherMatches = matchRoutes(routesToUse, f.path, basename);
@@ -4187,6 +4148,32 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
4187
4148
  });
4188
4149
  return [navigationMatches, revalidatingFetchers];
4189
4150
  }
4151
+ function shouldLoadRouteOnHydration(route, loaderData, errors) {
4152
+ // We dunno if we have a loader - gotta find out!
4153
+ if (route.lazy) {
4154
+ return true;
4155
+ }
4156
+
4157
+ // No loader, nothing to initialize
4158
+ if (!route.loader) {
4159
+ return false;
4160
+ }
4161
+ let hasData = loaderData != null && loaderData[route.id] !== undefined;
4162
+ let hasError = errors != null && errors[route.id] !== undefined;
4163
+
4164
+ // Don't run if we error'd during SSR
4165
+ if (!hasData && hasError) {
4166
+ return false;
4167
+ }
4168
+
4169
+ // Explicitly opting-in to running on hydration
4170
+ if (typeof route.loader === "function" && route.loader.hydrate === true) {
4171
+ return true;
4172
+ }
4173
+
4174
+ // Otherwise, run if we're not yet initialized with anything
4175
+ return !hasData && !hasError;
4176
+ }
4190
4177
  function isNewLoader(currentLoaderData, currentMatch, match) {
4191
4178
  let isNew =
4192
4179
  // [a] -> [a, b]
@@ -4220,34 +4207,6 @@ function shouldRevalidateLoader(loaderMatch, arg) {
4220
4207
  }
4221
4208
  return arg.defaultShouldRevalidate;
4222
4209
  }
4223
-
4224
- /**
4225
- * Idempotent utility to execute patchRoutesOnNavigation() to lazily load route
4226
- * definitions and update the routes/routeManifest
4227
- */
4228
- async function loadLazyRouteChildren(patchRoutesOnNavigationImpl, path, matches, routes, manifest, mapRouteProperties, pendingRouteChildren, signal) {
4229
- let key = [path, ...matches.map(m => m.route.id)].join("-");
4230
- try {
4231
- let pending = pendingRouteChildren.get(key);
4232
- if (!pending) {
4233
- pending = patchRoutesOnNavigationImpl({
4234
- path,
4235
- matches,
4236
- patch: (routeId, children) => {
4237
- if (!signal.aborted) {
4238
- patchRoutesImpl(routeId, children, routes, manifest, mapRouteProperties);
4239
- }
4240
- }
4241
- });
4242
- pendingRouteChildren.set(key, pending);
4243
- }
4244
- if (pending && isPromise(pending)) {
4245
- await pending;
4246
- }
4247
- } finally {
4248
- pendingRouteChildren.delete(key);
4249
- }
4250
- }
4251
4210
  function patchRoutesImpl(routeId, children, routesToUse, manifest, mapRouteProperties) {
4252
4211
  let childrenToPatch;
4253
4212
  if (routeId) {
@@ -4516,7 +4475,7 @@ async function convertDataStrategyResultToDataResult(dataStrategyResult) {
4516
4475
  result,
4517
4476
  type
4518
4477
  } = dataStrategyResult;
4519
- if (isResponse$2(result)) {
4478
+ if (isResponse(result)) {
4520
4479
  let data;
4521
4480
  try {
4522
4481
  let contentType = result.headers.get("Content-Type");
@@ -4852,9 +4811,7 @@ function getInternalRouterError(status, {
4852
4811
  let errorMessage = "Unknown @remix-run/router error";
4853
4812
  if (status === 400) {
4854
4813
  statusText = "Bad Request";
4855
- if (type === "route-discovery") {
4856
- errorMessage = `Unable to match URL "${pathname}" - the \`patchRoutesOnNavigation()\` ` + `function threw the following error:\n${message}`;
4857
- } else if (method && pathname && routeId) {
4814
+ if (method && pathname && routeId) {
4858
4815
  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.`;
4859
4816
  } else if (type === "invalid-body") {
4860
4817
  errorMessage = "Unable to encode submission body";
@@ -4915,14 +4872,11 @@ function isHashChangeOnly(a, b) {
4915
4872
  // /page#hash -> /page
4916
4873
  return false;
4917
4874
  }
4918
- function isPromise(val) {
4919
- return typeof val === "object" && val != null && "then" in val;
4920
- }
4921
4875
  function isDataStrategyResult(result) {
4922
4876
  return result != null && typeof result === "object" && "type" in result && "result" in result && (result.type === ResultType.data || result.type === ResultType.error);
4923
4877
  }
4924
- function isRedirectDataStrategyResultResult(result) {
4925
- return isResponse$2(result.result) && redirectStatusCodes$1.has(result.result.status);
4878
+ function isRedirectDataStrategyResult(result) {
4879
+ return isResponse(result.result) && redirectStatusCodes.has(result.result.status);
4926
4880
  }
4927
4881
  function isErrorResult(result) {
4928
4882
  return result.type === ResultType.error;
@@ -4933,16 +4887,14 @@ function isRedirectResult(result) {
4933
4887
  function isDataWithResponseInit(value) {
4934
4888
  return typeof value === "object" && value != null && "type" in value && "data" in value && "init" in value && value.type === "DataWithResponseInit";
4935
4889
  }
4936
- function isResponse$2(value) {
4890
+ function isResponse(value) {
4937
4891
  return value != null && typeof value.status === "number" && typeof value.statusText === "string" && typeof value.headers === "object" && typeof value.body !== "undefined";
4938
4892
  }
4939
- function isRedirectResponse$1(result) {
4940
- if (!isResponse$2(result)) {
4941
- return false;
4942
- }
4943
- let status = result.status;
4944
- let location = result.headers.get("Location");
4945
- return status >= 300 && status <= 399 && location != null;
4893
+ function isRedirectStatusCode(statusCode) {
4894
+ return redirectStatusCodes.has(statusCode);
4895
+ }
4896
+ function isRedirectResponse(result) {
4897
+ return isResponse(result) && isRedirectStatusCode(result.status) && result.headers.has("Location");
4946
4898
  }
4947
4899
  function isValidMethod(method) {
4948
4900
  return validRequestMethods.has(method.toUpperCase());
@@ -5884,7 +5836,7 @@ var DataRouterStateHook$1 = /*#__PURE__*/function (DataRouterStateHook) {
5884
5836
  return DataRouterStateHook;
5885
5837
  }(DataRouterStateHook$1 || {});
5886
5838
  function getDataRouterConsoleError$1(hookName) {
5887
- return `${hookName} must be used within a data router. See https://reactrouter.com/routers/picking-a-router.`;
5839
+ return `${hookName} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`;
5888
5840
  }
5889
5841
  function useDataRouterContext$2(hookName) {
5890
5842
  let ctx = React.useContext(DataRouterContext);
@@ -6375,8 +6327,8 @@ function RouterProvider({
6375
6327
  let fetcherData = React.useRef(new Map());
6376
6328
  let setState = React.useCallback((newState, {
6377
6329
  deletedFetchers,
6378
- flushSync: flushSync,
6379
- viewTransitionOpts: viewTransitionOpts
6330
+ flushSync,
6331
+ viewTransitionOpts
6380
6332
  }) => {
6381
6333
  deletedFetchers.forEach(key => fetcherData.current.delete(key));
6382
6334
  newState.fetchers.forEach((fetcher, key) => {
@@ -7266,7 +7218,56 @@ function invariant$1(value, message) {
7266
7218
  * @see https://remix.run/route/meta
7267
7219
  */
7268
7220
 
7269
- // Loose copy from @react-router/server-runtime to avoid circular imports
7221
+ /**
7222
+ * A function that returns an array of data objects to use for rendering
7223
+ * metadata HTML tags in a route. These tags are not rendered on descendant
7224
+ * routes in the route hierarchy. In other words, they will only be rendered on
7225
+ * the route in which they are exported.
7226
+ *
7227
+ * @param Loader - The type of the current route's loader function
7228
+ * @param MatchLoaders - Mapping from a parent route's filepath to its loader
7229
+ * function type
7230
+ *
7231
+ * Note that parent route filepaths are relative to the `app/` directory.
7232
+ *
7233
+ * For example, if this meta function is for `/sales/customers/$customerId`:
7234
+ *
7235
+ * ```ts
7236
+ * // app/root.tsx
7237
+ * const loader = () => ({ hello: "world" })
7238
+ * export type Loader = typeof loader
7239
+ *
7240
+ * // app/routes/sales.tsx
7241
+ * const loader = () => ({ salesCount: 1074 })
7242
+ * export type Loader = typeof loader
7243
+ *
7244
+ * // app/routes/sales/customers.tsx
7245
+ * const loader = () => ({ customerCount: 74 })
7246
+ * export type Loader = typeof loader
7247
+ *
7248
+ * // app/routes/sales/customers/$customersId.tsx
7249
+ * import type { Loader as RootLoader } from "../../../root"
7250
+ * import type { Loader as SalesLoader } from "../../sales"
7251
+ * import type { Loader as CustomersLoader } from "../../sales/customers"
7252
+ *
7253
+ * const loader = () => ({ name: "Customer name" })
7254
+ *
7255
+ * const meta: MetaFunction<typeof loader, {
7256
+ * "root": RootLoader,
7257
+ * "routes/sales": SalesLoader,
7258
+ * "routes/sales/customers": CustomersLoader,
7259
+ * }> = ({ data, matches }) => {
7260
+ * const { name } = data
7261
+ * // ^? string
7262
+ * const { customerCount } = matches.find((match) => match.id === "routes/sales/customers").data
7263
+ * // ^? number
7264
+ * const { salesCount } = matches.find((match) => match.id === "routes/sales").data
7265
+ * // ^? number
7266
+ * const { hello } = matches.find((match) => match.id === "root").data
7267
+ * // ^? "world"
7268
+ * }
7269
+ * ```
7270
+ */
7270
7271
 
7271
7272
  /**
7272
7273
  * A React component that is rendered for a route.
@@ -7323,7 +7324,7 @@ function getKeyedLinksForMatches(matches, routeModules, manifest) {
7323
7324
  let descriptors = matches.map(match => {
7324
7325
  let module = routeModules[match.route.id];
7325
7326
  let route = manifest.routes[match.route.id];
7326
- return [route.css ? route.css.map(href => ({
7327
+ return [route && route.css ? route.css.map(href => ({
7327
7328
  rel: "stylesheet",
7328
7329
  href
7329
7330
  })) : [], module?.links?.() || []];
@@ -7403,8 +7404,12 @@ function isHtmlLinkDescriptor(object) {
7403
7404
  }
7404
7405
  async function getKeyedPrefetchLinks(matches, manifest, routeModules) {
7405
7406
  let links = await Promise.all(matches.map(async match => {
7406
- let mod = await loadRouteModule(manifest.routes[match.route.id], routeModules);
7407
- return mod.links ? mod.links() : [];
7407
+ let route = manifest.routes[match.route.id];
7408
+ if (route) {
7409
+ let mod = await loadRouteModule(route, routeModules);
7410
+ return mod.links ? mod.links() : [];
7411
+ }
7412
+ return [];
7408
7413
  }));
7409
7414
  return dedupeLinkDescriptors(links.flat(1).filter(isHtmlLinkDescriptor).filter(link => link.rel === "stylesheet" || link.rel === "preload").map(link => link.rel === "stylesheet" ? {
7410
7415
  ...link,
@@ -7418,7 +7423,6 @@ async function getKeyedPrefetchLinks(matches, manifest, routeModules) {
7418
7423
 
7419
7424
  // This is ridiculously identical to transition.ts `filterMatchesToLoad`
7420
7425
  function getNewMatchesForLinks(page, nextMatches, currentMatches, manifest, location, mode) {
7421
- let path = parsePathPatch(page);
7422
7426
  let isNew = (match, index) => {
7423
7427
  if (!currentMatches[index]) return true;
7424
7428
  return match.route.id !== currentMatches[index].route.id;
@@ -7432,42 +7436,44 @@ function getNewMatchesForLinks(page, nextMatches, currentMatches, manifest, loca
7432
7436
  currentMatches[index].route.path?.endsWith("*") && currentMatches[index].params["*"] !== match.params["*"]
7433
7437
  );
7434
7438
  };
7439
+ if (mode === "assets") {
7440
+ return nextMatches.filter((match, index) => isNew(match, index) || matchPathChanged(match, index));
7441
+ }
7435
7442
 
7436
- // NOTE: keep this mostly up-to-date w/ the transition data diff, but this
7443
+ // NOTE: keep this mostly up-to-date w/ the router data diff, but this
7437
7444
  // version doesn't care about submissions
7438
- let newMatches = mode === "data" && location.search !== path.search ?
7439
- // this is really similar to stuff in transition.ts, maybe somebody smarter
7445
+ // TODO: this is really similar to stuff in router.ts, maybe somebody smarter
7440
7446
  // than me (or in less of a hurry) can share some of it. You're the best.
7441
- nextMatches.filter((match, index) => {
7442
- let manifestRoute = manifest.routes[match.route.id];
7443
- if (!manifestRoute.hasLoader) {
7444
- return false;
7445
- }
7446
- if (isNew(match, index) || matchPathChanged(match, index)) {
7447
- return true;
7448
- }
7449
- if (match.route.shouldRevalidate) {
7450
- let routeChoice = match.route.shouldRevalidate({
7451
- currentUrl: new URL(location.pathname + location.search + location.hash, window.origin),
7452
- currentParams: currentMatches[0]?.params || {},
7453
- nextUrl: new URL(page, window.origin),
7454
- nextParams: match.params,
7455
- defaultShouldRevalidate: true
7456
- });
7457
- if (typeof routeChoice === "boolean") {
7458
- return routeChoice;
7447
+ if (mode === "data") {
7448
+ return nextMatches.filter((match, index) => {
7449
+ let manifestRoute = manifest.routes[match.route.id];
7450
+ if (!manifestRoute || !manifestRoute.hasLoader) {
7451
+ return false;
7459
7452
  }
7460
- }
7461
- return true;
7462
- }) : nextMatches.filter((match, index) => {
7463
- let manifestRoute = manifest.routes[match.route.id];
7464
- return (mode === "assets" || manifestRoute.hasLoader) && (isNew(match, index) || matchPathChanged(match, index));
7465
- });
7466
- return newMatches;
7453
+ if (isNew(match, index) || matchPathChanged(match, index)) {
7454
+ return true;
7455
+ }
7456
+ if (match.route.shouldRevalidate) {
7457
+ let routeChoice = match.route.shouldRevalidate({
7458
+ currentUrl: new URL(location.pathname + location.search + location.hash, window.origin),
7459
+ currentParams: currentMatches[0]?.params || {},
7460
+ nextUrl: new URL(page, window.origin),
7461
+ nextParams: match.params,
7462
+ defaultShouldRevalidate: true
7463
+ });
7464
+ if (typeof routeChoice === "boolean") {
7465
+ return routeChoice;
7466
+ }
7467
+ }
7468
+ return true;
7469
+ });
7470
+ }
7471
+ return [];
7467
7472
  }
7468
7473
  function getModuleLinkHrefs(matches, manifestPatch) {
7469
7474
  return dedupeHrefs(matches.map(match => {
7470
7475
  let route = manifestPatch.routes[match.route.id];
7476
+ if (!route) return [];
7471
7477
  let hrefs = [route.module];
7472
7478
  if (route.imports) {
7473
7479
  hrefs = hrefs.concat(route.imports);
@@ -7482,6 +7488,7 @@ function getModuleLinkHrefs(matches, manifestPatch) {
7482
7488
  function getCurrentPageModulePreloadHrefs(matches, manifest) {
7483
7489
  return dedupeHrefs(matches.map(match => {
7484
7490
  let route = manifest.routes[match.route.id];
7491
+ if (!route) return [];
7485
7492
  let hrefs = [route.module];
7486
7493
  if (route.imports) {
7487
7494
  hrefs = hrefs.concat(route.imports);
@@ -7520,13 +7527,6 @@ function dedupeLinkDescriptors(descriptors, preloads) {
7520
7527
  }, []);
7521
7528
  }
7522
7529
 
7523
- // https://github.com/remix-run/history/issues/897
7524
- function parsePathPatch(href) {
7525
- let path = parsePath(href);
7526
- if (path.search === undefined) path.search = "";
7527
- return path;
7528
- }
7529
-
7530
7530
  // Detect if this browser supports <link rel="preload"> (or has it enabled).
7531
7531
  // Originally added to handle the firefox `network.preload` config:
7532
7532
  // https://bugzilla.mozilla.org/show_bug.cgi?id=1847811
@@ -7564,13 +7564,6 @@ function createHtml(html) {
7564
7564
  };
7565
7565
  }
7566
7566
 
7567
- /**
7568
- * Data for a route that was returned from a `loader()`.
7569
- */
7570
-
7571
- function isResponse$1(value) {
7572
- return value != null && typeof value.status === "number" && typeof value.statusText === "string" && typeof value.headers === "object" && typeof value.body !== "undefined";
7573
- }
7574
7567
  async function createRequestInit(request) {
7575
7568
  let init = {
7576
7569
  signal: request.signal
@@ -7707,7 +7700,7 @@ async function singleFetchActionStrategy(request, matches) {
7707
7700
  });
7708
7701
  return result;
7709
7702
  });
7710
- if (isResponse$1(result.result) || isRouteErrorResponse(result.result)) {
7703
+ if (isResponse(result.result) || isRouteErrorResponse(result.result)) {
7711
7704
  return {
7712
7705
  [actionMatch.route.id]: result
7713
7706
  };
@@ -7752,6 +7745,7 @@ async function singleFetchLoaderNavigationStrategy(manifest, routeModules, route
7752
7745
  let results = {};
7753
7746
  let resolvePromise = Promise.all(matches.map(async (m, i) => m.resolve(async handler => {
7754
7747
  routeDfds[i].resolve();
7748
+ let manifestRoute = manifest.routes[m.route.id];
7755
7749
  if (!m.shouldLoad) {
7756
7750
  // If we're not yet initialized and this is the initial load, respect
7757
7751
  // `shouldLoad` because we're only dealing with `clientLoader.hydrate`
@@ -7763,7 +7757,7 @@ async function singleFetchLoaderNavigationStrategy(manifest, routeModules, route
7763
7757
  // Otherwise, we opt out if we currently have data, a `loader`, and a
7764
7758
  // `shouldRevalidate` function. This implies that the user opted out
7765
7759
  // via `shouldRevalidate`
7766
- if (m.route.id in router.state.loaderData && manifest.routes[m.route.id].hasLoader && routeModules[m.route.id]?.shouldRevalidate) {
7760
+ if (m.route.id in router.state.loaderData && manifestRoute && manifestRoute.hasLoader && routeModules[m.route.id]?.shouldRevalidate) {
7767
7761
  foundOptOutRoute = true;
7768
7762
  return;
7769
7763
  }
@@ -7771,8 +7765,8 @@ async function singleFetchLoaderNavigationStrategy(manifest, routeModules, route
7771
7765
 
7772
7766
  // When a route has a client loader, it opts out of the singular call and
7773
7767
  // calls it's server loader via `serverLoader()` using a `?_routes` param
7774
- if (manifest.routes[m.route.id].hasClientLoader) {
7775
- if (manifest.routes[m.route.id].hasLoader) {
7768
+ if (manifestRoute && manifestRoute.hasClientLoader) {
7769
+ if (manifestRoute.hasLoader) {
7776
7770
  foundOptOutRoute = true;
7777
7771
  }
7778
7772
  try {
@@ -7791,7 +7785,7 @@ async function singleFetchLoaderNavigationStrategy(manifest, routeModules, route
7791
7785
  }
7792
7786
 
7793
7787
  // Load this route on the server if it has a loader
7794
- if (manifest.routes[m.route.id].hasLoader) {
7788
+ if (manifestRoute && manifestRoute.hasLoader) {
7795
7789
  routesParams.add(m.route.id);
7796
7790
  }
7797
7791
 
@@ -8188,20 +8182,18 @@ function RemixRootDefaultHydrateFallback() {
8188
8182
  }));
8189
8183
  }
8190
8184
 
8191
- // NOTE: make sure to change the Route in server-runtime if you change this
8192
-
8193
- // NOTE: make sure to change the EntryRoute in server-runtime if you change this
8194
-
8195
8185
  // Create a map of routes by parentId to use recursively instead of
8196
8186
  // repeatedly filtering the manifest.
8197
8187
  function groupRoutesByParentId$1(manifest) {
8198
8188
  let routes = {};
8199
8189
  Object.values(manifest).forEach(route => {
8200
- let parentId = route.parentId || "";
8201
- if (!routes[parentId]) {
8202
- routes[parentId] = [];
8190
+ if (route) {
8191
+ let parentId = route.parentId || "";
8192
+ if (!routes[parentId]) {
8193
+ routes[parentId] = [];
8194
+ }
8195
+ routes[parentId].push(route);
8203
8196
  }
8204
- routes[parentId].push(route);
8205
8197
  });
8206
8198
  return routes;
8207
8199
  }
@@ -8332,10 +8324,12 @@ function createClientRoutes(manifest, routeModulesCache, initialState, isSpaMode
8332
8324
  ...dataRoute,
8333
8325
  ...getRouteComponents(route, routeModule, isSpaMode),
8334
8326
  handle: routeModule.handle,
8335
- shouldRevalidate: needsRevalidation ? wrapShouldRevalidateForHdr(route.id, routeModule.shouldRevalidate, needsRevalidation) : routeModule.shouldRevalidate
8327
+ shouldRevalidate: getShouldRevalidateFunction(routeModule, route.id, needsRevalidation)
8336
8328
  });
8337
- let initialData = initialState?.loaderData?.[route.id];
8338
- let initialError = initialState?.errors?.[route.id];
8329
+ let hasInitialData = initialState && initialState.loaderData && route.id in initialState.loaderData;
8330
+ let initialData = hasInitialData ? initialState?.loaderData?.[route.id] : undefined;
8331
+ let hasInitialError = initialState && initialState.errors && route.id in initialState.errors;
8332
+ let initialError = hasInitialError ? initialState?.errors?.[route.id] : undefined;
8339
8333
  let isHydrationRequest = needsRevalidation == null && (routeModule.clientLoader?.hydrate === true || !route.hasLoader);
8340
8334
  dataRoute.loader = async ({
8341
8335
  request,
@@ -8357,10 +8351,12 @@ function createClientRoutes(manifest, routeModulesCache, initialState, isSpaMode
8357
8351
 
8358
8352
  // On the first call, resolve with the server result
8359
8353
  if (isHydrationRequest) {
8360
- if (initialError !== undefined) {
8354
+ if (hasInitialData) {
8355
+ return initialData;
8356
+ }
8357
+ if (hasInitialError) {
8361
8358
  throw initialError;
8362
8359
  }
8363
- return initialData;
8364
8360
  }
8365
8361
 
8366
8362
  // Call the server loader for client-side navigations
@@ -8449,9 +8445,6 @@ function createClientRoutes(manifest, routeModulesCache, initialState, isSpaMode
8449
8445
  }
8450
8446
  });
8451
8447
  }
8452
- if (needsRevalidation) {
8453
- lazyRoute.shouldRevalidate = wrapShouldRevalidateForHdr(route.id, mod.shouldRevalidate, needsRevalidation);
8454
- }
8455
8448
  return {
8456
8449
  ...(lazyRoute.loader ? {
8457
8450
  loader: lazyRoute.loader
@@ -8460,7 +8453,7 @@ function createClientRoutes(manifest, routeModulesCache, initialState, isSpaMode
8460
8453
  action: lazyRoute.action
8461
8454
  } : {}),
8462
8455
  hasErrorBoundary: lazyRoute.hasErrorBoundary,
8463
- shouldRevalidate: lazyRoute.shouldRevalidate,
8456
+ shouldRevalidate: getShouldRevalidateFunction(lazyRoute, route.id, needsRevalidation),
8464
8457
  handle: lazyRoute.handle,
8465
8458
  // No need to wrap these in layout since the root route is never
8466
8459
  // loaded via route.lazy()
@@ -8474,6 +8467,23 @@ function createClientRoutes(manifest, routeModulesCache, initialState, isSpaMode
8474
8467
  return dataRoute;
8475
8468
  });
8476
8469
  }
8470
+ function getShouldRevalidateFunction(route, routeId, needsRevalidation) {
8471
+ // During HDR we force revalidation for updated routes
8472
+ if (needsRevalidation) {
8473
+ return wrapShouldRevalidateForHdr(routeId, route.shouldRevalidate, needsRevalidation);
8474
+ }
8475
+
8476
+ // Single fetch revalidates by default, so override the RR default value which
8477
+ // matches the multi-fetch behavior with `true`
8478
+ if (route.shouldRevalidate) {
8479
+ let fn = route.shouldRevalidate;
8480
+ return opts => fn({
8481
+ ...opts,
8482
+ defaultShouldRevalidate: true
8483
+ });
8484
+ }
8485
+ return route.shouldRevalidate;
8486
+ }
8477
8487
 
8478
8488
  // When an HMR / HDR update happens we opt out of all user-defined
8479
8489
  // revalidation logic and force a revalidation on the first call
@@ -8674,9 +8684,12 @@ async function fetchAndApplyManifestPatches(paths, manifest, routeModules, isSpa
8674
8684
 
8675
8685
  // Patch routes we don't know about yet into the manifest
8676
8686
  let knownRoutes = new Set(Object.keys(manifest.routes));
8677
- let patches = Object.values(serverPatches).reduce((acc, route) => !knownRoutes.has(route.id) ? Object.assign(acc, {
8678
- [route.id]: route
8679
- }) : acc, {});
8687
+ let patches = Object.values(serverPatches).reduce((acc, route) => {
8688
+ if (route && !knownRoutes.has(route.id)) {
8689
+ acc[route.id] = route;
8690
+ }
8691
+ return acc;
8692
+ }, {});
8680
8693
  Object.assign(manifest.routes, patches);
8681
8694
 
8682
8695
  // Track discovered paths so we don't have to fetch them again
@@ -8686,7 +8699,7 @@ async function fetchAndApplyManifestPatches(paths, manifest, routeModules, isSpa
8686
8699
  // in their new children
8687
8700
  let parentIds = new Set();
8688
8701
  Object.values(patches).forEach(patch => {
8689
- if (!patch.parentId || !patches[patch.parentId]) {
8702
+ if (patch && (!patch.parentId || !patches[patch.parentId])) {
8690
8703
  parentIds.add(patch.parentId);
8691
8704
  }
8692
8705
  });
@@ -8710,8 +8723,6 @@ function debounce(callback, wait) {
8710
8723
  };
8711
8724
  }
8712
8725
 
8713
- // TODO: Temporary shim until we figure out the way to handle typings in v7
8714
-
8715
8726
  function useDataRouterContext$1() {
8716
8727
  let context = React.useContext(DataRouterContext);
8717
8728
  !context ? invariant$1(false, "You must render this element inside a <DataRouterContext.Provider> element") : void 0;
@@ -8968,12 +8979,13 @@ function PrefetchPageLinksImpl({
8968
8979
  let routesParams = new Set();
8969
8980
  let foundOptOutRoute = false;
8970
8981
  nextMatches.forEach(m => {
8971
- if (!manifest.routes[m.route.id].hasLoader) {
8982
+ let manifestRoute = manifest.routes[m.route.id];
8983
+ if (!manifestRoute || !manifestRoute.hasLoader) {
8972
8984
  return;
8973
8985
  }
8974
8986
  if (!newMatchesForData.some(m2 => m2.route.id === m.route.id) && m.route.id in loaderData && routeModules[m.route.id]?.shouldRevalidate) {
8975
8987
  foundOptOutRoute = true;
8976
- } else if (manifest.routes[m.route.id].hasClientLoader) {
8988
+ } else if (manifestRoute.hasClientLoader) {
8977
8989
  foundOptOutRoute = true;
8978
8990
  } else {
8979
8991
  routesParams.add(m.route.id);
@@ -9244,7 +9256,7 @@ import(${JSON.stringify(manifest.entry.module)});`;
9244
9256
  }, []);
9245
9257
  let routePreloads = matches.map(match => {
9246
9258
  let route = manifest.routes[match.route.id];
9247
- return (route.imports || []).concat([route.module]);
9259
+ return route ? (route.imports || []).concat([route.module]) : [];
9248
9260
  }).flat(1);
9249
9261
  let preloads = isHydrated ? [] : manifest.entry.imports.concat(routePreloads);
9250
9262
  return isHydrated ? null : /*#__PURE__*/React.createElement(React.Fragment, null, !enableFogOfWar ? /*#__PURE__*/React.createElement("link", {
@@ -9950,7 +9962,7 @@ var DataRouterStateHook = /*#__PURE__*/function (DataRouterStateHook) {
9950
9962
  return DataRouterStateHook;
9951
9963
  }(DataRouterStateHook || {}); // Internal hooks
9952
9964
  function getDataRouterConsoleError(hookName) {
9953
- return `${hookName} must be used within a data router. See https://reactrouter.com/routers/picking-a-router.`;
9965
+ return `${hookName} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`;
9954
9966
  }
9955
9967
  function useDataRouterContext(hookName) {
9956
9968
  let ctx = React.useContext(DataRouterContext);
@@ -10939,7 +10951,7 @@ function ServerRouter({
10939
10951
  // route opted into clientLoader hydration and either:
10940
10952
  // * gave us a HydrateFallback
10941
10953
  // * or doesn't have a server loader and we have no data to render
10942
- if (route && shouldHydrateRouteLoader(manifestRoute, route, context.isSpaMode) && (route.HydrateFallback || !manifestRoute.hasLoader)) {
10954
+ if (route && manifestRoute && shouldHydrateRouteLoader(manifestRoute, route, context.isSpaMode) && (route.HydrateFallback || !manifestRoute.hasLoader)) {
10943
10955
  delete context.staticHandlerContext.loaderData[routeId];
10944
10956
  }
10945
10957
  }
@@ -11152,7 +11164,17 @@ const createCookie = (name, cookieOptions = {}) => {
11152
11164
  ...options,
11153
11165
  ...parseOptions
11154
11166
  });
11155
- return name in cookies ? cookies[name] === "" ? "" : await decodeCookieValue(cookies[name], secrets) : null;
11167
+ if (name in cookies) {
11168
+ let value = cookies[name];
11169
+ if (typeof value === "string" && value !== "") {
11170
+ let decoded = await decodeCookieValue(value, secrets);
11171
+ return decoded;
11172
+ } else {
11173
+ return "";
11174
+ }
11175
+ } else {
11176
+ return null;
11177
+ }
11156
11178
  },
11157
11179
  async serialize(value, serializeOptions) {
11158
11180
  return serialize(name, value === "" ? "" : await encodeCookieValue(value, secrets), {
@@ -11262,7 +11284,10 @@ function warnOnceAboutExpiresCookie(name, expires) {
11262
11284
 
11263
11285
  function createEntryRouteModules(manifest) {
11264
11286
  return Object.keys(manifest).reduce((memo, routeId) => {
11265
- memo[routeId] = manifest[routeId].module;
11287
+ let route = manifest[routeId];
11288
+ if (route) {
11289
+ memo[routeId] = route.module;
11290
+ }
11266
11291
  return memo;
11267
11292
  }, {});
11268
11293
  }
@@ -11391,29 +11416,6 @@ function matchServerRoutes(routes, pathname, basename) {
11391
11416
  }));
11392
11417
  }
11393
11418
 
11394
- // must be a type since this is a subtype of response
11395
- // interfaces must conform to the types they extend
11396
-
11397
- /**
11398
- * This is a shortcut for creating `application/json` responses. Converts `data`
11399
- * to JSON and sets the `Content-Type` header.
11400
- *
11401
- * @see https://remix.run/utils/json
11402
- */
11403
- const json = (data, init = {}) => {
11404
- return json$1(data, init);
11405
- };
11406
- function isResponse(value) {
11407
- return value != null && typeof value.status === "number" && typeof value.statusText === "string" && typeof value.headers === "object" && typeof value.body !== "undefined";
11408
- }
11409
- const redirectStatusCodes = new Set([301, 302, 303, 307, 308]);
11410
- function isRedirectStatusCode(statusCode) {
11411
- return redirectStatusCodes.has(statusCode);
11412
- }
11413
- function isRedirectResponse(response) {
11414
- return isRedirectStatusCode(response.status);
11415
- }
11416
-
11417
11419
  /**
11418
11420
  * An object of unknown type for route loaders and actions provided by the
11419
11421
  * server's `getLoadContext()` function. This is defined as an empty interface
@@ -11421,10 +11423,6 @@ function isRedirectResponse(response) {
11421
11423
  * globally: https://www.typescriptlang.org/docs/handbook/declaration-merging.html
11422
11424
  */
11423
11425
 
11424
- /**
11425
- * Data for a route that was returned from a `loader()`.
11426
- */
11427
-
11428
11426
  // Need to use RR's version here to permit the optional context even
11429
11427
  // though we know it'll always be provided in remix
11430
11428
  async function callRouteHandler(handler, args) {
@@ -11485,18 +11483,23 @@ function stripRoutesParam(request) {
11485
11483
  return new Request(url.href, init);
11486
11484
  }
11487
11485
 
11488
- // NOTE: make sure to change the Route in remix-react/react-router-dev if you change this
11489
-
11490
- // NOTE: make sure to change the EntryRoute in react-router/react-router-dev if you change this
11486
+ function invariant(value, message) {
11487
+ if (value === false || value === null || typeof value === "undefined") {
11488
+ console.error("The following error is a bug in React Router; please open an issue! https://github.com/remix-run/react-router/issues/new/choose");
11489
+ throw new Error(message);
11490
+ }
11491
+ }
11491
11492
 
11492
11493
  function groupRoutesByParentId(manifest) {
11493
11494
  let routes = {};
11494
11495
  Object.values(manifest).forEach(route => {
11495
- let parentId = route.parentId || "";
11496
- if (!routes[parentId]) {
11497
- routes[parentId] = [];
11496
+ if (route) {
11497
+ let parentId = route.parentId || "";
11498
+ if (!routes[parentId]) {
11499
+ routes[parentId] = [];
11500
+ }
11501
+ routes[parentId].push(route);
11498
11502
  }
11499
- routes[parentId].push(route);
11500
11503
  });
11501
11504
  return routes;
11502
11505
  }
@@ -11521,7 +11524,29 @@ function createStaticHandlerDataRoutes(manifest, future, parentId = "", routesBy
11521
11524
  path: route.path,
11522
11525
  // Need to use RR's version in the param typed here to permit the optional
11523
11526
  // context even though we know it'll always be provided in remix
11524
- loader: route.module.loader ? args => callRouteHandler(route.module.loader, args) : undefined,
11527
+ loader: route.module.loader ? async args => {
11528
+ // If we're prerendering, use the data passed in from prerendering
11529
+ // the .data route so we dom't call loaders twice
11530
+ if (args.request.headers.has("X-React-Router-Prerender-Data")) {
11531
+ let encoded = args.request.headers.get("X-React-Router-Prerender-Data");
11532
+ !encoded ? invariant(false, "Missing prerendered data for route") : void 0;
11533
+ let uint8array = new TextEncoder().encode(encoded);
11534
+ let stream = new ReadableStream({
11535
+ start(controller) {
11536
+ controller.enqueue(uint8array);
11537
+ controller.close();
11538
+ }
11539
+ });
11540
+ let decoded = await decodeViaTurboStream(stream, global);
11541
+ let data = decoded.value;
11542
+ !(data && route.id in data) ? invariant(false, "Unable to decode prerendered data") : void 0;
11543
+ let result = data[route.id];
11544
+ !("data" in result) ? invariant(false, "Unable to process prerendered data") : void 0;
11545
+ return result.data;
11546
+ }
11547
+ let val = await callRouteHandler(route.module.loader, args);
11548
+ return val;
11549
+ } : undefined,
11525
11550
  action: route.module.action ? args => callRouteHandler(route.module.action, args) : undefined,
11526
11551
  handle: route.module.handle
11527
11552
  };
@@ -11598,13 +11623,15 @@ function getDocumentHeaders(build, context) {
11598
11623
  let {
11599
11624
  id
11600
11625
  } = match.route;
11601
- let routeModule = build.routes[id].module;
11626
+ let route = build.routes[id];
11627
+ !route ? invariant(false, `Route with id "${id}" not found in build`) : void 0;
11628
+ let routeModule = route.module;
11602
11629
  let loaderHeaders = context.loaderHeaders[id] || new Headers();
11603
11630
  let actionHeaders = context.actionHeaders[id] || new Headers();
11604
11631
 
11605
11632
  // Only expose errorHeaders to the leaf headers() function to
11606
11633
  // avoid duplication via parentHeaders
11607
- let includeErrorHeaders = errorHeaders != undefined && idx === matches.length - 1;
11634
+ let includeErrorHeaders = errorHeaders != null && idx === matches.length - 1;
11608
11635
  // Only prepend cookies from errorHeaders at the leaf renderable route
11609
11636
  // when it's not the same as loaderHeaders/actionHeaders to avoid
11610
11637
  // duplicate cookies
@@ -11901,13 +11928,6 @@ function encodeViaTurboStream(data, requestSignal, streamTimeout, serverMode) {
11901
11928
  });
11902
11929
  }
11903
11930
 
11904
- function invariant(value, message) {
11905
- if (value === false || value === null || typeof value === "undefined") {
11906
- console.error("The following error is a bug in Remix; please open an issue! https://github.com/remix-run/remix/issues/new");
11907
- throw new Error(message);
11908
- }
11909
- }
11910
-
11911
11931
  function derive(build, mode) {
11912
11932
  let routes = createRoutes(build.routes);
11913
11933
  let dataRoutes = createStaticHandlerDataRoutes(build.routes, build.future);
@@ -12034,15 +12054,18 @@ async function handleManifestRequest(build, routes, url) {
12034
12054
  if (matches) {
12035
12055
  for (let match of matches) {
12036
12056
  let routeId = match.route.id;
12037
- patches[routeId] = build.assets.routes[routeId];
12057
+ let route = build.assets.routes[routeId];
12058
+ if (route) {
12059
+ patches[routeId] = route;
12060
+ }
12038
12061
  }
12039
12062
  }
12040
12063
  }
12041
- return json(patches, {
12064
+ return Response.json(patches, {
12042
12065
  headers: {
12043
12066
  "Cache-Control": "public, max-age=31536000, immutable"
12044
12067
  }
12045
- }); // Override the TypedResponse stuff from json()
12068
+ });
12046
12069
  }
12047
12070
  return new Response("Invalid Request", {
12048
12071
  status: 400
@@ -12222,7 +12245,7 @@ async function handleResourceRequest(serverMode, build, staticHandler, routeId,
12222
12245
  }
12223
12246
  }
12224
12247
  function errorResponseToJson(errorResponse, serverMode) {
12225
- return json$1(serializeError(
12248
+ return Response.json(serializeError(
12226
12249
  // @ts-expect-error This is "private" from users but intended for internal use
12227
12250
  errorResponse.error || new Error("Unexpected Server Error"), serverMode), {
12228
12251
  status: errorResponse.status,
@@ -12501,5 +12524,5 @@ function deserializeErrors(errors) {
12501
12524
  return serialized;
12502
12525
  }
12503
12526
 
12504
- export { Await, BrowserRouter, Form, HashRouter, IDLE_BLOCKER, IDLE_FETCHER, IDLE_NAVIGATION, Link, Links, MemoryRouter, Meta, NavLink, Navigate, Action as NavigationType, Outlet, PrefetchPageLinks, Route, Router, RouterProvider, Routes, Scripts, ScrollRestoration, ServerRouter, StaticRouter, StaticRouterProvider, DataRouterContext as UNSAFE_DataRouterContext, DataRouterStateContext as UNSAFE_DataRouterStateContext, ErrorResponseImpl as UNSAFE_ErrorResponseImpl, FetchersContext as UNSAFE_FetchersContext, FrameworkContext as UNSAFE_FrameworkContext, LocationContext as UNSAFE_LocationContext, NavigationContext as UNSAFE_NavigationContext, RemixErrorBoundary as UNSAFE_RemixErrorBoundary, RouteContext as UNSAFE_RouteContext, ServerMode as UNSAFE_ServerMode, SingleFetchRedirectSymbol as UNSAFE_SingleFetchRedirectSymbol, ViewTransitionContext as UNSAFE_ViewTransitionContext, createBrowserHistory as UNSAFE_createBrowserHistory, createClientRoutes as UNSAFE_createClientRoutes, createClientRoutesWithHMRRevalidationOptOut as UNSAFE_createClientRoutesWithHMRRevalidationOptOut, createRouter as UNSAFE_createRouter, decodeViaTurboStream as UNSAFE_decodeViaTurboStream, deserializeErrors as UNSAFE_deserializeErrors, getPatchRoutesOnNavigationFunction as UNSAFE_getPatchRoutesOnNavigationFunction, getSingleFetchDataStrategy$1 as UNSAFE_getSingleFetchDataStrategy, invariant$2 as UNSAFE_invariant, mapRouteProperties as UNSAFE_mapRouteProperties, shouldHydrateRouteLoader as UNSAFE_shouldHydrateRouteLoader, useFogOFWarDiscovery as UNSAFE_useFogOFWarDiscovery, useScrollRestoration as UNSAFE_useScrollRestoration, createBrowserRouter, createCookie, createCookieSessionStorage, createHashRouter, createMemoryRouter, createMemorySessionStorage, createPath, createRequestHandler, createRoutesFromChildren, createRoutesFromChildren as createRoutesFromElements, createRoutesStub, createSearchParams, createSession, createSessionStorage, createStaticHandler, createStaticRouter, data, generatePath, isCookie, isRouteErrorResponse, isSession, json$1 as json, matchPath, matchRoutes, parsePath, redirect, redirectDocument, renderMatches, replace, resolvePath, HistoryRouter as unstable_HistoryRouter, setDevServerHooks as unstable_setDevServerHooks, usePrompt as unstable_usePrompt, useActionData, useAsyncError, useAsyncValue, useBeforeUnload, useBlocker, useFetcher, useFetchers, useFormAction, useHref, useInRouterContext, useLinkClickHandler, useLoaderData, useLocation, useMatch, useMatches, useNavigate, useNavigation, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRevalidator, useRouteError, useRouteLoaderData, useRoutes, useSearchParams, useSubmit, useViewTransitionState };
12527
+ export { Await, BrowserRouter, Form, HashRouter, IDLE_BLOCKER, IDLE_FETCHER, IDLE_NAVIGATION, Link, Links, MemoryRouter, Meta, NavLink, Navigate, Action as NavigationType, Outlet, PrefetchPageLinks, Route, Router, RouterProvider, Routes, Scripts, ScrollRestoration, ServerRouter, StaticRouter, StaticRouterProvider, DataRouterContext as UNSAFE_DataRouterContext, DataRouterStateContext as UNSAFE_DataRouterStateContext, ErrorResponseImpl as UNSAFE_ErrorResponseImpl, FetchersContext as UNSAFE_FetchersContext, FrameworkContext as UNSAFE_FrameworkContext, LocationContext as UNSAFE_LocationContext, NavigationContext as UNSAFE_NavigationContext, RemixErrorBoundary as UNSAFE_RemixErrorBoundary, RouteContext as UNSAFE_RouteContext, ServerMode as UNSAFE_ServerMode, SingleFetchRedirectSymbol as UNSAFE_SingleFetchRedirectSymbol, ViewTransitionContext as UNSAFE_ViewTransitionContext, createBrowserHistory as UNSAFE_createBrowserHistory, createClientRoutes as UNSAFE_createClientRoutes, createClientRoutesWithHMRRevalidationOptOut as UNSAFE_createClientRoutesWithHMRRevalidationOptOut, createRouter as UNSAFE_createRouter, decodeViaTurboStream as UNSAFE_decodeViaTurboStream, deserializeErrors as UNSAFE_deserializeErrors, getPatchRoutesOnNavigationFunction as UNSAFE_getPatchRoutesOnNavigationFunction, getSingleFetchDataStrategy$1 as UNSAFE_getSingleFetchDataStrategy, invariant$2 as UNSAFE_invariant, mapRouteProperties as UNSAFE_mapRouteProperties, shouldHydrateRouteLoader as UNSAFE_shouldHydrateRouteLoader, useFogOFWarDiscovery as UNSAFE_useFogOFWarDiscovery, useScrollRestoration as UNSAFE_useScrollRestoration, createBrowserRouter, createCookie, createCookieSessionStorage, createHashRouter, createMemoryRouter, createMemorySessionStorage, createPath, createRequestHandler, createRoutesFromChildren, createRoutesFromChildren as createRoutesFromElements, createRoutesStub, createSearchParams, createSession, createSessionStorage, createStaticHandler, createStaticRouter, data, generatePath, isCookie, isRouteErrorResponse, isSession, matchPath, matchRoutes, parsePath, redirect, redirectDocument, renderMatches, replace, resolvePath, HistoryRouter as unstable_HistoryRouter, setDevServerHooks as unstable_setDevServerHooks, usePrompt as unstable_usePrompt, useActionData, useAsyncError, useAsyncValue, useBeforeUnload, useBlocker, useFetcher, useFetchers, useFormAction, useHref, useInRouterContext, useLinkClickHandler, useLoaderData, useLocation, useMatch, useMatches, useNavigate, useNavigation, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRevalidator, useRouteError, useRouteLoaderData, useRoutes, useSearchParams, useSubmit, useViewTransitionState };
12505
12528
  //# sourceMappingURL=react-router.development.js.map