@remix-run/router 0.0.0-experimental-63b6834e → 0.0.0-experimental-cf9637ce

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,8 +1,7 @@
1
- export type { ActionFunction, ActionFunctionArgs, AgnosticDataIndexRouteObject, AgnosticDataNonIndexRouteObject, AgnosticDataRouteMatch, AgnosticDataRouteObject, AgnosticIndexRouteObject, AgnosticNonIndexRouteObject, AgnosticRouteMatch, AgnosticRouteObject, LazyRouteFunction, TrackedPromise, FormEncType, FormMethod, JsonFunction, LoaderFunction, LoaderFunctionArgs, ParamParseKey, Params, PathMatch, PathPattern, RedirectFunction, ShouldRevalidateFunction, Submission, } from "./utils";
1
+ export type { ActionFunction, ActionFunctionArgs, AgnosticDataIndexRouteObject, AgnosticDataNonIndexRouteObject, AgnosticDataRouteMatch, AgnosticDataRouteObject, AgnosticIndexRouteObject, AgnosticNonIndexRouteObject, AgnosticRouteMatch, AgnosticRouteObject, TrackedPromise, FormEncType, FormMethod, JsonFunction, LoaderFunction, LoaderFunctionArgs, ParamParseKey, Params, PathMatch, PathPattern, RedirectFunction, ShouldRevalidateFunction, Submission, } from "./utils";
2
2
  export { AbortedDeferredError, ErrorResponse, defer, generatePath, getToPathname, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, redirect, resolvePath, resolveTo, stripBasename, warning, } from "./utils";
3
3
  export type { BrowserHistory, BrowserHistoryOptions, HashHistory, HashHistoryOptions, History, InitialEntry, Location, MemoryHistory, MemoryHistoryOptions, Path, To, } from "./history";
4
4
  export { Action, createBrowserHistory, createPath, createHashHistory, createMemoryHistory, invariant, parsePath, } from "./history";
5
5
  export * from "./router";
6
6
  /** @internal */
7
- export type { RouteManifest as UNSAFE_RouteManifest } from "./utils";
8
7
  export { DeferredData as UNSAFE_DeferredData, convertRoutesToDataRoutes as UNSAFE_convertRoutesToDataRoutes, getPathContributingMatches as UNSAFE_getPathContributingMatches, } from "./utils";
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @remix-run/router v0.0.0-experimental-63b6834e
2
+ * @remix-run/router v0.0.0-experimental-cf9637ce
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -565,54 +565,40 @@ let ResultType;
565
565
  ResultType["error"] = "error";
566
566
  })(ResultType || (ResultType = {}));
567
567
 
568
- const immutableRouteKeys = new Set(["caseSensitive", "path", "id", "index", "children"]);
569
- /**
570
- * lazy() function to load a route definition, which can add non-matching
571
- * related properties to a route
572
- */
573
-
574
568
  function isIndexRoute(route) {
575
569
  return route.index === true;
576
570
  } // Walk the route tree generating unique IDs where necessary so we are working
577
571
  // solely with AgnosticDataRouteObject's within the Router
578
572
 
579
573
 
580
- function convertRoutesToDataRoutes(routes, hasErrorBoundary, parentPath, manifest) {
574
+ function convertRoutesToDataRoutes(routes, parentPath, allIds) {
581
575
  if (parentPath === void 0) {
582
576
  parentPath = [];
583
577
  }
584
578
 
585
- if (manifest === void 0) {
586
- manifest = {};
579
+ if (allIds === void 0) {
580
+ allIds = new Set();
587
581
  }
588
582
 
589
583
  return routes.map((route, index) => {
590
584
  let treePath = [...parentPath, index];
591
585
  let id = typeof route.id === "string" ? route.id : treePath.join("-");
592
586
  invariant(route.index !== true || !route.children, "Cannot specify children on an index route");
593
- invariant(!manifest[id], "Found a route id collision on id \"" + id + "\". Route " + "id's must be globally unique within Data Router usages");
587
+ invariant(!allIds.has(id), "Found a route id collision on id \"" + id + "\". Route " + "id's must be globally unique within Data Router usages");
588
+ allIds.add(id);
594
589
 
595
590
  if (isIndexRoute(route)) {
596
591
  let indexRoute = _extends({}, route, {
597
- hasErrorBoundary: hasErrorBoundary(route),
598
592
  id
599
593
  });
600
594
 
601
- manifest[id] = indexRoute;
602
595
  return indexRoute;
603
596
  } else {
604
597
  let pathOrLayoutRoute = _extends({}, route, {
605
598
  id,
606
- hasErrorBoundary: hasErrorBoundary(route),
607
- children: undefined
599
+ children: route.children ? convertRoutesToDataRoutes(route.children, treePath, allIds) : undefined
608
600
  });
609
601
 
610
- manifest[id] = pathOrLayoutRoute;
611
-
612
- if (route.children) {
613
- pathOrLayoutRoute.children = convertRoutesToDataRoutes(route.children, hasErrorBoundary, treePath, manifest);
614
- }
615
-
616
602
  return pathOrLayoutRoute;
617
603
  }
618
604
  });
@@ -1492,9 +1478,7 @@ const IDLE_BLOCKER = {
1492
1478
  };
1493
1479
  const ABSOLUTE_URL_REGEX = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;
1494
1480
  const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined";
1495
- const isServer = !isBrowser;
1496
-
1497
- const defaultHasErrorBoundary = route => Boolean(route.hasErrorBoundary); //#endregion
1481
+ const isServer = !isBrowser; //#endregion
1498
1482
  ////////////////////////////////////////////////////////////////////////////////
1499
1483
  //#region createRouter
1500
1484
  ////////////////////////////////////////////////////////////////////////////////
@@ -1503,14 +1487,10 @@ const defaultHasErrorBoundary = route => Boolean(route.hasErrorBoundary); //#end
1503
1487
  * Create a router and listen to history POP navigations
1504
1488
  */
1505
1489
 
1506
-
1507
1490
  function createRouter(init) {
1508
1491
  invariant(init.routes.length > 0, "You must provide a non-empty routes array to createRouter");
1509
- let hasErrorBoundary = init.hasErrorBoundary || defaultHasErrorBoundary; // Routes keyed by ID
1510
-
1511
- let manifest = {}; // Routes in tree format for matching
1512
-
1513
- let dataRoutes = convertRoutesToDataRoutes(init.routes, hasErrorBoundary, undefined, manifest); // Cleanup function for history
1492
+ let dataRoutes = convertRoutesToDataRoutes(init.routes);
1493
+ let inFlightDataRoutes; // Cleanup function for history
1514
1494
 
1515
1495
  let unlistenHistory = null; // Externally-provided functions to call on all state changes
1516
1496
 
@@ -1547,10 +1527,7 @@ function createRouter(init) {
1547
1527
  };
1548
1528
  }
1549
1529
 
1550
- let initialized = // All initialMatches need to be loaded before we're ready. If we have lazy
1551
- // functions around still then we'll need to run them in initialize()
1552
- !initialMatches.some(m => m.route.lazy) && ( // And we have to either have no loaders or have been provided hydrationData
1553
- !initialMatches.some(m => m.route.loader) || init.hydrationData != null);
1530
+ let initialized = !initialMatches.some(m => m.route.loader) || init.hydrationData != null;
1554
1531
  let router;
1555
1532
  let state = {
1556
1533
  historyAction: init.history.action,
@@ -1674,57 +1651,12 @@ function createRouter(init) {
1674
1651
  }
1675
1652
 
1676
1653
  return startNavigation(historyAction, location);
1677
- });
1654
+ }); // Kick off initial data load if needed. Use Pop to avoid modifying history
1678
1655
 
1679
- if (init.onInitialize) {
1680
- if (state.initialized) {
1681
- // We delay calling the onInitialize function until the next tick so
1682
- // this function has a chance to return the router instance, otherwise
1683
- // consumers will get an error if they try to use the returned router
1684
- // instance in their callback. Note that we also provide the router
1685
- // instance to the callback as a convenience and to avoid this ambiguity
1686
- // in the consumer code.
1687
- Promise.resolve().then(() => init.onInitialize({
1688
- router
1689
- }));
1690
- } else {
1691
- let unsubscribe = subscribe(updatedState => {
1692
- if (updatedState.initialized) {
1693
- unsubscribe();
1694
- init.onInitialize({
1695
- router
1696
- });
1697
- }
1698
- });
1699
- }
1700
- }
1701
-
1702
- if (state.initialized) {
1703
- return router;
1704
- }
1705
-
1706
- let lazyMatches = state.matches.filter(m => m.route.lazy);
1707
-
1708
- if (lazyMatches.length === 0) {
1709
- // Kick off initial data load if needed. Use Pop to avoid modifying history
1656
+ if (!state.initialized) {
1710
1657
  startNavigation(exports.Action.Pop, state.location);
1711
- return router;
1712
- } // Load lazy modules, then kick off initial data load if needed
1713
-
1714
-
1715
- loadLazyRouteModules(lazyMatches, hasErrorBoundary, manifest).then(() => {
1716
- let initialized = !state.matches.some(m => m.route.loader) || init.hydrationData != null;
1658
+ }
1717
1659
 
1718
- if (initialized) {
1719
- // We already have required loaderData so we can just set initialized
1720
- updateState({
1721
- initialized: true
1722
- });
1723
- } else {
1724
- // We still need to kick off initial data loads
1725
- startNavigation(exports.Action.Pop, state.location);
1726
- }
1727
- });
1728
1660
  return router;
1729
1661
  } // Clean up a router and it's side effects
1730
1662
 
@@ -1794,6 +1726,12 @@ function createRouter(init) {
1794
1726
 
1795
1727
 
1796
1728
  let preventScrollReset = pendingPreventScrollReset === true || state.navigation.formMethod != null && isMutationMethod(state.navigation.formMethod) && ((_location$state2 = location.state) == null ? void 0 : _location$state2._isRedirect) !== true;
1729
+
1730
+ if (inFlightDataRoutes) {
1731
+ dataRoutes = inFlightDataRoutes;
1732
+ inFlightDataRoutes = undefined;
1733
+ }
1734
+
1797
1735
  updateState(_extends({}, newState, {
1798
1736
  // matches, errors, fetchers go through as-is
1799
1737
  actionData,
@@ -1949,8 +1887,9 @@ function createRouter(init) {
1949
1887
 
1950
1888
  saveScrollPosition(state.location, state.matches);
1951
1889
  pendingPreventScrollReset = (opts && opts.preventScrollReset) === true;
1890
+ let routesToUse = inFlightDataRoutes || dataRoutes;
1952
1891
  let loadingNavigation = opts && opts.overrideNavigation;
1953
- let matches = matchRoutes(dataRoutes, location, init.basename); // Short circuit with a 404 on the root error boundary if we match nothing
1892
+ let matches = matchRoutes(routesToUse, location, init.basename); // Short circuit with a 404 on the root error boundary if we match nothing
1954
1893
 
1955
1894
  if (!matches) {
1956
1895
  let error = getInternalRouterError(404, {
@@ -1959,7 +1898,7 @@ function createRouter(init) {
1959
1898
  let {
1960
1899
  matches: notFoundMatches,
1961
1900
  route
1962
- } = getShortCircuitMatches(dataRoutes); // Cancel all pending deferred on 404s since we don't keep any routes
1901
+ } = getShortCircuitMatches(routesToUse); // Cancel all pending deferred on 404s since we don't keep any routes
1963
1902
 
1964
1903
  cancelActiveDeferreds();
1965
1904
  completeNavigation(location, {
@@ -2063,16 +2002,6 @@ function createRouter(init) {
2063
2002
  let result;
2064
2003
  let actionMatch = getTargetMatch(matches, location);
2065
2004
 
2066
- if (actionMatch.route.lazy) {
2067
- await loadLazyRouteModules([actionMatch], hasErrorBoundary, manifest);
2068
-
2069
- if (request.signal.aborted) {
2070
- return {
2071
- shortCircuited: true
2072
- };
2073
- }
2074
- }
2075
-
2076
2005
  if (!actionMatch.route.action) {
2077
2006
  result = {
2078
2007
  type: ResultType.error,
@@ -2174,7 +2103,8 @@ function createRouter(init) {
2174
2103
  formData: loadingNavigation.formData,
2175
2104
  formEncType: loadingNavigation.formEncType
2176
2105
  } : undefined;
2177
- let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(init.history, state, matches, activeSubmission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, pendingActionData, pendingError, fetchLoadMatches); // Cancel pending deferreds for no-longer-matched routes or routes we're
2106
+ let routesToUse = inFlightDataRoutes || dataRoutes;
2107
+ let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(init.history, state, matches, activeSubmission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, fetchLoadMatches, routesToUse, init.basename, pendingActionData, pendingError); // Cancel pending deferreds for no-longer-matched routes or routes we're
2178
2108
  // about to reload. Note that if this is an action reload we would have
2179
2109
  // already cancelled all pending deferreds so this would be a no-op
2180
2110
 
@@ -2226,18 +2156,6 @@ function createRouter(init) {
2226
2156
 
2227
2157
  pendingNavigationLoadId = ++incrementingLoadId;
2228
2158
  revalidatingFetchers.forEach(rf => fetchControllers.set(rf.key, pendingNavigationController));
2229
- let lazyMatches = matches.filter(m => m.route.lazy);
2230
-
2231
- if (lazyMatches.length > 0) {
2232
- await loadLazyRouteModules(lazyMatches, hasErrorBoundary, manifest);
2233
-
2234
- if (request.signal.aborted) {
2235
- return {
2236
- shortCircuited: true
2237
- };
2238
- }
2239
- }
2240
-
2241
2159
  let {
2242
2160
  results,
2243
2161
  loaderResults,
@@ -2303,7 +2221,8 @@ function createRouter(init) {
2303
2221
  }
2304
2222
 
2305
2223
  if (fetchControllers.has(key)) abortFetcher(key);
2306
- let matches = matchRoutes(dataRoutes, href, init.basename);
2224
+ let routesToUse = inFlightDataRoutes || dataRoutes;
2225
+ let matches = matchRoutes(routesToUse, href, init.basename);
2307
2226
 
2308
2227
  if (!matches) {
2309
2228
  setFetcherError(key, routeId, getInternalRouterError(404, {
@@ -2328,9 +2247,7 @@ function createRouter(init) {
2328
2247
 
2329
2248
  fetchLoadMatches.set(key, {
2330
2249
  routeId,
2331
- path,
2332
- match,
2333
- matches
2250
+ path
2334
2251
  });
2335
2252
  handleFetcherLoader(key, routeId, path, match, matches, submission);
2336
2253
  } // Call the action for the matched fetcher.submit(), and then handle redirects,
@@ -2341,7 +2258,7 @@ function createRouter(init) {
2341
2258
  interruptActiveLoads();
2342
2259
  fetchLoadMatches.delete(key);
2343
2260
 
2344
- if (!match.route.lazy && !match.route.action) {
2261
+ if (!match.route.action) {
2345
2262
  let error = getInternalRouterError(405, {
2346
2263
  method: submission.formMethod,
2347
2264
  pathname: path,
@@ -2369,25 +2286,6 @@ function createRouter(init) {
2369
2286
  let abortController = new AbortController();
2370
2287
  let fetchRequest = createClientSideRequest(init.history, path, abortController.signal, submission);
2371
2288
  fetchControllers.set(key, abortController);
2372
-
2373
- if (match.route.lazy) {
2374
- await loadLazyRouteModules([match], hasErrorBoundary, manifest);
2375
-
2376
- if (fetchRequest.signal.aborted) {
2377
- return;
2378
- }
2379
-
2380
- if (!match.route.action) {
2381
- let error = getInternalRouterError(405, {
2382
- method: submission.formMethod,
2383
- pathname: path,
2384
- routeId: routeId
2385
- });
2386
- setFetcherError(key, routeId, error);
2387
- return;
2388
- }
2389
- }
2390
-
2391
2289
  let actionResult = await callLoaderOrAction("action", fetchRequest, match, requestMatches, router.basename);
2392
2290
 
2393
2291
  if (fetchRequest.signal.aborted) {
@@ -2436,7 +2334,8 @@ function createRouter(init) {
2436
2334
 
2437
2335
  let nextLocation = state.navigation.location || state.location;
2438
2336
  let revalidationRequest = createClientSideRequest(init.history, nextLocation, abortController.signal);
2439
- let matches = state.navigation.state !== "idle" ? matchRoutes(dataRoutes, state.navigation.location, init.basename) : state.matches;
2337
+ let routesToUse = inFlightDataRoutes || dataRoutes;
2338
+ let matches = state.navigation.state !== "idle" ? matchRoutes(routesToUse, state.navigation.location, init.basename) : state.matches;
2440
2339
  invariant(matches, "Didn't find any matches after fetcher action");
2441
2340
  let loadId = ++incrementingLoadId;
2442
2341
  fetchReloadIds.set(key, loadId);
@@ -2449,10 +2348,10 @@ function createRouter(init) {
2449
2348
  });
2450
2349
 
2451
2350
  state.fetchers.set(key, loadFetcher);
2452
- let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(init.history, state, matches, submission, nextLocation, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, {
2351
+ let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(init.history, state, matches, submission, nextLocation, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, fetchLoadMatches, routesToUse, init.basename, {
2453
2352
  [match.route.id]: actionResult.data
2454
- }, undefined, // No need to send through errors since we short circuit above
2455
- fetchLoadMatches); // Put all revalidating fetchers into the loading state, except for the
2353
+ }, undefined // No need to send through errors since we short circuit above
2354
+ ); // Put all revalidating fetchers into the loading state, except for the
2456
2355
  // current fetcher which we want to keep in it's current loading state which
2457
2356
  // contains it's action submission info + action data
2458
2357
 
@@ -2474,16 +2373,6 @@ function createRouter(init) {
2474
2373
  updateState({
2475
2374
  fetchers: new Map(state.fetchers)
2476
2375
  });
2477
- let lazyMatches = matches.filter(m => m.route.lazy);
2478
-
2479
- if (match.route.lazy) {
2480
- await loadLazyRouteModules(lazyMatches, hasErrorBoundary, manifest);
2481
-
2482
- if (revalidationRequest.signal.aborted) {
2483
- return;
2484
- }
2485
- }
2486
-
2487
2376
  let {
2488
2377
  results,
2489
2378
  loaderResults,
@@ -2568,15 +2457,6 @@ function createRouter(init) {
2568
2457
  let abortController = new AbortController();
2569
2458
  let fetchRequest = createClientSideRequest(init.history, path, abortController.signal);
2570
2459
  fetchControllers.set(key, abortController);
2571
-
2572
- if (match.route.lazy) {
2573
- await loadLazyRouteModules([match], hasErrorBoundary, manifest);
2574
-
2575
- if (fetchRequest.signal.aborted) {
2576
- return;
2577
- }
2578
- }
2579
-
2580
2460
  let result = await callLoaderOrAction("loader", fetchRequest, match, matches, router.basename); // Deferred isn't supported for fetcher loads, await everything and treat it
2581
2461
  // as a normal load. resolveDeferredData will return undefined if this
2582
2462
  // fetcher gets aborted, so we just leave result untouched and short circuit
@@ -2745,7 +2625,19 @@ function createRouter(init) {
2745
2625
  // Call all navigation loaders and revalidating fetcher loaders in parallel,
2746
2626
  // then slice off the results into separate arrays so we can handle them
2747
2627
  // accordingly
2748
- let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, router.basename)), ...fetchersToLoad.map(f => callLoaderOrAction("loader", createClientSideRequest(init.history, f.path, request.signal), f.match, f.matches, router.basename))]);
2628
+ let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, router.basename)), ...fetchersToLoad.map(f => {
2629
+ if (f.matches && f.match) {
2630
+ return callLoaderOrAction("loader", createClientSideRequest(init.history, f.path, request.signal), f.match, f.matches, router.basename);
2631
+ } else {
2632
+ let error = {
2633
+ type: ResultType.error,
2634
+ error: getInternalRouterError(404, {
2635
+ pathname: f.path
2636
+ })
2637
+ };
2638
+ return error;
2639
+ }
2640
+ })]);
2749
2641
  let loaderResults = results.slice(0, matchesToLoad.length);
2750
2642
  let fetcherResults = results.slice(matchesToLoad.length);
2751
2643
  await Promise.all([resolveDeferredResults(currentMatches, matchesToLoad, loaderResults, request.signal, false, state.loaderData), resolveDeferredResults(currentMatches, fetchersToLoad.map(f => f.match), fetcherResults, request.signal, true)]);
@@ -2980,6 +2872,10 @@ function createRouter(init) {
2980
2872
  return null;
2981
2873
  }
2982
2874
 
2875
+ function _internalSetRoutes(newRoutes) {
2876
+ inFlightDataRoutes = newRoutes;
2877
+ }
2878
+
2983
2879
  router = {
2984
2880
  get basename() {
2985
2881
  return init.basename;
@@ -3009,7 +2905,10 @@ function createRouter(init) {
3009
2905
  getBlocker,
3010
2906
  deleteBlocker,
3011
2907
  _internalFetchControllers: fetchControllers,
3012
- _internalActiveDeferreds: activeDeferreds
2908
+ _internalActiveDeferreds: activeDeferreds,
2909
+ // TODO: Remove setRoutes, it's temporary to avoid dealing with
2910
+ // updating the tree while validating the update algorithm.
2911
+ _internalSetRoutes
3013
2912
  };
3014
2913
  return router;
3015
2914
  } //#endregion
@@ -3020,9 +2919,7 @@ function createRouter(init) {
3020
2919
  const UNSAFE_DEFERRED_SYMBOL = Symbol("deferred");
3021
2920
  function createStaticHandler(routes, opts) {
3022
2921
  invariant(routes.length > 0, "You must provide a non-empty routes array to createStaticHandler");
3023
- let manifest = {};
3024
- let hasErrorBoundary = (opts == null ? void 0 : opts.hasErrorBoundary) || defaultHasErrorBoundary;
3025
- let dataRoutes = convertRoutesToDataRoutes(routes, hasErrorBoundary, undefined, manifest);
2922
+ let dataRoutes = convertRoutesToDataRoutes(routes);
3026
2923
  let basename = (opts ? opts.basename : null) || "/";
3027
2924
  /**
3028
2925
  * The query() method is intended for document requests, in which we want to
@@ -3207,16 +3104,6 @@ function createStaticHandler(routes, opts) {
3207
3104
 
3208
3105
  async function queryImpl(request, location, matches, requestContext, routeMatch) {
3209
3106
  invariant(request.signal, "query()/queryRoute() requests must contain an AbortController signal");
3210
- let lazyMatches = matches.filter(m => m.route.lazy);
3211
-
3212
- if (lazyMatches.length > 0) {
3213
- await loadLazyRouteModules(lazyMatches, hasErrorBoundary, manifest);
3214
-
3215
- if (request.signal.aborted) {
3216
- let method = routeMatch != null ? "queryRoute" : "query";
3217
- throw new Error(method + "() call aborted");
3218
- }
3219
- }
3220
3107
 
3221
3108
  try {
3222
3109
  if (isMutationMethod(request.method.toLowerCase())) {
@@ -3522,7 +3409,7 @@ function getLoaderMatchesUntilBoundary(matches, boundaryId) {
3522
3409
  return boundaryMatches;
3523
3410
  }
3524
3411
 
3525
- function getMatchesToLoad(history, state, matches, submission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, pendingActionData, pendingError, fetchLoadMatches) {
3412
+ function getMatchesToLoad(history, state, matches, submission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, fetchLoadMatches, routesToUse, basename, pendingActionData, pendingError) {
3526
3413
  let actionResult = pendingError ? Object.values(pendingError)[0] : pendingActionData ? Object.values(pendingActionData)[0] : undefined;
3527
3414
  let currentUrl = history.createURL(state.location);
3528
3415
  let nextUrl = history.createURL(location);
@@ -3534,11 +3421,6 @@ function getMatchesToLoad(history, state, matches, submission, location, isReval
3534
3421
  let boundaryId = pendingError ? Object.keys(pendingError)[0] : undefined;
3535
3422
  let boundaryMatches = getLoaderMatchesUntilBoundary(matches, boundaryId);
3536
3423
  let navigationMatches = boundaryMatches.filter((match, index) => {
3537
- if (match.route.lazy) {
3538
- // We haven't loaded this route yet so we don't know if it's got a loader!
3539
- return true;
3540
- }
3541
-
3542
3424
  if (match.route.loader == null) {
3543
3425
  return false;
3544
3426
  } // Always call the loader on new route instances and pending defer cancellations
@@ -3566,36 +3448,56 @@ function getMatchesToLoad(history, state, matches, submission, location, isReval
3566
3448
  }); // Pick fetcher.loads that need to be revalidated
3567
3449
 
3568
3450
  let revalidatingFetchers = [];
3569
- fetchLoadMatches && fetchLoadMatches.forEach((f, key) => {
3451
+ fetchLoadMatches.forEach((f, key) => {
3452
+ // Don't revalidate if fetcher won't be present in the subsequent render
3570
3453
  if (!matches.some(m => m.route.id === f.routeId)) {
3571
- // This fetcher is not going to be present in the subsequent render so
3572
- // there's no need to revalidate it
3573
3454
  return;
3574
- } else if (cancelledFetcherLoads.includes(key)) {
3575
- // This fetcher was cancelled from a prior action submission - force reload
3455
+ }
3456
+
3457
+ let fetcherMatches = matchRoutes(routesToUse, f.path, basename); // If the fetcher path no longer matches, push it in with null matches so
3458
+ // we can trigger a 404 in callLoadersAndMaybeResolveData
3459
+
3460
+ if (!fetcherMatches) {
3576
3461
  revalidatingFetchers.push(_extends({
3577
3462
  key
3578
- }, f));
3579
- } else {
3580
- // Revalidating fetchers are decoupled from the route matches since they
3581
- // hit a static href, so they _always_ check shouldRevalidate and the
3582
- // default is strictly if a revalidation is explicitly required (action
3583
- // submissions, useRevalidator, X-Remix-Revalidate).
3584
- let shouldRevalidate = shouldRevalidateLoader(f.match, _extends({
3585
- currentUrl,
3586
- currentParams: state.matches[state.matches.length - 1].params,
3587
- nextUrl,
3588
- nextParams: matches[matches.length - 1].params
3589
- }, submission, {
3590
- actionResult,
3591
- defaultShouldRevalidate
3463
+ }, f, {
3464
+ matches: null,
3465
+ match: null
3592
3466
  }));
3467
+ return;
3468
+ }
3593
3469
 
3594
- if (shouldRevalidate) {
3595
- revalidatingFetchers.push(_extends({
3596
- key
3597
- }, f));
3598
- }
3470
+ let fetcherMatch = getTargetMatch(fetcherMatches, f.path);
3471
+
3472
+ if (cancelledFetcherLoads.includes(key)) {
3473
+ revalidatingFetchers.push(_extends({
3474
+ key,
3475
+ matches: fetcherMatches,
3476
+ match: fetcherMatch
3477
+ }, f));
3478
+ return;
3479
+ } // Revalidating fetchers are decoupled from the route matches since they
3480
+ // hit a static href, so they _always_ check shouldRevalidate and the
3481
+ // default is strictly if a revalidation is explicitly required (action
3482
+ // submissions, useRevalidator, X-Remix-Revalidate).
3483
+
3484
+
3485
+ let shouldRevalidate = shouldRevalidateLoader(fetcherMatch, _extends({
3486
+ currentUrl,
3487
+ currentParams: state.matches[state.matches.length - 1].params,
3488
+ nextUrl,
3489
+ nextParams: matches[matches.length - 1].params
3490
+ }, submission, {
3491
+ actionResult,
3492
+ defaultShouldRevalidate
3493
+ }));
3494
+
3495
+ if (shouldRevalidate) {
3496
+ revalidatingFetchers.push(_extends({
3497
+ key,
3498
+ matches: fetcherMatches,
3499
+ match: fetcherMatch
3500
+ }, f));
3599
3501
  }
3600
3502
  });
3601
3503
  return [navigationMatches, revalidatingFetchers];
@@ -3632,57 +3534,6 @@ function shouldRevalidateLoader(loaderMatch, arg) {
3632
3534
 
3633
3535
  return arg.defaultShouldRevalidate;
3634
3536
  }
3635
- /**
3636
- * Execute route.lazy() methods to lazily load route modules (loader, action,
3637
- * shouldRevalidate) and update the routeManifest in place which shares objects
3638
- * with dataRoutes so those get updated as well.
3639
- */
3640
-
3641
-
3642
- async function loadLazyRouteModules(lazyMatches, hasErrorBoundary, manifest) {
3643
- await Promise.all(lazyMatches.map(async match => {
3644
- let mod = await match.route.lazy(); // If the lazy route function has already been executed and removed from
3645
- // the route object by another call while we were waiting for the promise
3646
- // to resolve then we don't want to resolve the same route again.
3647
-
3648
- if (!match.route.lazy) {
3649
- return;
3650
- }
3651
-
3652
- let routeToUpdate = manifest[match.route.id];
3653
- invariant(routeToUpdate, "No route found in manifest"); // For now, we update in place. We think this is ok since there's no way
3654
- // we could yet be sitting on this route since we can't get there without
3655
- // resolving through here first. This is different than the HMR "update"
3656
- // use-case where we may actively be on the route being updated. The main
3657
- // concern boils down to "does this mutation affect any ongoing navigations
3658
- // or any current state.matches values?". If not, I think it's safe to
3659
- // mutate in place. It's also worth noting that this is a more targeted
3660
- // update that cannot touch things like path/index/children so it cannot
3661
- // affect the routes we've already matched.
3662
-
3663
- let routeUpdates = {};
3664
-
3665
- for (let k in mod) {
3666
- if (!immutableRouteKeys.has(k)) {
3667
- routeUpdates[k] = mod[k];
3668
- }
3669
- } // Mutate the route with the provided updates. Do this first so we pass
3670
- // the updated version to hasErrorBoundary
3671
-
3672
-
3673
- Object.assign(routeToUpdate, routeUpdates); // Mutate the `hasErrorBoundary` property on the route based on the route
3674
- // updates and remove the `lazy` function so we don't resolve the lazy
3675
- // route again.
3676
-
3677
- Object.assign(routeToUpdate, {
3678
- // To keep things framework agnostic, we use the provided
3679
- // `hasErrorBoundary` function to set the `hasErrorBoundary` route
3680
- // property since the logic will differ between frameworks.
3681
- hasErrorBoundary: hasErrorBoundary(_extends({}, routeToUpdate)),
3682
- lazy: undefined
3683
- });
3684
- }));
3685
- }
3686
3537
 
3687
3538
  async function callLoaderOrAction(type, request, match, matches, basename, isStaticRequest, isRouteRequest, requestContext) {
3688
3539
  if (basename === void 0) {
@@ -3816,9 +3667,13 @@ async function callLoaderOrAction(type, request, match, matches, basename, isSta
3816
3667
  }
3817
3668
 
3818
3669
  if (result instanceof DeferredData) {
3670
+ var _result$init, _result$init2;
3671
+
3819
3672
  return {
3820
3673
  type: ResultType.deferred,
3821
- deferredData: result
3674
+ deferredData: result,
3675
+ statusCode: (_result$init = result.init) == null ? void 0 : _result$init.status,
3676
+ headers: ((_result$init2 = result.init) == null ? void 0 : _result$init2.headers) && new Headers(result.init.headers)
3822
3677
  };
3823
3678
  }
3824
3679
 
@@ -3955,7 +3810,7 @@ function processLoaderData(state, matches, matchesToLoad, results, pendingError,
3955
3810
  let result = fetcherResults[index]; // Process fetcher non-redirect errors
3956
3811
 
3957
3812
  if (isErrorResult(result)) {
3958
- let boundaryMatch = findNearestBoundary(state.matches, match.route.id);
3813
+ let boundaryMatch = findNearestBoundary(state.matches, match == null ? void 0 : match.route.id);
3959
3814
 
3960
3815
  if (!(errors && errors[boundaryMatch.route.id])) {
3961
3816
  errors = _extends({}, errors, {
@@ -4002,7 +3857,9 @@ function mergeLoaderData(loaderData, newLoaderData, matches, errors) {
4002
3857
  if (newLoaderData[id] !== undefined) {
4003
3858
  mergedLoaderData[id] = newLoaderData[id];
4004
3859
  }
4005
- } else if (loaderData[id] !== undefined) {
3860
+ } else if (loaderData[id] !== undefined && match.route.loader) {
3861
+ // Preserve existing keys not included in newLoaderData and where a loader
3862
+ // wasn't removed by HMR
4006
3863
  mergedLoaderData[id] = loaderData[id];
4007
3864
  }
4008
3865
 
@@ -4139,7 +3996,14 @@ function isMutationMethod(method) {
4139
3996
  async function resolveDeferredResults(currentMatches, matchesToLoad, results, signal, isFetcher, currentLoaderData) {
4140
3997
  for (let index = 0; index < results.length; index++) {
4141
3998
  let result = results[index];
4142
- let match = matchesToLoad[index];
3999
+ let match = matchesToLoad[index]; // If we don't have a match, then we can have a deferred result to do
4000
+ // anything with. This is for revalidating fetchers where the route was
4001
+ // removed during HMR
4002
+
4003
+ if (!match) {
4004
+ continue;
4005
+ }
4006
+
4143
4007
  let currentMatch = currentMatches.find(m => m.route.id === match.route.id);
4144
4008
  let isRevalidatingLoader = currentMatch != null && !isNewRouteInstance(currentMatch, match) && (currentLoaderData && currentLoaderData[match.route.id]) !== undefined;
4145
4009