@remix-run/router 1.19.1 → 1.19.2-pre.0

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/router.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @remix-run/router v1.19.1
2
+ * @remix-run/router v1.19.2-pre.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1550,7 +1550,7 @@ function createRouter(init) {
1550
1550
  let pendingPatchRoutes = new Map();
1551
1551
  // Flag to ignore the next history update, so we can revert the URL change on
1552
1552
  // a POP navigation that was blocked by the user without touching router state
1553
- let ignoreNextHistoryUpdate = false;
1553
+ let unblockBlockerHistoryUpdate = undefined;
1554
1554
  // Initialize the router, all side effects should be kicked off from here.
1555
1555
  // Implemented as a Fluent API for ease of:
1556
1556
  // let router = createRouter(init).initialize();
@@ -1565,8 +1565,9 @@ function createRouter(init) {
1565
1565
  } = _ref;
1566
1566
  // Ignore this event if it was just us resetting the URL from a
1567
1567
  // blocked POP navigation
1568
- if (ignoreNextHistoryUpdate) {
1569
- ignoreNextHistoryUpdate = false;
1568
+ if (unblockBlockerHistoryUpdate) {
1569
+ unblockBlockerHistoryUpdate();
1570
+ unblockBlockerHistoryUpdate = undefined;
1570
1571
  return;
1571
1572
  }
1572
1573
  warning(blockerFunctions.size === 0 || delta != null, "You are trying to use a blocker on a POP navigation to a location " + "that was not created by @remix-run/router. This will fail silently in " + "production. This can happen if you are navigating outside the router " + "via `window.history.pushState`/`window.location.hash` instead of using " + "router navigation APIs. This can also happen if you are using " + "createHashRouter and the user manually changes the URL.");
@@ -1577,7 +1578,9 @@ function createRouter(init) {
1577
1578
  });
1578
1579
  if (blockerKey && delta != null) {
1579
1580
  // Restore the URL to match the current UI, but don't update router state
1580
- ignoreNextHistoryUpdate = true;
1581
+ let nextHistoryUpdatePromise = new Promise(resolve => {
1582
+ unblockBlockerHistoryUpdate = resolve;
1583
+ });
1581
1584
  init.history.go(delta * -1);
1582
1585
  // Put the blocker into a blocked state
1583
1586
  updateBlocker(blockerKey, {
@@ -1590,8 +1593,10 @@ function createRouter(init) {
1590
1593
  reset: undefined,
1591
1594
  location
1592
1595
  });
1593
- // Re-do the same POP navigation we just blocked
1594
- init.history.go(delta);
1596
+ // Re-do the same POP navigation we just blocked, after the url
1597
+ // restoration is also complete. See:
1598
+ // https://github.com/remix-run/react-router/issues/11613
1599
+ nextHistoryUpdatePromise.then(() => init.history.go(delta));
1595
1600
  },
1596
1601
  reset() {
1597
1602
  let blockers = new Map(state.blockers);
@@ -1890,7 +1895,9 @@ function createRouter(init) {
1890
1895
  // navigation to the navigation.location but do not trigger an uninterrupted
1891
1896
  // revalidation so that history correctly updates once the navigation completes
1892
1897
  startNavigation(pendingAction || state.historyAction, state.navigation.location, {
1893
- overrideNavigation: state.navigation
1898
+ overrideNavigation: state.navigation,
1899
+ // Proxy through any rending view transition
1900
+ enableViewTransition: pendingViewTransitionEnabled === true
1894
1901
  });
1895
1902
  }
1896
1903
  // Start a navigation to the given action/location. Can optionally provide a
@@ -2079,8 +2086,8 @@ function createRouter(init) {
2079
2086
  })
2080
2087
  };
2081
2088
  } else {
2082
- let results = await callDataStrategy("action", request, [actionMatch], matches);
2083
- result = results[0];
2089
+ let results = await callDataStrategy("action", state, request, [actionMatch], matches, null);
2090
+ result = results[actionMatch.route.id];
2084
2091
  if (request.signal.aborted) {
2085
2092
  return {
2086
2093
  shortCircuited: true
@@ -2098,7 +2105,7 @@ function createRouter(init) {
2098
2105
  let location = normalizeRedirectLocation(result.response.headers.get("Location"), new URL(request.url), basename);
2099
2106
  replace = location === state.location.pathname + state.location.search;
2100
2107
  }
2101
- await startRedirectNavigation(request, result, {
2108
+ await startRedirectNavigation(request, result, true, {
2102
2109
  submission,
2103
2110
  replace
2104
2111
  });
@@ -2260,7 +2267,7 @@ function createRouter(init) {
2260
2267
  let {
2261
2268
  loaderResults,
2262
2269
  fetcherResults
2263
- } = await callLoadersAndMaybeResolveData(state.matches, matches, matchesToLoad, revalidatingFetchers, request);
2270
+ } = await callLoadersAndMaybeResolveData(state, matches, matchesToLoad, revalidatingFetchers, request);
2264
2271
  if (request.signal.aborted) {
2265
2272
  return {
2266
2273
  shortCircuited: true
@@ -2274,16 +2281,22 @@ function createRouter(init) {
2274
2281
  }
2275
2282
  revalidatingFetchers.forEach(rf => fetchControllers.delete(rf.key));
2276
2283
  // If any loaders returned a redirect Response, start a new REPLACE navigation
2277
- let redirect = findRedirect([...loaderResults, ...fetcherResults]);
2284
+ let redirect = findRedirect(loaderResults);
2278
2285
  if (redirect) {
2279
- if (redirect.idx >= matchesToLoad.length) {
2280
- // If this redirect came from a fetcher make sure we mark it in
2281
- // fetchRedirectIds so it doesn't get revalidated on the next set of
2282
- // loader executions
2283
- let fetcherKey = revalidatingFetchers[redirect.idx - matchesToLoad.length].key;
2284
- fetchRedirectIds.add(fetcherKey);
2285
- }
2286
- await startRedirectNavigation(request, redirect.result, {
2286
+ await startRedirectNavigation(request, redirect.result, true, {
2287
+ replace
2288
+ });
2289
+ return {
2290
+ shortCircuited: true
2291
+ };
2292
+ }
2293
+ redirect = findRedirect(fetcherResults);
2294
+ if (redirect) {
2295
+ // If this redirect came from a fetcher make sure we mark it in
2296
+ // fetchRedirectIds so it doesn't get revalidated on the next set of
2297
+ // loader executions
2298
+ fetchRedirectIds.add(redirect.key);
2299
+ await startRedirectNavigation(request, redirect.result, true, {
2287
2300
  replace
2288
2301
  });
2289
2302
  return {
@@ -2459,8 +2472,8 @@ function createRouter(init) {
2459
2472
  // Call the action for the fetcher
2460
2473
  fetchControllers.set(key, abortController);
2461
2474
  let originatingLoadId = incrementingLoadId;
2462
- let actionResults = await callDataStrategy("action", fetchRequest, [match], requestMatches);
2463
- let actionResult = actionResults[0];
2475
+ let actionResults = await callDataStrategy("action", state, fetchRequest, [match], requestMatches, key);
2476
+ let actionResult = actionResults[match.route.id];
2464
2477
  if (fetchRequest.signal.aborted) {
2465
2478
  // We can delete this so long as we weren't aborted by our own fetcher
2466
2479
  // re-submit which would have put _new_ controller is in fetchControllers
@@ -2491,7 +2504,7 @@ function createRouter(init) {
2491
2504
  } else {
2492
2505
  fetchRedirectIds.add(key);
2493
2506
  updateFetcherState(key, getLoadingFetcher(submission));
2494
- return startRedirectNavigation(fetchRequest, actionResult, {
2507
+ return startRedirectNavigation(fetchRequest, actionResult, false, {
2495
2508
  fetcherSubmission: submission
2496
2509
  });
2497
2510
  }
@@ -2542,7 +2555,7 @@ function createRouter(init) {
2542
2555
  let {
2543
2556
  loaderResults,
2544
2557
  fetcherResults
2545
- } = await callLoadersAndMaybeResolveData(state.matches, matches, matchesToLoad, revalidatingFetchers, revalidationRequest);
2558
+ } = await callLoadersAndMaybeResolveData(state, matches, matchesToLoad, revalidatingFetchers, revalidationRequest);
2546
2559
  if (abortController.signal.aborted) {
2547
2560
  return;
2548
2561
  }
@@ -2550,22 +2563,23 @@ function createRouter(init) {
2550
2563
  fetchReloadIds.delete(key);
2551
2564
  fetchControllers.delete(key);
2552
2565
  revalidatingFetchers.forEach(r => fetchControllers.delete(r.key));
2553
- let redirect = findRedirect([...loaderResults, ...fetcherResults]);
2566
+ let redirect = findRedirect(loaderResults);
2554
2567
  if (redirect) {
2555
- if (redirect.idx >= matchesToLoad.length) {
2556
- // If this redirect came from a fetcher make sure we mark it in
2557
- // fetchRedirectIds so it doesn't get revalidated on the next set of
2558
- // loader executions
2559
- let fetcherKey = revalidatingFetchers[redirect.idx - matchesToLoad.length].key;
2560
- fetchRedirectIds.add(fetcherKey);
2561
- }
2562
- return startRedirectNavigation(revalidationRequest, redirect.result);
2568
+ return startRedirectNavigation(revalidationRequest, redirect.result, false);
2569
+ }
2570
+ redirect = findRedirect(fetcherResults);
2571
+ if (redirect) {
2572
+ // If this redirect came from a fetcher make sure we mark it in
2573
+ // fetchRedirectIds so it doesn't get revalidated on the next set of
2574
+ // loader executions
2575
+ fetchRedirectIds.add(redirect.key);
2576
+ return startRedirectNavigation(revalidationRequest, redirect.result, false);
2563
2577
  }
2564
2578
  // Process and commit output from loaders
2565
2579
  let {
2566
2580
  loaderData,
2567
2581
  errors
2568
- } = processLoaderData(state, state.matches, matchesToLoad, loaderResults, undefined, revalidatingFetchers, fetcherResults, activeDeferreds);
2582
+ } = processLoaderData(state, matches, matchesToLoad, loaderResults, undefined, revalidatingFetchers, fetcherResults, activeDeferreds);
2569
2583
  // Since we let revalidations complete even if the submitting fetcher was
2570
2584
  // deleted, only put it back to idle if it hasn't been deleted
2571
2585
  if (state.fetchers.has(key)) {
@@ -2632,8 +2646,8 @@ function createRouter(init) {
2632
2646
  // Call the loader for this fetcher route match
2633
2647
  fetchControllers.set(key, abortController);
2634
2648
  let originatingLoadId = incrementingLoadId;
2635
- let results = await callDataStrategy("loader", fetchRequest, [match], matches);
2636
- let result = results[0];
2649
+ let results = await callDataStrategy("loader", state, fetchRequest, [match], matches, key);
2650
+ let result = results[match.route.id];
2637
2651
  // Deferred isn't supported for fetcher loads, await everything and treat it
2638
2652
  // as a normal load. resolveDeferredData will return undefined if this
2639
2653
  // fetcher gets aborted, so we just leave result untouched and short circuit
@@ -2664,7 +2678,7 @@ function createRouter(init) {
2664
2678
  return;
2665
2679
  } else {
2666
2680
  fetchRedirectIds.add(key);
2667
- await startRedirectNavigation(fetchRequest, result);
2681
+ await startRedirectNavigation(fetchRequest, result, false);
2668
2682
  return;
2669
2683
  }
2670
2684
  }
@@ -2696,7 +2710,7 @@ function createRouter(init) {
2696
2710
  * actually touch history until we've processed redirects, so we just use
2697
2711
  * the history action from the original navigation (PUSH or REPLACE).
2698
2712
  */
2699
- async function startRedirectNavigation(request, redirect, _temp2) {
2713
+ async function startRedirectNavigation(request, redirect, isNavigation, _temp2) {
2700
2714
  let {
2701
2715
  submission,
2702
2716
  fetcherSubmission,
@@ -2756,8 +2770,9 @@ function createRouter(init) {
2756
2770
  submission: _extends({}, activeSubmission, {
2757
2771
  formAction: location
2758
2772
  }),
2759
- // Preserve this flag across redirects
2760
- preventScrollReset: pendingPreventScrollReset
2773
+ // Preserve these flags across redirects
2774
+ preventScrollReset: pendingPreventScrollReset,
2775
+ enableViewTransition: isNavigation ? pendingViewTransitionEnabled : undefined
2761
2776
  });
2762
2777
  } else {
2763
2778
  // If we have a navigation submission, we will preserve it through the
@@ -2767,50 +2782,69 @@ function createRouter(init) {
2767
2782
  overrideNavigation,
2768
2783
  // Send fetcher submissions through for shouldRevalidate
2769
2784
  fetcherSubmission,
2770
- // Preserve this flag across redirects
2771
- preventScrollReset: pendingPreventScrollReset
2785
+ // Preserve these flags across redirects
2786
+ preventScrollReset: pendingPreventScrollReset,
2787
+ enableViewTransition: isNavigation ? pendingViewTransitionEnabled : undefined
2772
2788
  });
2773
2789
  }
2774
2790
  }
2775
2791
  // Utility wrapper for calling dataStrategy client-side without having to
2776
2792
  // pass around the manifest, mapRouteProperties, etc.
2777
- async function callDataStrategy(type, request, matchesToLoad, matches) {
2793
+ async function callDataStrategy(type, state, request, matchesToLoad, matches, fetcherKey) {
2794
+ let results;
2795
+ let dataResults = {};
2778
2796
  try {
2779
- let results = await callDataStrategyImpl(dataStrategyImpl, type, request, matchesToLoad, matches, manifest, mapRouteProperties);
2780
- return await Promise.all(results.map((result, i) => {
2781
- if (isRedirectHandlerResult(result)) {
2782
- let response = result.result;
2783
- return {
2784
- type: ResultType.redirect,
2785
- response: normalizeRelativeRoutingRedirectResponse(response, request, matchesToLoad[i].route.id, matches, basename, future.v7_relativeSplatPath)
2786
- };
2787
- }
2788
- return convertHandlerResultToDataResult(result);
2789
- }));
2797
+ results = await callDataStrategyImpl(dataStrategyImpl, type, state, request, matchesToLoad, matches, fetcherKey, manifest, mapRouteProperties);
2790
2798
  } catch (e) {
2791
2799
  // If the outer dataStrategy method throws, just return the error for all
2792
2800
  // matches - and it'll naturally bubble to the root
2793
- return matchesToLoad.map(() => ({
2794
- type: ResultType.error,
2795
- error: e
2796
- }));
2801
+ matchesToLoad.forEach(m => {
2802
+ dataResults[m.route.id] = {
2803
+ type: ResultType.error,
2804
+ error: e
2805
+ };
2806
+ });
2807
+ return dataResults;
2808
+ }
2809
+ for (let [routeId, result] of Object.entries(results)) {
2810
+ if (isRedirectDataStrategyResultResult(result)) {
2811
+ let response = result.result;
2812
+ dataResults[routeId] = {
2813
+ type: ResultType.redirect,
2814
+ response: normalizeRelativeRoutingRedirectResponse(response, request, routeId, matches, basename, future.v7_relativeSplatPath)
2815
+ };
2816
+ } else {
2817
+ dataResults[routeId] = await convertDataStrategyResultToDataResult(result);
2818
+ }
2797
2819
  }
2820
+ return dataResults;
2798
2821
  }
2799
- async function callLoadersAndMaybeResolveData(currentMatches, matches, matchesToLoad, fetchersToLoad, request) {
2800
- let [loaderResults, ...fetcherResults] = await Promise.all([matchesToLoad.length ? callDataStrategy("loader", request, matchesToLoad, matches) : [], ...fetchersToLoad.map(f => {
2822
+ async function callLoadersAndMaybeResolveData(state, matches, matchesToLoad, fetchersToLoad, request) {
2823
+ let currentMatches = state.matches;
2824
+ // Kick off loaders and fetchers in parallel
2825
+ let loaderResultsPromise = callDataStrategy("loader", state, request, matchesToLoad, matches, null);
2826
+ let fetcherResultsPromise = Promise.all(fetchersToLoad.map(async f => {
2801
2827
  if (f.matches && f.match && f.controller) {
2802
- let fetcherRequest = createClientSideRequest(init.history, f.path, f.controller.signal);
2803
- return callDataStrategy("loader", fetcherRequest, [f.match], f.matches).then(r => r[0]);
2828
+ let results = await callDataStrategy("loader", state, createClientSideRequest(init.history, f.path, f.controller.signal), [f.match], f.matches, f.key);
2829
+ let result = results[f.match.route.id];
2830
+ // Fetcher results are keyed by fetcher key from here on out, not routeId
2831
+ return {
2832
+ [f.key]: result
2833
+ };
2804
2834
  } else {
2805
2835
  return Promise.resolve({
2806
- type: ResultType.error,
2807
- error: getInternalRouterError(404, {
2808
- pathname: f.path
2809
- })
2836
+ [f.key]: {
2837
+ type: ResultType.error,
2838
+ error: getInternalRouterError(404, {
2839
+ pathname: f.path
2840
+ })
2841
+ }
2810
2842
  });
2811
2843
  }
2812
- })]);
2813
- await Promise.all([resolveDeferredResults(currentMatches, matchesToLoad, loaderResults, loaderResults.map(() => request.signal), false, state.loaderData), resolveDeferredResults(currentMatches, fetchersToLoad.map(f => f.match), fetcherResults, fetchersToLoad.map(f => f.controller ? f.controller.signal : null), true)]);
2844
+ }));
2845
+ let loaderResults = await loaderResultsPromise;
2846
+ let fetcherResults = (await fetcherResultsPromise).reduce((acc, r) => Object.assign(acc, r), {});
2847
+ await Promise.all([resolveNavigationDeferredResults(matches, loaderResults, request.signal, currentMatches, state.loaderData), resolveFetcherDeferredResults(matches, fetcherResults, fetchersToLoad)]);
2814
2848
  return {
2815
2849
  loaderResults,
2816
2850
  fetcherResults
@@ -3453,9 +3487,9 @@ function createStaticHandler(routes, opts) {
3453
3487
  });
3454
3488
  } catch (e) {
3455
3489
  // If the user threw/returned a Response in callLoaderOrAction for a
3456
- // `queryRoute` call, we throw the `HandlerResult` to bail out early
3490
+ // `queryRoute` call, we throw the `DataStrategyResult` to bail out early
3457
3491
  // and then return or throw the raw Response here accordingly
3458
- if (isHandlerResult(e) && isResponse(e.result)) {
3492
+ if (isDataStrategyResult(e) && isResponse(e.result)) {
3459
3493
  if (e.type === ResultType.error) {
3460
3494
  throw e.result;
3461
3495
  }
@@ -3486,7 +3520,7 @@ function createStaticHandler(routes, opts) {
3486
3520
  };
3487
3521
  } else {
3488
3522
  let results = await callDataStrategy("action", request, [actionMatch], matches, isRouteRequest, requestContext, unstable_dataStrategy);
3489
- result = results[0];
3523
+ result = results[actionMatch.route.id];
3490
3524
  if (request.signal.aborted) {
3491
3525
  throwStaticHandlerAbortedError(request, isRouteRequest, future);
3492
3526
  }
@@ -3603,7 +3637,7 @@ function createStaticHandler(routes, opts) {
3603
3637
  }
3604
3638
  // Process and commit output from loaders
3605
3639
  let activeDeferreds = new Map();
3606
- let context = processRouteLoaderData(matches, matchesToLoad, results, pendingActionResult, activeDeferreds, skipLoaderErrorBubbling);
3640
+ let context = processRouteLoaderData(matches, results, pendingActionResult, activeDeferreds, skipLoaderErrorBubbling);
3607
3641
  // Add a null for any non-loader matches for proper revalidation on the client
3608
3642
  let executedLoaders = new Set(matchesToLoad.map(match => match.route.id));
3609
3643
  matches.forEach(match => {
@@ -3619,20 +3653,26 @@ function createStaticHandler(routes, opts) {
3619
3653
  // Utility wrapper for calling dataStrategy server-side without having to
3620
3654
  // pass around the manifest, mapRouteProperties, etc.
3621
3655
  async function callDataStrategy(type, request, matchesToLoad, matches, isRouteRequest, requestContext, unstable_dataStrategy) {
3622
- let results = await callDataStrategyImpl(unstable_dataStrategy || defaultDataStrategy, type, request, matchesToLoad, matches, manifest, mapRouteProperties, requestContext);
3623
- return await Promise.all(results.map((result, i) => {
3624
- if (isRedirectHandlerResult(result)) {
3656
+ let results = await callDataStrategyImpl(unstable_dataStrategy || defaultDataStrategy, type, null, request, matchesToLoad, matches, null, manifest, mapRouteProperties, requestContext);
3657
+ let dataResults = {};
3658
+ await Promise.all(matches.map(async match => {
3659
+ if (!(match.route.id in results)) {
3660
+ return;
3661
+ }
3662
+ let result = results[match.route.id];
3663
+ if (isRedirectDataStrategyResultResult(result)) {
3625
3664
  let response = result.result;
3626
3665
  // Throw redirects and let the server handle them with an HTTP redirect
3627
- throw normalizeRelativeRoutingRedirectResponse(response, request, matchesToLoad[i].route.id, matches, basename, future.v7_relativeSplatPath);
3666
+ throw normalizeRelativeRoutingRedirectResponse(response, request, match.route.id, matches, basename, future.v7_relativeSplatPath);
3628
3667
  }
3629
3668
  if (isResponse(result.result) && isRouteRequest) {
3630
3669
  // For SSR single-route requests, we want to hand Responses back
3631
3670
  // directly without unwrapping
3632
3671
  throw result;
3633
3672
  }
3634
- return convertHandlerResultToDataResult(result);
3673
+ dataResults[match.route.id] = await convertDataStrategyResultToDataResult(result);
3635
3674
  }));
3675
+ return dataResults;
3636
3676
  }
3637
3677
  return {
3638
3678
  dataRoutes,
@@ -4094,52 +4134,67 @@ async function loadLazyRouteModule(route, mapRouteProperties, manifest) {
4094
4134
  }));
4095
4135
  }
4096
4136
  // Default implementation of `dataStrategy` which fetches all loaders in parallel
4097
- function defaultDataStrategy(opts) {
4098
- return Promise.all(opts.matches.map(m => m.resolve()));
4137
+ async function defaultDataStrategy(_ref6) {
4138
+ let {
4139
+ matches
4140
+ } = _ref6;
4141
+ let matchesToLoad = matches.filter(m => m.shouldLoad);
4142
+ let results = await Promise.all(matchesToLoad.map(m => m.resolve()));
4143
+ return results.reduce((acc, result, i) => Object.assign(acc, {
4144
+ [matchesToLoad[i].route.id]: result
4145
+ }), {});
4099
4146
  }
4100
- async function callDataStrategyImpl(dataStrategyImpl, type, request, matchesToLoad, matches, manifest, mapRouteProperties, requestContext) {
4101
- let routeIdsToLoad = matchesToLoad.reduce((acc, m) => acc.add(m.route.id), new Set());
4102
- let loadedMatches = new Set();
4147
+ async function callDataStrategyImpl(dataStrategyImpl, type, state, request, matchesToLoad, matches, fetcherKey, manifest, mapRouteProperties, requestContext) {
4148
+ let loadRouteDefinitionsPromises = matches.map(m => m.route.lazy ? loadLazyRouteModule(m.route, mapRouteProperties, manifest) : undefined);
4149
+ let dsMatches = matches.map((match, i) => {
4150
+ let loadRoutePromise = loadRouteDefinitionsPromises[i];
4151
+ let shouldLoad = matchesToLoad.some(m => m.route.id === match.route.id);
4152
+ // `resolve` encapsulates route.lazy(), executing the loader/action,
4153
+ // and mapping return values/thrown errors to a `DataStrategyResult`. Users
4154
+ // can pass a callback to take fine-grained control over the execution
4155
+ // of the loader/action
4156
+ let resolve = async handlerOverride => {
4157
+ if (handlerOverride && request.method === "GET" && (match.route.lazy || match.route.loader)) {
4158
+ shouldLoad = true;
4159
+ }
4160
+ return shouldLoad ? callLoaderOrAction(type, request, match, loadRoutePromise, handlerOverride, requestContext) : Promise.resolve({
4161
+ type: ResultType.data,
4162
+ result: undefined
4163
+ });
4164
+ };
4165
+ return _extends({}, match, {
4166
+ shouldLoad,
4167
+ resolve
4168
+ });
4169
+ });
4103
4170
  // Send all matches here to allow for a middleware-type implementation.
4104
4171
  // handler will be a no-op for unneeded routes and we filter those results
4105
4172
  // back out below.
4106
4173
  let results = await dataStrategyImpl({
4107
- matches: matches.map(match => {
4108
- let shouldLoad = routeIdsToLoad.has(match.route.id);
4109
- // `resolve` encapsulates the route.lazy, executing the
4110
- // loader/action, and mapping return values/thrown errors to a
4111
- // HandlerResult. Users can pass a callback to take fine-grained control
4112
- // over the execution of the loader/action
4113
- let resolve = handlerOverride => {
4114
- loadedMatches.add(match.route.id);
4115
- return shouldLoad ? callLoaderOrAction(type, request, match, manifest, mapRouteProperties, handlerOverride, requestContext) : Promise.resolve({
4116
- type: ResultType.data,
4117
- result: undefined
4118
- });
4119
- };
4120
- return _extends({}, match, {
4121
- shouldLoad,
4122
- resolve
4123
- });
4124
- }),
4174
+ matches: dsMatches,
4125
4175
  request,
4126
4176
  params: matches[0].params,
4177
+ fetcherKey,
4127
4178
  context: requestContext
4128
4179
  });
4129
- // Throw if any loadRoute implementations not called since they are what
4130
- // ensures a route is fully loaded
4131
- matches.forEach(m => invariant(loadedMatches.has(m.route.id), "`match.resolve()` was not called for route id \"" + m.route.id + "\". " + "You must call `match.resolve()` on every match passed to " + "`dataStrategy` to ensure all routes are properly loaded."));
4132
- // Filter out any middleware-only matches for which we didn't need to run handlers
4133
- return results.filter((_, i) => routeIdsToLoad.has(matches[i].route.id));
4180
+ // Wait for all routes to load here but 'swallow the error since we want
4181
+ // it to bubble up from the `await loadRoutePromise` in `callLoaderOrAction` -
4182
+ // called from `match.resolve()`
4183
+ try {
4184
+ await Promise.all(loadRouteDefinitionsPromises);
4185
+ } catch (e) {
4186
+ // No-op
4187
+ }
4188
+ return results;
4134
4189
  }
4135
4190
  // Default logic for calling a loader/action is the user has no specified a dataStrategy
4136
- async function callLoaderOrAction(type, request, match, manifest, mapRouteProperties, handlerOverride, staticContext) {
4191
+ async function callLoaderOrAction(type, request, match, loadRoutePromise, handlerOverride, staticContext) {
4137
4192
  let result;
4138
4193
  let onReject;
4139
4194
  let runHandler = handler => {
4140
4195
  // Setup a promise we can race against so that abort signals short circuit
4141
4196
  let reject;
4142
- // This will never resolve so safe to type it as Promise<HandlerResult> to
4197
+ // This will never resolve so safe to type it as Promise<DataStrategyResult> to
4143
4198
  // satisfy the function return value
4144
4199
  let abortPromise = new Promise((_, r) => reject = r);
4145
4200
  onReject = () => reject();
@@ -4154,30 +4209,26 @@ async function callLoaderOrAction(type, request, match, manifest, mapRouteProper
4154
4209
  context: staticContext
4155
4210
  }, ...(ctx !== undefined ? [ctx] : []));
4156
4211
  };
4157
- let handlerPromise;
4158
- if (handlerOverride) {
4159
- handlerPromise = handlerOverride(ctx => actualHandler(ctx));
4160
- } else {
4161
- handlerPromise = (async () => {
4162
- try {
4163
- let val = await actualHandler();
4164
- return {
4165
- type: "data",
4166
- result: val
4167
- };
4168
- } catch (e) {
4169
- return {
4170
- type: "error",
4171
- result: e
4172
- };
4173
- }
4174
- })();
4175
- }
4212
+ let handlerPromise = (async () => {
4213
+ try {
4214
+ let val = await (handlerOverride ? handlerOverride(ctx => actualHandler(ctx)) : actualHandler());
4215
+ return {
4216
+ type: "data",
4217
+ result: val
4218
+ };
4219
+ } catch (e) {
4220
+ return {
4221
+ type: "error",
4222
+ result: e
4223
+ };
4224
+ }
4225
+ })();
4176
4226
  return Promise.race([handlerPromise, abortPromise]);
4177
4227
  };
4178
4228
  try {
4179
4229
  let handler = match.route[type];
4180
- if (match.route.lazy) {
4230
+ // If we have a route.lazy promise, await that first
4231
+ if (loadRoutePromise) {
4181
4232
  if (handler) {
4182
4233
  // Run statically defined handler in parallel with lazy()
4183
4234
  let handlerError;
@@ -4187,14 +4238,14 @@ async function callLoaderOrAction(type, request, match, manifest, mapRouteProper
4187
4238
  // route has a boundary that can handle the error
4188
4239
  runHandler(handler).catch(e => {
4189
4240
  handlerError = e;
4190
- }), loadLazyRouteModule(match.route, mapRouteProperties, manifest)]);
4241
+ }), loadRoutePromise]);
4191
4242
  if (handlerError !== undefined) {
4192
4243
  throw handlerError;
4193
4244
  }
4194
4245
  result = value;
4195
4246
  } else {
4196
4247
  // Load lazy route module, then run any returned handler
4197
- await loadLazyRouteModule(match.route, mapRouteProperties, manifest);
4248
+ await loadRoutePromise;
4198
4249
  handler = match.route[type];
4199
4250
  if (handler) {
4200
4251
  // Handler still runs even if we got interrupted to maintain consistency
@@ -4230,7 +4281,7 @@ async function callLoaderOrAction(type, request, match, manifest, mapRouteProper
4230
4281
  invariant(result.result !== undefined, "You defined " + (type === "action" ? "an action" : "a loader") + " for route " + ("\"" + match.route.id + "\" but didn't return anything from your `" + type + "` ") + "function. Please return a value or `null`.");
4231
4282
  } catch (e) {
4232
4283
  // We should already be catching and converting normal handler executions to
4233
- // HandlerResults and returning them, so anything that throws here is an
4284
+ // DataStrategyResults and returning them, so anything that throws here is an
4234
4285
  // unexpected error we still need to wrap
4235
4286
  return {
4236
4287
  type: ResultType.error,
@@ -4243,11 +4294,11 @@ async function callLoaderOrAction(type, request, match, manifest, mapRouteProper
4243
4294
  }
4244
4295
  return result;
4245
4296
  }
4246
- async function convertHandlerResultToDataResult(handlerResult) {
4297
+ async function convertDataStrategyResultToDataResult(dataStrategyResult) {
4247
4298
  let {
4248
4299
  result,
4249
4300
  type
4250
- } = handlerResult;
4301
+ } = dataStrategyResult;
4251
4302
  if (isResponse(result)) {
4252
4303
  let data;
4253
4304
  try {
@@ -4400,7 +4451,7 @@ function convertSearchParamsToFormData(searchParams) {
4400
4451
  }
4401
4452
  return formData;
4402
4453
  }
4403
- function processRouteLoaderData(matches, matchesToLoad, results, pendingActionResult, activeDeferreds, skipLoaderErrorBubbling) {
4454
+ function processRouteLoaderData(matches, results, pendingActionResult, activeDeferreds, skipLoaderErrorBubbling) {
4404
4455
  // Fill in loaderData/errors from our loaders
4405
4456
  let loaderData = {};
4406
4457
  let errors = null;
@@ -4409,8 +4460,12 @@ function processRouteLoaderData(matches, matchesToLoad, results, pendingActionRe
4409
4460
  let loaderHeaders = {};
4410
4461
  let pendingError = pendingActionResult && isErrorResult(pendingActionResult[1]) ? pendingActionResult[1].error : undefined;
4411
4462
  // Process loader results into state.loaderData/state.errors
4412
- results.forEach((result, index) => {
4413
- let id = matchesToLoad[index].route.id;
4463
+ matches.forEach(match => {
4464
+ if (!(match.route.id in results)) {
4465
+ return;
4466
+ }
4467
+ let id = match.route.id;
4468
+ let result = results[id];
4414
4469
  invariant(!isRedirectResult(result), "Cannot handle redirect results in processLoaderData");
4415
4470
  if (isErrorResult(result)) {
4416
4471
  let error = result.error;
@@ -4489,21 +4544,21 @@ function processLoaderData(state, matches, matchesToLoad, results, pendingAction
4489
4544
  let {
4490
4545
  loaderData,
4491
4546
  errors
4492
- } = processRouteLoaderData(matches, matchesToLoad, results, pendingActionResult, activeDeferreds, false // This method is only called client side so we always want to bubble
4547
+ } = processRouteLoaderData(matches, results, pendingActionResult, activeDeferreds, false // This method is only called client side so we always want to bubble
4493
4548
  );
4494
4549
  // Process results from our revalidating fetchers
4495
- for (let index = 0; index < revalidatingFetchers.length; index++) {
4550
+ revalidatingFetchers.forEach(rf => {
4496
4551
  let {
4497
4552
  key,
4498
4553
  match,
4499
4554
  controller
4500
- } = revalidatingFetchers[index];
4501
- invariant(fetcherResults !== undefined && fetcherResults[index] !== undefined, "Did not find corresponding fetcher result");
4502
- let result = fetcherResults[index];
4555
+ } = rf;
4556
+ let result = fetcherResults[key];
4557
+ invariant(result, "Did not find corresponding fetcher result");
4503
4558
  // Process fetcher non-redirect errors
4504
4559
  if (controller && controller.signal.aborted) {
4505
4560
  // Nothing to do for aborted fetchers
4506
- continue;
4561
+ return;
4507
4562
  } else if (isErrorResult(result)) {
4508
4563
  let boundaryMatch = findNearestBoundary(state.matches, match == null ? void 0 : match.route.id);
4509
4564
  if (!(errors && errors[boundaryMatch.route.id])) {
@@ -4524,7 +4579,7 @@ function processLoaderData(state, matches, matchesToLoad, results, pendingAction
4524
4579
  let doneFetcher = getDoneFetcher(result.data);
4525
4580
  state.fetchers.set(key, doneFetcher);
4526
4581
  }
4527
- }
4582
+ });
4528
4583
  return {
4529
4584
  loaderData,
4530
4585
  errors
@@ -4624,12 +4679,13 @@ function getInternalRouterError(status, _temp5) {
4624
4679
  }
4625
4680
  // Find any returned redirect errors, starting from the lowest match
4626
4681
  function findRedirect(results) {
4627
- for (let i = results.length - 1; i >= 0; i--) {
4628
- let result = results[i];
4682
+ let entries = Object.entries(results);
4683
+ for (let i = entries.length - 1; i >= 0; i--) {
4684
+ let [key, result] = entries[i];
4629
4685
  if (isRedirectResult(result)) {
4630
4686
  return {
4631
- result,
4632
- idx: i
4687
+ key,
4688
+ result
4633
4689
  };
4634
4690
  }
4635
4691
  }
@@ -4661,10 +4717,10 @@ function isHashChangeOnly(a, b) {
4661
4717
  function isPromise(val) {
4662
4718
  return typeof val === "object" && val != null && "then" in val;
4663
4719
  }
4664
- function isHandlerResult(result) {
4720
+ function isDataStrategyResult(result) {
4665
4721
  return result != null && typeof result === "object" && "type" in result && "result" in result && (result.type === ResultType.data || result.type === ResultType.error);
4666
4722
  }
4667
- function isRedirectHandlerResult(result) {
4723
+ function isRedirectDataStrategyResultResult(result) {
4668
4724
  return isResponse(result.result) && redirectStatusCodes.has(result.result.status);
4669
4725
  }
4670
4726
  function isDeferredResult(result) {
@@ -4700,10 +4756,11 @@ function isValidMethod(method) {
4700
4756
  function isMutationMethod(method) {
4701
4757
  return validMutationMethods.has(method.toLowerCase());
4702
4758
  }
4703
- async function resolveDeferredResults(currentMatches, matchesToLoad, results, signals, isFetcher, currentLoaderData) {
4704
- for (let index = 0; index < results.length; index++) {
4705
- let result = results[index];
4706
- let match = matchesToLoad[index];
4759
+ async function resolveNavigationDeferredResults(matches, results, signal, currentMatches, currentLoaderData) {
4760
+ let entries = Object.entries(results);
4761
+ for (let index = 0; index < entries.length; index++) {
4762
+ let [routeId, result] = entries[index];
4763
+ let match = matches.find(m => (m == null ? void 0 : m.route.id) === routeId);
4707
4764
  // If we don't have a match, then we can have a deferred result to do
4708
4765
  // anything with. This is for revalidating fetchers where the route was
4709
4766
  // removed during HMR
@@ -4712,15 +4769,41 @@ async function resolveDeferredResults(currentMatches, matchesToLoad, results, si
4712
4769
  }
4713
4770
  let currentMatch = currentMatches.find(m => m.route.id === match.route.id);
4714
4771
  let isRevalidatingLoader = currentMatch != null && !isNewRouteInstance(currentMatch, match) && (currentLoaderData && currentLoaderData[match.route.id]) !== undefined;
4715
- if (isDeferredResult(result) && (isFetcher || isRevalidatingLoader)) {
4772
+ if (isDeferredResult(result) && isRevalidatingLoader) {
4773
+ // Note: we do not have to touch activeDeferreds here since we race them
4774
+ // against the signal in resolveDeferredData and they'll get aborted
4775
+ // there if needed
4776
+ await resolveDeferredData(result, signal, false).then(result => {
4777
+ if (result) {
4778
+ results[routeId] = result;
4779
+ }
4780
+ });
4781
+ }
4782
+ }
4783
+ }
4784
+ async function resolveFetcherDeferredResults(matches, results, revalidatingFetchers) {
4785
+ for (let index = 0; index < revalidatingFetchers.length; index++) {
4786
+ let {
4787
+ key,
4788
+ routeId,
4789
+ controller
4790
+ } = revalidatingFetchers[index];
4791
+ let result = results[key];
4792
+ let match = matches.find(m => (m == null ? void 0 : m.route.id) === routeId);
4793
+ // If we don't have a match, then we can have a deferred result to do
4794
+ // anything with. This is for revalidating fetchers where the route was
4795
+ // removed during HMR
4796
+ if (!match) {
4797
+ continue;
4798
+ }
4799
+ if (isDeferredResult(result)) {
4716
4800
  // Note: we do not have to touch activeDeferreds here since we race them
4717
4801
  // against the signal in resolveDeferredData and they'll get aborted
4718
4802
  // there if needed
4719
- let signal = signals[index];
4720
- invariant(signal, "Expected an AbortSignal for revalidating fetcher deferred result");
4721
- await resolveDeferredData(result, signal, isFetcher).then(result => {
4803
+ invariant(controller, "Expected an AbortController for revalidating fetcher deferred result");
4804
+ await resolveDeferredData(result, controller.signal, true).then(result => {
4722
4805
  if (result) {
4723
- results[index] = result || results[index];
4806
+ results[key] = result;
4724
4807
  }
4725
4808
  });
4726
4809
  }