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
  *
@@ -310,7 +310,7 @@
310
310
  pathname = "/",
311
311
  search = "",
312
312
  hash = ""
313
- } = parsePath(window.location.hash.substr(1));
313
+ } = parsePath(window.location.hash.substring(1));
314
314
 
315
315
  // Hash URL should always have a leading / just like window.location.pathname
316
316
  // does, so if an app ends up at a route like /#something then we add a
@@ -373,7 +373,7 @@
373
373
  }
374
374
  }
375
375
  function createKey$1() {
376
- return Math.random().toString(36).substr(2, 8);
376
+ return Math.random().toString(36).substring(2, 10);
377
377
  }
378
378
 
379
379
  /**
@@ -435,13 +435,13 @@
435
435
  if (path) {
436
436
  let hashIndex = path.indexOf("#");
437
437
  if (hashIndex >= 0) {
438
- parsedPath.hash = path.substr(hashIndex);
439
- path = path.substr(0, hashIndex);
438
+ parsedPath.hash = path.substring(hashIndex);
439
+ path = path.substring(0, hashIndex);
440
440
  }
441
441
  let searchIndex = path.indexOf("?");
442
442
  if (searchIndex >= 0) {
443
- parsedPath.search = path.substr(searchIndex);
444
- path = path.substr(0, searchIndex);
443
+ parsedPath.search = path.substring(searchIndex);
444
+ path = path.substring(0, searchIndex);
445
445
  }
446
446
  if (path) {
447
447
  parsedPath.pathname = path;
@@ -1335,27 +1335,6 @@
1335
1335
  * @private
1336
1336
  */
1337
1337
  const normalizeHash = hash => !hash || hash === "#" ? "" : hash.startsWith("#") ? hash : "#" + hash;
1338
- /**
1339
- * This is a shortcut for creating `application/json` responses. Converts `data`
1340
- * to JSON and sets the `Content-Type` header.
1341
- *
1342
- * @category Utils
1343
- */
1344
- const json$1 = function json(data, init) {
1345
- if (init === void 0) {
1346
- init = {};
1347
- }
1348
- let responseInit = typeof init === "number" ? {
1349
- status: init
1350
- } : init;
1351
- let headers = new Headers(responseInit.headers);
1352
- if (!headers.has("Content-Type")) {
1353
- headers.set("Content-Type", "application/json; charset=utf-8");
1354
- }
1355
- return new Response(JSON.stringify(data), _extends({}, responseInit, {
1356
- headers
1357
- }));
1358
- };
1359
1338
  class DataWithResponseInit {
1360
1339
  constructor(data, init) {
1361
1340
  this.type = "DataWithResponseInit";
@@ -1375,6 +1354,10 @@
1375
1354
  status: init
1376
1355
  } : init);
1377
1356
  }
1357
+
1358
+ // This is now only used by the Await component and will eventually probably
1359
+ // go away in favor of the format used by `React.use`
1360
+
1378
1361
  /**
1379
1362
  * A redirect response. Sets the status code and the `Location` header.
1380
1363
  * Defaults to "302 Found".
@@ -1563,7 +1546,7 @@
1563
1546
  const validMutationMethods = new Set(validMutationMethodsArr);
1564
1547
  const validRequestMethodsArr = ["GET", ...validMutationMethodsArr];
1565
1548
  const validRequestMethods = new Set(validRequestMethodsArr);
1566
- const redirectStatusCodes$1 = new Set([301, 302, 303, 307, 308]);
1549
+ const redirectStatusCodes = new Set([301, 302, 303, 307, 308]);
1567
1550
  const redirectPreserveMethodStatusCodes = new Set([307, 308]);
1568
1551
  const IDLE_NAVIGATION = {
1569
1552
  state: "idle",
@@ -1699,25 +1682,12 @@
1699
1682
  // were marked for explicit hydration
1700
1683
  let loaderData = init.hydrationData ? init.hydrationData.loaderData : null;
1701
1684
  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
1685
  // If errors exist, don't consider routes below the boundary
1716
1686
  if (errors) {
1717
1687
  let idx = initialMatches.findIndex(m => errors[m.route.id] !== undefined);
1718
- initialized = initialMatches.slice(0, idx + 1).every(isRouteInitialized);
1688
+ initialized = initialMatches.slice(0, idx + 1).every(m => !shouldLoadRouteOnHydration(m.route, loaderData, errors));
1719
1689
  } else {
1720
- initialized = initialMatches.every(isRouteInitialized);
1690
+ initialized = initialMatches.every(m => !shouldLoadRouteOnHydration(m.route, loaderData, errors));
1721
1691
  }
1722
1692
  }
1723
1693
  let router;
@@ -1803,10 +1773,6 @@
1803
1773
  // we don't need to update UI state if they change
1804
1774
  let blockerFunctions = new Map();
1805
1775
 
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
1776
  // Flag to ignore the next history update, so we can revert the URL change on
1811
1777
  // a POP navigation that was blocked by the user without touching router state
1812
1778
  let unblockBlockerHistoryUpdate = undefined;
@@ -2247,7 +2213,7 @@
2247
2213
  // Short circuit if it's only a hash change and not a revalidation or
2248
2214
  // mutation submission.
2249
2215
  //
2250
- // Ignore on initial page loads because since the initial load will always
2216
+ // Ignore on initial page loads because since the initial hydration will always
2251
2217
  // be "same hash". For example, on /page#hash and submit a <Form method="post">
2252
2218
  // which will default to a navigation to /page
2253
2219
  if (state.initialized && !isRevalidationRequired && isHashChangeOnly(state.location, location) && !(opts && opts.submission && isMutationMethod(opts.submission.formMethod))) {
@@ -2354,15 +2320,12 @@
2354
2320
  shortCircuited: true
2355
2321
  };
2356
2322
  } else if (discoverResult.type === "error") {
2357
- let {
2358
- boundaryId,
2359
- error
2360
- } = handleDiscoverRouteError(location.pathname, discoverResult);
2323
+ let boundaryId = findNearestBoundary(discoverResult.partialMatches).route.id;
2361
2324
  return {
2362
2325
  matches: discoverResult.partialMatches,
2363
2326
  pendingActionResult: [boundaryId, {
2364
2327
  type: ResultType.error,
2365
- error
2328
+ error: discoverResult.error
2366
2329
  }]
2367
2330
  };
2368
2331
  } else if (!discoverResult.matches) {
@@ -2487,15 +2450,12 @@
2487
2450
  shortCircuited: true
2488
2451
  };
2489
2452
  } else if (discoverResult.type === "error") {
2490
- let {
2491
- boundaryId,
2492
- error
2493
- } = handleDiscoverRouteError(location.pathname, discoverResult);
2453
+ let boundaryId = findNearestBoundary(discoverResult.partialMatches).route.id;
2494
2454
  return {
2495
2455
  matches: discoverResult.partialMatches,
2496
2456
  loaderData: {},
2497
2457
  errors: {
2498
- [boundaryId]: error
2458
+ [boundaryId]: discoverResult.error
2499
2459
  }
2500
2460
  };
2501
2461
  } else if (!discoverResult.matches) {
@@ -2618,17 +2578,9 @@
2618
2578
  errors
2619
2579
  } = processLoaderData(state, matches, loaderResults, pendingActionResult, revalidatingFetchers, fetcherResults);
2620
2580
 
2621
- // With "partial hydration", preserve SSR errors for routes that don't re-run
2581
+ // Preserve SSR errors during partial hydration
2622
2582
  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
- });
2583
+ errors = _extends({}, state.errors, errors);
2632
2584
  }
2633
2585
  let updatedFetchers = markFetchRedirectsDone();
2634
2586
  let didAbortFetchLoads = abortStaleFetchLoads(pendingNavigationLoadId);
@@ -2747,10 +2699,7 @@
2747
2699
  if (discoverResult.type === "aborted") {
2748
2700
  return;
2749
2701
  } else if (discoverResult.type === "error") {
2750
- let {
2751
- error
2752
- } = handleDiscoverRouteError(path, discoverResult);
2753
- setFetcherError(key, routeId, error, {
2702
+ setFetcherError(key, routeId, discoverResult.error, {
2754
2703
  flushSync
2755
2704
  });
2756
2705
  return;
@@ -2930,10 +2879,7 @@
2930
2879
  if (discoverResult.type === "aborted") {
2931
2880
  return;
2932
2881
  } else if (discoverResult.type === "error") {
2933
- let {
2934
- error
2935
- } = handleDiscoverRouteError(path, discoverResult);
2936
- setFetcherError(key, routeId, error, {
2882
+ setFetcherError(key, routeId, discoverResult.error, {
2937
2883
  flushSync
2938
2884
  });
2939
2885
  return;
@@ -3119,7 +3065,7 @@
3119
3065
  return dataResults;
3120
3066
  }
3121
3067
  for (let [routeId, result] of Object.entries(results)) {
3122
- if (isRedirectDataStrategyResultResult(result)) {
3068
+ if (isRedirectDataStrategyResult(result)) {
3123
3069
  let response = result.result;
3124
3070
  dataResults[routeId] = {
3125
3071
  type: ResultType.redirect,
@@ -3132,8 +3078,6 @@
3132
3078
  return dataResults;
3133
3079
  }
3134
3080
  async function callLoadersAndMaybeResolveData(state, matches, matchesToLoad, fetchersToLoad, request) {
3135
- state.matches;
3136
-
3137
3081
  // Kick off loaders and fetchers in parallel
3138
3082
  let loaderResultsPromise = callDataStrategy("loader", state, request, matchesToLoad, matches, null);
3139
3083
  let fetcherResultsPromise = Promise.all(fetchersToLoad.map(async f => {
@@ -3306,12 +3250,12 @@
3306
3250
  blockers
3307
3251
  });
3308
3252
  }
3309
- function shouldBlockNavigation(_ref4) {
3253
+ function shouldBlockNavigation(_ref2) {
3310
3254
  let {
3311
3255
  currentLocation,
3312
3256
  nextLocation,
3313
3257
  historyAction
3314
- } = _ref4;
3258
+ } = _ref2;
3315
3259
  if (blockerFunctions.size === 0) {
3316
3260
  return;
3317
3261
  }
@@ -3355,16 +3299,6 @@
3355
3299
  error
3356
3300
  };
3357
3301
  }
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
3302
 
3369
3303
  // Opt in to capturing and reporting scroll positions during navigations,
3370
3304
  // used by the <ScrollRestoration> component
@@ -3441,12 +3375,26 @@
3441
3375
  };
3442
3376
  }
3443
3377
  async function discoverRoutes(matches, pathname, signal) {
3378
+ if (!patchRoutesOnNavigationImpl) {
3379
+ return {
3380
+ type: "success",
3381
+ matches
3382
+ };
3383
+ }
3444
3384
  let partialMatches = matches;
3445
3385
  while (true) {
3446
3386
  let isNonHMR = inFlightDataRoutes == null;
3447
3387
  let routesToUse = inFlightDataRoutes || dataRoutes;
3388
+ let localManifest = manifest;
3448
3389
  try {
3449
- await loadLazyRouteChildren(patchRoutesOnNavigationImpl, pathname, partialMatches, routesToUse, manifest, mapRouteProperties, pendingPatchRoutes, signal);
3390
+ await patchRoutesOnNavigationImpl({
3391
+ path: pathname,
3392
+ matches: partialMatches,
3393
+ patch: (routeId, children) => {
3394
+ if (signal.aborted) return;
3395
+ patchRoutesImpl(routeId, children, routesToUse, localManifest, mapRouteProperties);
3396
+ }
3397
+ });
3450
3398
  } catch (e) {
3451
3399
  return {
3452
3400
  type: "error",
@@ -3460,7 +3408,7 @@
3460
3408
  // trigger a re-run of memoized `router.routes` dependencies.
3461
3409
  // HMR will already update the identity and reflow when it lands
3462
3410
  // `inFlightDataRoutes` in `completeNavigation`
3463
- if (isNonHMR) {
3411
+ if (isNonHMR && !signal.aborted) {
3464
3412
  dataRoutes = [...dataRoutes];
3465
3413
  }
3466
3414
  }
@@ -3641,7 +3589,7 @@
3641
3589
  };
3642
3590
  }
3643
3591
  let result = await queryImpl(request, location, matches, requestContext, dataStrategy || null, skipLoaderErrorBubbling === true, null);
3644
- if (isResponse$2(result)) {
3592
+ if (isResponse(result)) {
3645
3593
  return result;
3646
3594
  }
3647
3595
 
@@ -3714,7 +3662,7 @@
3714
3662
  });
3715
3663
  }
3716
3664
  let result = await queryImpl(request, location, matches, requestContext, dataStrategy || null, false, match);
3717
- if (isResponse$2(result)) {
3665
+ if (isResponse(result)) {
3718
3666
  return result;
3719
3667
  }
3720
3668
  let error = result.errors ? Object.values(result.errors)[0] : undefined;
@@ -3743,7 +3691,7 @@
3743
3691
  return result;
3744
3692
  }
3745
3693
  let result = await loadRouteData(request, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, routeMatch);
3746
- return isResponse$2(result) ? result : _extends({}, result, {
3694
+ return isResponse(result) ? result : _extends({}, result, {
3747
3695
  actionData: null,
3748
3696
  actionHeaders: {}
3749
3697
  });
@@ -3751,7 +3699,7 @@
3751
3699
  // If the user threw/returned a Response in callLoaderOrAction for a
3752
3700
  // `queryRoute` call, we throw the `DataStrategyResult` to bail out early
3753
3701
  // and then return or throw the raw Response here accordingly
3754
- if (isDataStrategyResult(e) && isResponse$2(e.result)) {
3702
+ if (isDataStrategyResult(e) && isResponse(e.result)) {
3755
3703
  if (e.type === ResultType.error) {
3756
3704
  throw e.result;
3757
3705
  }
@@ -3759,7 +3707,7 @@
3759
3707
  }
3760
3708
  // Redirects are always returned since they don't propagate to catch
3761
3709
  // boundaries
3762
- if (isRedirectResponse$1(e)) {
3710
+ if (isRedirectResponse(e)) {
3763
3711
  return e;
3764
3712
  }
3765
3713
  throw e;
@@ -3913,12 +3861,12 @@
3913
3861
  return;
3914
3862
  }
3915
3863
  let result = results[match.route.id];
3916
- if (isRedirectDataStrategyResultResult(result)) {
3864
+ if (isRedirectDataStrategyResult(result)) {
3917
3865
  let response = result.result;
3918
3866
  // Throw redirects and let the server handle them with an HTTP redirect
3919
3867
  throw normalizeRelativeRoutingRedirectResponse(response, request, match.route.id, matches, basename);
3920
3868
  }
3921
- if (isResponse$2(result.result) && isRouteRequest) {
3869
+ if (isResponse(result.result) && isRouteRequest) {
3922
3870
  // For SSR single-route requests, we want to hand Responses back
3923
3871
  // directly without unwrapping
3924
3872
  throw result;
@@ -4058,8 +4006,8 @@
4058
4006
  }
4059
4007
  let text = typeof opts.body === "string" ? opts.body : opts.body instanceof FormData || opts.body instanceof URLSearchParams ?
4060
4008
  // 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;
4009
+ Array.from(opts.body.entries()).reduce((acc, _ref3) => {
4010
+ let [name, value] = _ref3;
4063
4011
  return "" + acc + name + "=" + value + "\n";
4064
4012
  }, "") : String(opts.body);
4065
4013
  return {
@@ -4149,26 +4097,37 @@
4149
4097
  };
4150
4098
  }
4151
4099
 
4152
- // Filter out all routes below any caught error as they aren't going to
4100
+ // Filter out all routes at/below any caught error as they aren't going to
4153
4101
  // 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
- }
4102
+ function getLoaderMatchesUntilBoundary(matches, boundaryId, includeBoundary) {
4103
+ if (includeBoundary === void 0) {
4104
+ includeBoundary = false;
4161
4105
  }
4162
- return boundaryMatches;
4106
+ let index = matches.findIndex(m => m.route.id === boundaryId);
4107
+ if (index >= 0) {
4108
+ return matches.slice(0, includeBoundary ? index + 1 : index);
4109
+ }
4110
+ return matches;
4163
4111
  }
4164
- function getMatchesToLoad(history, state, matches, submission, location, isInitialLoad, isRevalidationRequired, cancelledFetcherLoads, fetchersQueuedForDeletion, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, pendingActionResult) {
4112
+ function getMatchesToLoad(history, state, matches, submission, location, initialHydration, isRevalidationRequired, cancelledFetcherLoads, fetchersQueuedForDeletion, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, pendingActionResult) {
4165
4113
  let actionResult = pendingActionResult ? isErrorResult(pendingActionResult[1]) ? pendingActionResult[1].error : pendingActionResult[1].data : undefined;
4166
4114
  let currentUrl = history.createURL(state.location);
4167
4115
  let nextUrl = history.createURL(location);
4168
4116
 
4169
4117
  // 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;
4118
+ let boundaryMatches = matches;
4119
+ if (initialHydration && state.errors) {
4120
+ // On initial hydration, only consider matches up to _and including_ the boundary.
4121
+ // This is inclusive to handle cases where a server loader ran successfully,
4122
+ // a child server loader bubbled up to this route, but this route has
4123
+ // `clientLoader.hydrate` so we want to still run the `clientLoader` so that
4124
+ // we have a complete version of `loaderData`
4125
+ boundaryMatches = getLoaderMatchesUntilBoundary(matches, Object.keys(state.errors)[0], true);
4126
+ } else if (pendingActionResult && isErrorResult(pendingActionResult[1])) {
4127
+ // If an action threw an error, we call loaders up to, but not including the
4128
+ // boundary
4129
+ boundaryMatches = getLoaderMatchesUntilBoundary(matches, pendingActionResult[0]);
4130
+ }
4172
4131
 
4173
4132
  // Don't revalidate loaders by default after action 4xx/5xx responses
4174
4133
  // when the flag is enabled. They can still opt-into revalidation via
@@ -4186,13 +4145,8 @@
4186
4145
  if (route.loader == null) {
4187
4146
  return false;
4188
4147
  }
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);
4148
+ if (initialHydration) {
4149
+ return shouldLoadRouteOnHydration(route, state.loaderData, state.errors);
4196
4150
  }
4197
4151
 
4198
4152
  // Always call the loader on new route instances
@@ -4226,10 +4180,11 @@
4226
4180
  let revalidatingFetchers = [];
4227
4181
  fetchLoadMatches.forEach((f, key) => {
4228
4182
  // 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)) {
4183
+ // - on initial hydration (shouldn't be any fetchers then anyway)
4184
+ // - if fetcher won't be present in the subsequent render
4185
+ // - no longer matches the URL (v7_fetcherPersist=false)
4186
+ // - was unmounted but persisted due to v7_fetcherPersist=true
4187
+ if (initialHydration || !matches.some(m => m.route.id === f.routeId) || fetchersQueuedForDeletion.has(key)) {
4233
4188
  return;
4234
4189
  }
4235
4190
  let fetcherMatches = matchRoutes(routesToUse, f.path, basename);
@@ -4295,6 +4250,32 @@
4295
4250
  });
4296
4251
  return [navigationMatches, revalidatingFetchers];
4297
4252
  }
4253
+ function shouldLoadRouteOnHydration(route, loaderData, errors) {
4254
+ // We dunno if we have a loader - gotta find out!
4255
+ if (route.lazy) {
4256
+ return true;
4257
+ }
4258
+
4259
+ // No loader, nothing to initialize
4260
+ if (!route.loader) {
4261
+ return false;
4262
+ }
4263
+ let hasData = loaderData != null && loaderData[route.id] !== undefined;
4264
+ let hasError = errors != null && errors[route.id] !== undefined;
4265
+
4266
+ // Don't run if we error'd during SSR
4267
+ if (!hasData && hasError) {
4268
+ return false;
4269
+ }
4270
+
4271
+ // Explicitly opting-in to running on hydration
4272
+ if (typeof route.loader === "function" && route.loader.hydrate === true) {
4273
+ return true;
4274
+ }
4275
+
4276
+ // Otherwise, run if we're not yet initialized with anything
4277
+ return !hasData && !hasError;
4278
+ }
4298
4279
  function isNewLoader(currentLoaderData, currentMatch, match) {
4299
4280
  let isNew =
4300
4281
  // [a] -> [a, b]
@@ -4328,34 +4309,6 @@
4328
4309
  }
4329
4310
  return arg.defaultShouldRevalidate;
4330
4311
  }
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
4312
  function patchRoutesImpl(routeId, children, routesToUse, manifest, mapRouteProperties) {
4360
4313
  var _childrenToPatch;
4361
4314
  let childrenToPatch;
@@ -4456,10 +4409,10 @@
4456
4409
  }
4457
4410
 
4458
4411
  // Default implementation of `dataStrategy` which fetches all loaders in parallel
4459
- async function defaultDataStrategy(_ref6) {
4412
+ async function defaultDataStrategy(_ref4) {
4460
4413
  let {
4461
4414
  matches
4462
- } = _ref6;
4415
+ } = _ref4;
4463
4416
  let matchesToLoad = matches.filter(m => m.shouldLoad);
4464
4417
  let results = await Promise.all(matchesToLoad.map(m => m.resolve()));
4465
4418
  return results.reduce((acc, result, i) => Object.assign(acc, {
@@ -4624,7 +4577,7 @@
4624
4577
  result,
4625
4578
  type
4626
4579
  } = dataStrategyResult;
4627
- if (isResponse$2(result)) {
4580
+ if (isResponse(result)) {
4628
4581
  let data;
4629
4582
  try {
4630
4583
  let contentType = result.headers.get("Content-Type");
@@ -4902,11 +4855,11 @@
4902
4855
  }
4903
4856
  function mergeLoaderData(loaderData, newLoaderData, matches, errors) {
4904
4857
  // Start with all new entries that are not being reset
4905
- let mergedLoaderData = Object.entries(newLoaderData).filter(_ref7 => {
4906
- let [, v] = _ref7;
4858
+ let mergedLoaderData = Object.entries(newLoaderData).filter(_ref5 => {
4859
+ let [, v] = _ref5;
4907
4860
  return v !== ResetLoaderDataSymbol;
4908
- }).reduce((merged, _ref8) => {
4909
- let [k, v] = _ref8;
4861
+ }).reduce((merged, _ref6) => {
4862
+ let [k, v] = _ref6;
4910
4863
  merged[k] = v;
4911
4864
  return merged;
4912
4865
  }, {});
@@ -4973,9 +4926,7 @@
4973
4926
  let errorMessage = "Unknown @remix-run/router error";
4974
4927
  if (status === 400) {
4975
4928
  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) {
4929
+ if (method && pathname && routeId) {
4979
4930
  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
4931
  } else if (type === "invalid-body") {
4981
4932
  errorMessage = "Unable to encode submission body";
@@ -5035,14 +4986,11 @@
5035
4986
  // /page#hash -> /page
5036
4987
  return false;
5037
4988
  }
5038
- function isPromise(val) {
5039
- return typeof val === "object" && val != null && "then" in val;
5040
- }
5041
4989
  function isDataStrategyResult(result) {
5042
4990
  return result != null && typeof result === "object" && "type" in result && "result" in result && (result.type === ResultType.data || result.type === ResultType.error);
5043
4991
  }
5044
- function isRedirectDataStrategyResultResult(result) {
5045
- return isResponse$2(result.result) && redirectStatusCodes$1.has(result.result.status);
4992
+ function isRedirectDataStrategyResult(result) {
4993
+ return isResponse(result.result) && redirectStatusCodes.has(result.result.status);
5046
4994
  }
5047
4995
  function isErrorResult(result) {
5048
4996
  return result.type === ResultType.error;
@@ -5053,16 +5001,14 @@
5053
5001
  function isDataWithResponseInit(value) {
5054
5002
  return typeof value === "object" && value != null && "type" in value && "data" in value && "init" in value && value.type === "DataWithResponseInit";
5055
5003
  }
5056
- function isResponse$2(value) {
5004
+ function isResponse(value) {
5057
5005
  return value != null && typeof value.status === "number" && typeof value.statusText === "string" && typeof value.headers === "object" && typeof value.body !== "undefined";
5058
5006
  }
5059
- function isRedirectResponse$1(result) {
5060
- if (!isResponse$2(result)) {
5061
- return false;
5062
- }
5063
- let status = result.status;
5064
- let location = result.headers.get("Location");
5065
- return status >= 300 && status <= 399 && location != null;
5007
+ function isRedirectStatusCode(statusCode) {
5008
+ return redirectStatusCodes.has(statusCode);
5009
+ }
5010
+ function isRedirectResponse(result) {
5011
+ return isResponse(result) && isRedirectStatusCode(result.status) && result.headers.has("Location");
5066
5012
  }
5067
5013
  function isValidMethod(method) {
5068
5014
  return validRequestMethods.has(method.toUpperCase());
@@ -6012,7 +5958,7 @@
6012
5958
  return DataRouterStateHook;
6013
5959
  }(DataRouterStateHook$1 || {});
6014
5960
  function getDataRouterConsoleError$1(hookName) {
6015
- return hookName + " must be used within a data router. See https://reactrouter.com/routers/picking-a-router.";
5961
+ return hookName + " must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.";
6016
5962
  }
6017
5963
  function useDataRouterContext$2(hookName) {
6018
5964
  let ctx = React__namespace.useContext(DataRouterContext);
@@ -6505,8 +6451,8 @@
6505
6451
  let setState = React__namespace.useCallback((newState, _ref2) => {
6506
6452
  let {
6507
6453
  deletedFetchers,
6508
- flushSync: flushSync,
6509
- viewTransitionOpts: viewTransitionOpts
6454
+ flushSync,
6455
+ viewTransitionOpts
6510
6456
  } = _ref2;
6511
6457
  deletedFetchers.forEach(key => fetcherData.current.delete(key));
6512
6458
  newState.fetchers.forEach((fetcher, key) => {
@@ -7408,7 +7354,56 @@
7408
7354
  * @see https://remix.run/route/meta
7409
7355
  */
7410
7356
 
7411
- // Loose copy from @react-router/server-runtime to avoid circular imports
7357
+ /**
7358
+ * A function that returns an array of data objects to use for rendering
7359
+ * metadata HTML tags in a route. These tags are not rendered on descendant
7360
+ * routes in the route hierarchy. In other words, they will only be rendered on
7361
+ * the route in which they are exported.
7362
+ *
7363
+ * @param Loader - The type of the current route's loader function
7364
+ * @param MatchLoaders - Mapping from a parent route's filepath to its loader
7365
+ * function type
7366
+ *
7367
+ * Note that parent route filepaths are relative to the `app/` directory.
7368
+ *
7369
+ * For example, if this meta function is for `/sales/customers/$customerId`:
7370
+ *
7371
+ * ```ts
7372
+ * // app/root.tsx
7373
+ * const loader = () => ({ hello: "world" })
7374
+ * export type Loader = typeof loader
7375
+ *
7376
+ * // app/routes/sales.tsx
7377
+ * const loader = () => ({ salesCount: 1074 })
7378
+ * export type Loader = typeof loader
7379
+ *
7380
+ * // app/routes/sales/customers.tsx
7381
+ * const loader = () => ({ customerCount: 74 })
7382
+ * export type Loader = typeof loader
7383
+ *
7384
+ * // app/routes/sales/customers/$customersId.tsx
7385
+ * import type { Loader as RootLoader } from "../../../root"
7386
+ * import type { Loader as SalesLoader } from "../../sales"
7387
+ * import type { Loader as CustomersLoader } from "../../sales/customers"
7388
+ *
7389
+ * const loader = () => ({ name: "Customer name" })
7390
+ *
7391
+ * const meta: MetaFunction<typeof loader, {
7392
+ * "root": RootLoader,
7393
+ * "routes/sales": SalesLoader,
7394
+ * "routes/sales/customers": CustomersLoader,
7395
+ * }> = ({ data, matches }) => {
7396
+ * const { name } = data
7397
+ * // ^? string
7398
+ * const { customerCount } = matches.find((match) => match.id === "routes/sales/customers").data
7399
+ * // ^? number
7400
+ * const { salesCount } = matches.find((match) => match.id === "routes/sales").data
7401
+ * // ^? number
7402
+ * const { hello } = matches.find((match) => match.id === "root").data
7403
+ * // ^? "world"
7404
+ * }
7405
+ * ```
7406
+ */
7412
7407
 
7413
7408
  /**
7414
7409
  * A React component that is rendered for a route.
@@ -7465,7 +7460,7 @@
7465
7460
  let descriptors = matches.map(match => {
7466
7461
  let module = routeModules[match.route.id];
7467
7462
  let route = manifest.routes[match.route.id];
7468
- return [route.css ? route.css.map(href => ({
7463
+ return [route && route.css ? route.css.map(href => ({
7469
7464
  rel: "stylesheet",
7470
7465
  href
7471
7466
  })) : [], (module == null || module.links == null ? void 0 : module.links()) || []];
@@ -7544,8 +7539,12 @@
7544
7539
  }
7545
7540
  async function getKeyedPrefetchLinks(matches, manifest, routeModules) {
7546
7541
  let links = await Promise.all(matches.map(async match => {
7547
- let mod = await loadRouteModule(manifest.routes[match.route.id], routeModules);
7548
- return mod.links ? mod.links() : [];
7542
+ let route = manifest.routes[match.route.id];
7543
+ if (route) {
7544
+ let mod = await loadRouteModule(route, routeModules);
7545
+ return mod.links ? mod.links() : [];
7546
+ }
7547
+ return [];
7549
7548
  }));
7550
7549
  return dedupeLinkDescriptors(links.flat(1).filter(isHtmlLinkDescriptor).filter(link => link.rel === "stylesheet" || link.rel === "preload").map(link => link.rel === "stylesheet" ? _extends({}, link, {
7551
7550
  rel: "prefetch",
@@ -7557,7 +7556,6 @@
7557
7556
 
7558
7557
  // This is ridiculously identical to transition.ts `filterMatchesToLoad`
7559
7558
  function getNewMatchesForLinks(page, nextMatches, currentMatches, manifest, location, mode) {
7560
- let path = parsePathPatch(page);
7561
7559
  let isNew = (match, index) => {
7562
7560
  if (!currentMatches[index]) return true;
7563
7561
  return match.route.id !== currentMatches[index].route.id;
@@ -7572,43 +7570,45 @@
7572
7570
  ((_currentMatches$index = currentMatches[index].route.path) == null ? void 0 : _currentMatches$index.endsWith("*")) && currentMatches[index].params["*"] !== match.params["*"]
7573
7571
  );
7574
7572
  };
7573
+ if (mode === "assets") {
7574
+ return nextMatches.filter((match, index) => isNew(match, index) || matchPathChanged(match, index));
7575
+ }
7575
7576
 
7576
- // NOTE: keep this mostly up-to-date w/ the transition data diff, but this
7577
+ // NOTE: keep this mostly up-to-date w/ the router data diff, but this
7577
7578
  // version doesn't care about submissions
7578
- let newMatches = mode === "data" && location.search !== path.search ?
7579
- // this is really similar to stuff in transition.ts, maybe somebody smarter
7579
+ // TODO: this is really similar to stuff in router.ts, maybe somebody smarter
7580
7580
  // than me (or in less of a hurry) can share some of it. You're the best.
7581
- nextMatches.filter((match, index) => {
7582
- let manifestRoute = manifest.routes[match.route.id];
7583
- if (!manifestRoute.hasLoader) {
7584
- return false;
7585
- }
7586
- if (isNew(match, index) || matchPathChanged(match, index)) {
7587
- return true;
7588
- }
7589
- if (match.route.shouldRevalidate) {
7590
- var _currentMatches$;
7591
- let routeChoice = match.route.shouldRevalidate({
7592
- currentUrl: new URL(location.pathname + location.search + location.hash, window.origin),
7593
- currentParams: ((_currentMatches$ = currentMatches[0]) == null ? void 0 : _currentMatches$.params) || {},
7594
- nextUrl: new URL(page, window.origin),
7595
- nextParams: match.params,
7596
- defaultShouldRevalidate: true
7597
- });
7598
- if (typeof routeChoice === "boolean") {
7599
- return routeChoice;
7581
+ if (mode === "data") {
7582
+ return nextMatches.filter((match, index) => {
7583
+ let manifestRoute = manifest.routes[match.route.id];
7584
+ if (!manifestRoute || !manifestRoute.hasLoader) {
7585
+ return false;
7600
7586
  }
7601
- }
7602
- return true;
7603
- }) : nextMatches.filter((match, index) => {
7604
- let manifestRoute = manifest.routes[match.route.id];
7605
- return (mode === "assets" || manifestRoute.hasLoader) && (isNew(match, index) || matchPathChanged(match, index));
7606
- });
7607
- return newMatches;
7587
+ if (isNew(match, index) || matchPathChanged(match, index)) {
7588
+ return true;
7589
+ }
7590
+ if (match.route.shouldRevalidate) {
7591
+ var _currentMatches$;
7592
+ let routeChoice = match.route.shouldRevalidate({
7593
+ currentUrl: new URL(location.pathname + location.search + location.hash, window.origin),
7594
+ currentParams: ((_currentMatches$ = currentMatches[0]) == null ? void 0 : _currentMatches$.params) || {},
7595
+ nextUrl: new URL(page, window.origin),
7596
+ nextParams: match.params,
7597
+ defaultShouldRevalidate: true
7598
+ });
7599
+ if (typeof routeChoice === "boolean") {
7600
+ return routeChoice;
7601
+ }
7602
+ }
7603
+ return true;
7604
+ });
7605
+ }
7606
+ return [];
7608
7607
  }
7609
7608
  function getModuleLinkHrefs(matches, manifestPatch) {
7610
7609
  return dedupeHrefs(matches.map(match => {
7611
7610
  let route = manifestPatch.routes[match.route.id];
7611
+ if (!route) return [];
7612
7612
  let hrefs = [route.module];
7613
7613
  if (route.imports) {
7614
7614
  hrefs = hrefs.concat(route.imports);
@@ -7623,6 +7623,7 @@
7623
7623
  function getCurrentPageModulePreloadHrefs(matches, manifest) {
7624
7624
  return dedupeHrefs(matches.map(match => {
7625
7625
  let route = manifest.routes[match.route.id];
7626
+ if (!route) return [];
7626
7627
  let hrefs = [route.module];
7627
7628
  if (route.imports) {
7628
7629
  hrefs = hrefs.concat(route.imports);
@@ -7661,13 +7662,6 @@
7661
7662
  }, []);
7662
7663
  }
7663
7664
 
7664
- // https://github.com/remix-run/history/issues/897
7665
- function parsePathPatch(href) {
7666
- let path = parsePath(href);
7667
- if (path.search === undefined) path.search = "";
7668
- return path;
7669
- }
7670
-
7671
7665
  // Detect if this browser supports <link rel="preload"> (or has it enabled).
7672
7666
  // Originally added to handle the firefox `network.preload` config:
7673
7667
  // https://bugzilla.mozilla.org/show_bug.cgi?id=1847811
@@ -7705,13 +7699,6 @@
7705
7699
  };
7706
7700
  }
7707
7701
 
7708
- /**
7709
- * Data for a route that was returned from a `loader()`.
7710
- */
7711
-
7712
- function isResponse$1(value) {
7713
- return value != null && typeof value.status === "number" && typeof value.statusText === "string" && typeof value.headers === "object" && typeof value.body !== "undefined";
7714
- }
7715
7702
  async function createRequestInit(request) {
7716
7703
  let init = {
7717
7704
  signal: request.signal
@@ -7850,7 +7837,7 @@
7850
7837
  });
7851
7838
  return result;
7852
7839
  });
7853
- if (isResponse$1(result.result) || isRouteErrorResponse(result.result)) {
7840
+ if (isResponse(result.result) || isRouteErrorResponse(result.result)) {
7854
7841
  return {
7855
7842
  [actionMatch.route.id]: result
7856
7843
  };
@@ -7895,6 +7882,7 @@
7895
7882
  let results = {};
7896
7883
  let resolvePromise = Promise.all(matches.map(async (m, i) => m.resolve(async handler => {
7897
7884
  routeDfds[i].resolve();
7885
+ let manifestRoute = manifest.routes[m.route.id];
7898
7886
  if (!m.shouldLoad) {
7899
7887
  var _routeModules$m$route;
7900
7888
  // If we're not yet initialized and this is the initial load, respect
@@ -7907,7 +7895,7 @@
7907
7895
  // Otherwise, we opt out if we currently have data, a `loader`, and a
7908
7896
  // `shouldRevalidate` function. This implies that the user opted out
7909
7897
  // via `shouldRevalidate`
7910
- if (m.route.id in router.state.loaderData && manifest.routes[m.route.id].hasLoader && (_routeModules$m$route = routeModules[m.route.id]) != null && _routeModules$m$route.shouldRevalidate) {
7898
+ if (m.route.id in router.state.loaderData && manifestRoute && manifestRoute.hasLoader && (_routeModules$m$route = routeModules[m.route.id]) != null && _routeModules$m$route.shouldRevalidate) {
7911
7899
  foundOptOutRoute = true;
7912
7900
  return;
7913
7901
  }
@@ -7915,8 +7903,8 @@
7915
7903
 
7916
7904
  // When a route has a client loader, it opts out of the singular call and
7917
7905
  // calls it's server loader via `serverLoader()` using a `?_routes` param
7918
- if (manifest.routes[m.route.id].hasClientLoader) {
7919
- if (manifest.routes[m.route.id].hasLoader) {
7906
+ if (manifestRoute && manifestRoute.hasClientLoader) {
7907
+ if (manifestRoute.hasLoader) {
7920
7908
  foundOptOutRoute = true;
7921
7909
  }
7922
7910
  try {
@@ -7935,7 +7923,7 @@
7935
7923
  }
7936
7924
 
7937
7925
  // Load this route on the server if it has a loader
7938
- if (manifest.routes[m.route.id].hasLoader) {
7926
+ if (manifestRoute && manifestRoute.hasLoader) {
7939
7927
  routesParams.add(m.route.id);
7940
7928
  }
7941
7929
 
@@ -8327,20 +8315,18 @@
8327
8315
  }));
8328
8316
  }
8329
8317
 
8330
- // NOTE: make sure to change the Route in server-runtime if you change this
8331
-
8332
- // NOTE: make sure to change the EntryRoute in server-runtime if you change this
8333
-
8334
8318
  // Create a map of routes by parentId to use recursively instead of
8335
8319
  // repeatedly filtering the manifest.
8336
8320
  function groupRoutesByParentId$1(manifest) {
8337
8321
  let routes = {};
8338
8322
  Object.values(manifest).forEach(route => {
8339
- let parentId = route.parentId || "";
8340
- if (!routes[parentId]) {
8341
- routes[parentId] = [];
8323
+ if (route) {
8324
+ let parentId = route.parentId || "";
8325
+ if (!routes[parentId]) {
8326
+ routes[parentId] = [];
8327
+ }
8328
+ routes[parentId].push(route);
8342
8329
  }
8343
- routes[parentId].push(route);
8344
8330
  });
8345
8331
  return routes;
8346
8332
  }
@@ -8480,10 +8466,12 @@
8480
8466
  // Use critical path modules directly
8481
8467
  Object.assign(dataRoute, _extends({}, dataRoute, getRouteComponents(route, routeModule, isSpaMode), {
8482
8468
  handle: routeModule.handle,
8483
- shouldRevalidate: needsRevalidation ? wrapShouldRevalidateForHdr(route.id, routeModule.shouldRevalidate, needsRevalidation) : routeModule.shouldRevalidate
8469
+ shouldRevalidate: getShouldRevalidateFunction(routeModule, route.id, needsRevalidation)
8484
8470
  }));
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];
8471
+ let hasInitialData = initialState && initialState.loaderData && route.id in initialState.loaderData;
8472
+ let initialData = hasInitialData ? initialState == null || (_initialState$loaderD = initialState.loaderData) == null ? void 0 : _initialState$loaderD[route.id] : undefined;
8473
+ let hasInitialError = initialState && initialState.errors && route.id in initialState.errors;
8474
+ let initialError = hasInitialError ? initialState == null || (_initialState$errors = initialState.errors) == null ? void 0 : _initialState$errors[route.id] : undefined;
8487
8475
  let isHydrationRequest = needsRevalidation == null && (((_routeModule$clientLo = routeModule.clientLoader) == null ? void 0 : _routeModule$clientLo.hydrate) === true || !route.hasLoader);
8488
8476
  dataRoute.loader = async (_ref, singleFetch) => {
8489
8477
  let {
@@ -8506,10 +8494,12 @@
8506
8494
 
8507
8495
  // On the first call, resolve with the server result
8508
8496
  if (isHydrationRequest) {
8509
- if (initialError !== undefined) {
8497
+ if (hasInitialData) {
8498
+ return initialData;
8499
+ }
8500
+ if (hasInitialError) {
8510
8501
  throw initialError;
8511
8502
  }
8512
- return initialData;
8513
8503
  }
8514
8504
 
8515
8505
  // Call the server loader for client-side navigations
@@ -8595,16 +8585,13 @@
8595
8585
  }
8596
8586
  }));
8597
8587
  }
8598
- if (needsRevalidation) {
8599
- lazyRoute.shouldRevalidate = wrapShouldRevalidateForHdr(route.id, mod.shouldRevalidate, needsRevalidation);
8600
- }
8601
8588
  return _extends({}, lazyRoute.loader ? {
8602
8589
  loader: lazyRoute.loader
8603
8590
  } : {}, lazyRoute.action ? {
8604
8591
  action: lazyRoute.action
8605
8592
  } : {}, {
8606
8593
  hasErrorBoundary: lazyRoute.hasErrorBoundary,
8607
- shouldRevalidate: lazyRoute.shouldRevalidate,
8594
+ shouldRevalidate: getShouldRevalidateFunction(lazyRoute, route.id, needsRevalidation),
8608
8595
  handle: lazyRoute.handle,
8609
8596
  // No need to wrap these in layout since the root route is never
8610
8597
  // loaded via route.lazy()
@@ -8618,6 +8605,22 @@
8618
8605
  return dataRoute;
8619
8606
  });
8620
8607
  }
8608
+ function getShouldRevalidateFunction(route, routeId, needsRevalidation) {
8609
+ // During HDR we force revalidation for updated routes
8610
+ if (needsRevalidation) {
8611
+ return wrapShouldRevalidateForHdr(routeId, route.shouldRevalidate, needsRevalidation);
8612
+ }
8613
+
8614
+ // Single fetch revalidates by default, so override the RR default value which
8615
+ // matches the multi-fetch behavior with `true`
8616
+ if (route.shouldRevalidate) {
8617
+ let fn = route.shouldRevalidate;
8618
+ return opts => fn(_extends({}, opts, {
8619
+ defaultShouldRevalidate: true
8620
+ }));
8621
+ }
8622
+ return route.shouldRevalidate;
8623
+ }
8621
8624
 
8622
8625
  // When an HMR / HDR update happens we opt out of all user-defined
8623
8626
  // revalidation logic and force a revalidation on the first call
@@ -8819,9 +8822,12 @@
8819
8822
 
8820
8823
  // Patch routes we don't know about yet into the manifest
8821
8824
  let knownRoutes = new Set(Object.keys(manifest.routes));
8822
- let patches = Object.values(serverPatches).reduce((acc, route) => !knownRoutes.has(route.id) ? Object.assign(acc, {
8823
- [route.id]: route
8824
- }) : acc, {});
8825
+ let patches = Object.values(serverPatches).reduce((acc, route) => {
8826
+ if (route && !knownRoutes.has(route.id)) {
8827
+ acc[route.id] = route;
8828
+ }
8829
+ return acc;
8830
+ }, {});
8825
8831
  Object.assign(manifest.routes, patches);
8826
8832
 
8827
8833
  // Track discovered paths so we don't have to fetch them again
@@ -8831,7 +8837,7 @@
8831
8837
  // in their new children
8832
8838
  let parentIds = new Set();
8833
8839
  Object.values(patches).forEach(patch => {
8834
- if (!patch.parentId || !patches[patch.parentId]) {
8840
+ if (patch && (!patch.parentId || !patches[patch.parentId])) {
8835
8841
  parentIds.add(patch.parentId);
8836
8842
  }
8837
8843
  });
@@ -8861,9 +8867,6 @@
8861
8867
  const _excluded$2 = ["page"],
8862
8868
  _excluded2$1 = ["page", "matches"],
8863
8869
  _excluded3$1 = ["tagName"];
8864
-
8865
- // TODO: Temporary shim until we figure out the way to handle typings in v7
8866
-
8867
8870
  function useDataRouterContext$1() {
8868
8871
  let context = React__namespace.useContext(DataRouterContext);
8869
8872
  !context ? invariant$1(false, "You must render this element inside a <DataRouterContext.Provider> element") : void 0;
@@ -9126,12 +9129,13 @@
9126
9129
  let foundOptOutRoute = false;
9127
9130
  nextMatches.forEach(m => {
9128
9131
  var _routeModules$m$route;
9129
- if (!manifest.routes[m.route.id].hasLoader) {
9132
+ let manifestRoute = manifest.routes[m.route.id];
9133
+ if (!manifestRoute || !manifestRoute.hasLoader) {
9130
9134
  return;
9131
9135
  }
9132
9136
  if (!newMatchesForData.some(m2 => m2.route.id === m.route.id) && m.route.id in loaderData && (_routeModules$m$route = routeModules[m.route.id]) != null && _routeModules$m$route.shouldRevalidate) {
9133
9137
  foundOptOutRoute = true;
9134
- } else if (manifest.routes[m.route.id].hasClientLoader) {
9138
+ } else if (manifestRoute.hasClientLoader) {
9135
9139
  foundOptOutRoute = true;
9136
9140
  } else {
9137
9141
  routesParams.add(m.route.id);
@@ -9402,7 +9406,7 @@
9402
9406
  }, []);
9403
9407
  let routePreloads = matches.map(match => {
9404
9408
  let route = manifest.routes[match.route.id];
9405
- return (route.imports || []).concat([route.module]);
9409
+ return route ? (route.imports || []).concat([route.module]) : [];
9406
9410
  }).flat(1);
9407
9411
  let preloads = isHydrated ? [] : manifest.entry.imports.concat(routePreloads);
9408
9412
  return isHydrated ? null : /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, !enableFogOfWar ? /*#__PURE__*/React__namespace.createElement("link", {
@@ -9442,7 +9446,6 @@
9442
9446
  _excluded2 = ["aria-current", "caseSensitive", "className", "end", "style", "to", "viewTransition", "children"],
9443
9447
  _excluded3 = ["discover", "fetcherKey", "navigate", "reloadDocument", "replace", "state", "method", "action", "onSubmit", "relative", "preventScrollReset", "viewTransition"],
9444
9448
  _excluded4 = ["getKey", "storageKey"];
9445
-
9446
9449
  ////////////////////////////////////////////////////////////////////////////////
9447
9450
  //#region Global Stuff
9448
9451
  ////////////////////////////////////////////////////////////////////////////////
@@ -10123,7 +10126,7 @@
10123
10126
  return DataRouterStateHook;
10124
10127
  }(DataRouterStateHook || {}); // Internal hooks
10125
10128
  function getDataRouterConsoleError(hookName) {
10126
- return hookName + " must be used within a data router. See https://reactrouter.com/routers/picking-a-router.";
10129
+ return hookName + " must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.";
10127
10130
  }
10128
10131
  function useDataRouterContext(hookName) {
10129
10132
  let ctx = React__namespace.useContext(DataRouterContext);
@@ -11120,7 +11123,7 @@
11120
11123
  // route opted into clientLoader hydration and either:
11121
11124
  // * gave us a HydrateFallback
11122
11125
  // * or doesn't have a server loader and we have no data to render
11123
- if (route && shouldHydrateRouteLoader(manifestRoute, route, context.isSpaMode) && (route.HydrateFallback || !manifestRoute.hasLoader)) {
11126
+ if (route && manifestRoute && shouldHydrateRouteLoader(manifestRoute, route, context.isSpaMode) && (route.HydrateFallback || !manifestRoute.hasLoader)) {
11124
11127
  delete context.staticHandlerContext.loaderData[routeId];
11125
11128
  }
11126
11129
  }
@@ -11337,7 +11340,17 @@
11337
11340
  async parse(cookieHeader, parseOptions) {
11338
11341
  if (!cookieHeader) return null;
11339
11342
  let cookies = cookie.parse(cookieHeader, _extends({}, options, parseOptions));
11340
- return name in cookies ? cookies[name] === "" ? "" : await decodeCookieValue(cookies[name], secrets) : null;
11343
+ if (name in cookies) {
11344
+ let value = cookies[name];
11345
+ if (typeof value === "string" && value !== "") {
11346
+ let decoded = await decodeCookieValue(value, secrets);
11347
+ return decoded;
11348
+ } else {
11349
+ return "";
11350
+ }
11351
+ } else {
11352
+ return null;
11353
+ }
11341
11354
  },
11342
11355
  async serialize(value, serializeOptions) {
11343
11356
  return cookie.serialize(name, value === "" ? "" : await encodeCookieValue(value, secrets), _extends({}, options, serializeOptions));
@@ -11444,7 +11457,10 @@
11444
11457
 
11445
11458
  function createEntryRouteModules(manifest) {
11446
11459
  return Object.keys(manifest).reduce((memo, routeId) => {
11447
- memo[routeId] = manifest[routeId].module;
11460
+ let route = manifest[routeId];
11461
+ if (route) {
11462
+ memo[routeId] = route.module;
11463
+ }
11448
11464
  return memo;
11449
11465
  }, {});
11450
11466
  }
@@ -11568,32 +11584,6 @@
11568
11584
  }));
11569
11585
  }
11570
11586
 
11571
- // must be a type since this is a subtype of response
11572
- // interfaces must conform to the types they extend
11573
-
11574
- /**
11575
- * This is a shortcut for creating `application/json` responses. Converts `data`
11576
- * to JSON and sets the `Content-Type` header.
11577
- *
11578
- * @see https://remix.run/utils/json
11579
- */
11580
- const json = function json(data, init) {
11581
- if (init === void 0) {
11582
- init = {};
11583
- }
11584
- return json$1(data, init);
11585
- };
11586
- function isResponse(value) {
11587
- return value != null && typeof value.status === "number" && typeof value.statusText === "string" && typeof value.headers === "object" && typeof value.body !== "undefined";
11588
- }
11589
- const redirectStatusCodes = new Set([301, 302, 303, 307, 308]);
11590
- function isRedirectStatusCode(statusCode) {
11591
- return redirectStatusCodes.has(statusCode);
11592
- }
11593
- function isRedirectResponse(response) {
11594
- return isRedirectStatusCode(response.status);
11595
- }
11596
-
11597
11587
  /**
11598
11588
  * An object of unknown type for route loaders and actions provided by the
11599
11589
  * server's `getLoadContext()` function. This is defined as an empty interface
@@ -11601,10 +11591,6 @@
11601
11591
  * globally: https://www.typescriptlang.org/docs/handbook/declaration-merging.html
11602
11592
  */
11603
11593
 
11604
- /**
11605
- * Data for a route that was returned from a `loader()`.
11606
- */
11607
-
11608
11594
  // Need to use RR's version here to permit the optional context even
11609
11595
  // though we know it'll always be provided in remix
11610
11596
  async function callRouteHandler(handler, args) {
@@ -11665,18 +11651,23 @@
11665
11651
  return new Request(url.href, init);
11666
11652
  }
11667
11653
 
11668
- // NOTE: make sure to change the Route in remix-react/react-router-dev if you change this
11669
-
11670
- // NOTE: make sure to change the EntryRoute in react-router/react-router-dev if you change this
11654
+ function invariant(value, message) {
11655
+ if (value === false || value === null || typeof value === "undefined") {
11656
+ 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");
11657
+ throw new Error(message);
11658
+ }
11659
+ }
11671
11660
 
11672
11661
  function groupRoutesByParentId(manifest) {
11673
11662
  let routes = {};
11674
11663
  Object.values(manifest).forEach(route => {
11675
- let parentId = route.parentId || "";
11676
- if (!routes[parentId]) {
11677
- routes[parentId] = [];
11664
+ if (route) {
11665
+ let parentId = route.parentId || "";
11666
+ if (!routes[parentId]) {
11667
+ routes[parentId] = [];
11668
+ }
11669
+ routes[parentId].push(route);
11678
11670
  }
11679
- routes[parentId].push(route);
11680
11671
  });
11681
11672
  return routes;
11682
11673
  }
@@ -11712,7 +11703,29 @@
11712
11703
  path: route.path,
11713
11704
  // Need to use RR's version in the param typed here to permit the optional
11714
11705
  // context even though we know it'll always be provided in remix
11715
- loader: route.module.loader ? args => callRouteHandler(route.module.loader, args) : undefined,
11706
+ loader: route.module.loader ? async args => {
11707
+ // If we're prerendering, use the data passed in from prerendering
11708
+ // the .data route so we dom't call loaders twice
11709
+ if (args.request.headers.has("X-React-Router-Prerender-Data")) {
11710
+ let encoded = args.request.headers.get("X-React-Router-Prerender-Data");
11711
+ !encoded ? invariant(false, "Missing prerendered data for route") : void 0;
11712
+ let uint8array = new TextEncoder().encode(encoded);
11713
+ let stream = new ReadableStream({
11714
+ start(controller) {
11715
+ controller.enqueue(uint8array);
11716
+ controller.close();
11717
+ }
11718
+ });
11719
+ let decoded = await decodeViaTurboStream(stream, global);
11720
+ let data = decoded.value;
11721
+ !(data && route.id in data) ? invariant(false, "Unable to decode prerendered data") : void 0;
11722
+ let result = data[route.id];
11723
+ !("data" in result) ? invariant(false, "Unable to process prerendered data") : void 0;
11724
+ return result.data;
11725
+ }
11726
+ let val = await callRouteHandler(route.module.loader, args);
11727
+ return val;
11728
+ } : undefined,
11716
11729
  action: route.module.action ? args => callRouteHandler(route.module.action, args) : undefined,
11717
11730
  handle: route.module.handle
11718
11731
  };
@@ -11787,13 +11800,15 @@
11787
11800
  let {
11788
11801
  id
11789
11802
  } = match.route;
11790
- let routeModule = build.routes[id].module;
11803
+ let route = build.routes[id];
11804
+ !route ? invariant(false, "Route with id \"" + id + "\" not found in build") : void 0;
11805
+ let routeModule = route.module;
11791
11806
  let loaderHeaders = context.loaderHeaders[id] || new Headers();
11792
11807
  let actionHeaders = context.actionHeaders[id] || new Headers();
11793
11808
 
11794
11809
  // Only expose errorHeaders to the leaf headers() function to
11795
11810
  // avoid duplication via parentHeaders
11796
- let includeErrorHeaders = errorHeaders != undefined && idx === matches.length - 1;
11811
+ let includeErrorHeaders = errorHeaders != null && idx === matches.length - 1;
11797
11812
  // Only prepend cookies from errorHeaders at the leaf renderable route
11798
11813
  // when it's not the same as loaderHeaders/actionHeaders to avoid
11799
11814
  // duplicate cookies
@@ -12092,13 +12107,6 @@
12092
12107
  });
12093
12108
  }
12094
12109
 
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
12110
  function derive(build, mode) {
12103
12111
  let routes = createRoutes(build.routes);
12104
12112
  let dataRoutes = createStaticHandlerDataRoutes(build.routes, build.future);
@@ -12232,15 +12240,18 @@
12232
12240
  if (matches) {
12233
12241
  for (let match of matches) {
12234
12242
  let routeId = match.route.id;
12235
- patches[routeId] = build.assets.routes[routeId];
12243
+ let route = build.assets.routes[routeId];
12244
+ if (route) {
12245
+ patches[routeId] = route;
12246
+ }
12236
12247
  }
12237
12248
  }
12238
12249
  }
12239
- return json(patches, {
12250
+ return Response.json(patches, {
12240
12251
  headers: {
12241
12252
  "Cache-Control": "public, max-age=31536000, immutable"
12242
12253
  }
12243
- }); // Override the TypedResponse stuff from json()
12254
+ });
12244
12255
  }
12245
12256
  return new Response("Invalid Request", {
12246
12257
  status: 400
@@ -12419,7 +12430,7 @@
12419
12430
  }
12420
12431
  }
12421
12432
  function errorResponseToJson(errorResponse, serverMode) {
12422
- return json$1(serializeError(
12433
+ return Response.json(serializeError(
12423
12434
  // @ts-expect-error This is "private" from users but intended for internal use
12424
12435
  errorResponse.error || new Error("Unexpected Server Error"), serverMode), {
12425
12436
  status: errorResponse.status,
@@ -12776,7 +12787,6 @@
12776
12787
  exports.isCookie = isCookie;
12777
12788
  exports.isRouteErrorResponse = isRouteErrorResponse;
12778
12789
  exports.isSession = isSession;
12779
- exports.json = json$1;
12780
12790
  exports.matchPath = matchPath;
12781
12791
  exports.matchRoutes = matchRoutes;
12782
12792
  exports.parsePath = parsePath;