@remix-run/router 1.19.0 → 1.19.1-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/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # `@remix-run/router`
2
2
 
3
+ ## 1.19.1-pre.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Fog of War: Update `unstable_patchRoutesOnMiss` logic so that we call the method when we match routes with dynamic param or splat segments in case there exists a higher-scoring static route that we've not yet discovered. ([#11883](https://github.com/remix-run/react-router/pull/11883))
8
+
9
+ - We also now leverage an internal FIFO queue of previous paths we've already called `unstable_patchRouteOnMiss` against so that we don't re-call on subsequent navigations to the same path
10
+
11
+ - Rename `unstable_patchRoutesOnMiss` to `unstable_patchRoutesOnNavigation` because it will now be called on the first navigation to paths matching splat/param routes in case there exists a higher-scoring route match not yet discovered ([#11888](https://github.com/remix-run/react-router/pull/11888))
12
+
3
13
  ## 1.19.0
4
14
 
5
15
  ### Minor Changes
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type { ActionFunction, ActionFunctionArgs, AgnosticDataIndexRouteObject, AgnosticDataNonIndexRouteObject, AgnosticDataRouteMatch, AgnosticDataRouteObject, AgnosticIndexRouteObject, AgnosticNonIndexRouteObject, AgnosticRouteMatch, AgnosticRouteObject, DataStrategyFunction as unstable_DataStrategyFunction, DataStrategyFunctionArgs as unstable_DataStrategyFunctionArgs, DataStrategyMatch as unstable_DataStrategyMatch, ErrorResponse, FormEncType, FormMethod, HandlerResult as unstable_HandlerResult, HTMLFormMethod, JsonFunction, LazyRouteFunction, LoaderFunction, LoaderFunctionArgs, ParamParseKey, Params, AgnosticPatchRoutesOnMissFunction as unstable_AgnosticPatchRoutesOnMissFunction, PathMatch, PathParam, PathPattern, RedirectFunction, ShouldRevalidateFunction, ShouldRevalidateFunctionArgs, TrackedPromise, UIMatch, V7_FormMethod, DataWithResponseInit as UNSAFE_DataWithResponseInit, } from "./utils";
1
+ export type { ActionFunction, ActionFunctionArgs, AgnosticDataIndexRouteObject, AgnosticDataNonIndexRouteObject, AgnosticDataRouteMatch, AgnosticDataRouteObject, AgnosticIndexRouteObject, AgnosticNonIndexRouteObject, AgnosticRouteMatch, AgnosticRouteObject, DataStrategyFunction as unstable_DataStrategyFunction, DataStrategyFunctionArgs as unstable_DataStrategyFunctionArgs, DataStrategyMatch as unstable_DataStrategyMatch, ErrorResponse, FormEncType, FormMethod, HandlerResult as unstable_HandlerResult, HTMLFormMethod, JsonFunction, LazyRouteFunction, LoaderFunction, LoaderFunctionArgs, ParamParseKey, Params, AgnosticPatchRoutesOnNavigationFunction as unstable_AgnosticPatchRoutesOnNavigationFunction, PathMatch, PathParam, PathPattern, RedirectFunction, ShouldRevalidateFunction, ShouldRevalidateFunctionArgs, TrackedPromise, UIMatch, V7_FormMethod, DataWithResponseInit as UNSAFE_DataWithResponseInit, } from "./utils";
2
2
  export { AbortedDeferredError, data as unstable_data, defer, generatePath, getToPathname, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, redirect, redirectDocument, replace, resolvePath, resolveTo, stripBasename, } from "./utils";
3
3
  export type { BrowserHistory, BrowserHistoryOptions, HashHistory, HashHistoryOptions, History, InitialEntry, Location, MemoryHistory, MemoryHistoryOptions, Path, To, } from "./history";
4
4
  export { Action, createBrowserHistory, createHashHistory, createMemoryHistory, createPath, parsePath, } from "./history";
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @remix-run/router v1.19.0
2
+ * @remix-run/router v1.19.1-pre.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1713,7 +1713,7 @@ function createRouter(init) {
1713
1713
  let inFlightDataRoutes;
1714
1714
  let basename = init.basename || "/";
1715
1715
  let dataStrategyImpl = init.unstable_dataStrategy || defaultDataStrategy;
1716
- let patchRoutesOnMissImpl = init.unstable_patchRoutesOnMiss;
1716
+ let patchRoutesOnNavigationImpl = init.unstable_patchRoutesOnNavigation;
1717
1717
 
1718
1718
  // Config driven behavior flags
1719
1719
  let future = _extends({
@@ -1728,6 +1728,10 @@ function createRouter(init) {
1728
1728
  let unlistenHistory = null;
1729
1729
  // Externally-provided functions to call on all state changes
1730
1730
  let subscribers = new Set();
1731
+ // FIFO queue of previously discovered routes to prevent re-calling on
1732
+ // subsequent navigations to the same path
1733
+ let discoveredRoutesMaxSize = 1000;
1734
+ let discoveredRoutes = new Set();
1731
1735
  // Externally-provided object to hold scroll restoration locations during routing
1732
1736
  let savedScrollPositions = null;
1733
1737
  // Externally-provided function to get scroll restoration keys
@@ -1743,7 +1747,7 @@ function createRouter(init) {
1743
1747
  let initialScrollRestored = init.hydrationData != null;
1744
1748
  let initialMatches = matchRoutes(dataRoutes, init.history.location, basename);
1745
1749
  let initialErrors = null;
1746
- if (initialMatches == null && !patchRoutesOnMissImpl) {
1750
+ if (initialMatches == null && !patchRoutesOnNavigationImpl) {
1747
1751
  // If we do not match a user-provided-route, fall back to the root
1748
1752
  // to allow the error boundary to take over
1749
1753
  let error = getInternalRouterError(404, {
@@ -1759,7 +1763,7 @@ function createRouter(init) {
1759
1763
  };
1760
1764
  }
1761
1765
 
1762
- // In SPA apps, if the user provided a patchRoutesOnMiss implementation and
1766
+ // In SPA apps, if the user provided a patchRoutesOnNavigation implementation and
1763
1767
  // our initial match is a splat route, clear them out so we run through lazy
1764
1768
  // discovery on hydration in case there's a more accurate lazy route match.
1765
1769
  // In SSR apps (with `hydrationData`), we expect that the server will send
@@ -1777,7 +1781,7 @@ function createRouter(init) {
1777
1781
  initialMatches = [];
1778
1782
 
1779
1783
  // If partial hydration and fog of war is enabled, we will be running
1780
- // `patchRoutesOnMiss` during hydration so include any partial matches as
1784
+ // `patchRoutesOnNavigation` during hydration so include any partial matches as
1781
1785
  // the initial matches so we can properly render `HydrateFallback`'s
1782
1786
  if (future.v7_partialHydration) {
1783
1787
  let fogOfWar = checkFogOfWar(null, dataRoutes, init.history.location.pathname);
@@ -1916,7 +1920,7 @@ function createRouter(init) {
1916
1920
  // we don't need to update UI state if they change
1917
1921
  let blockerFunctions = new Map();
1918
1922
 
1919
- // Map of pending patchRoutesOnMiss() promises (keyed by path/matches) so
1923
+ // Map of pending patchRoutesOnNavigation() promises (keyed by path/matches) so
1920
1924
  // that we only kick them off once for a given combo
1921
1925
  let pendingPatchRoutes = new Map();
1922
1926
 
@@ -3540,7 +3544,16 @@ function createRouter(init) {
3540
3544
  return null;
3541
3545
  }
3542
3546
  function checkFogOfWar(matches, routesToUse, pathname) {
3543
- if (patchRoutesOnMissImpl) {
3547
+ if (patchRoutesOnNavigationImpl) {
3548
+ // Don't bother re-calling patchRouteOnMiss for a path we've already
3549
+ // processed. the last execution would have patched the route tree
3550
+ // accordingly so `matches` here are already accurate.
3551
+ if (discoveredRoutes.has(pathname)) {
3552
+ return {
3553
+ active: false,
3554
+ matches
3555
+ };
3556
+ }
3544
3557
  if (!matches) {
3545
3558
  let fogMatches = matchRoutesImpl(routesToUse, pathname, basename, true);
3546
3559
  return {
@@ -3548,11 +3561,10 @@ function createRouter(init) {
3548
3561
  matches: fogMatches || []
3549
3562
  };
3550
3563
  } else {
3551
- let leafRoute = matches[matches.length - 1].route;
3552
- if (leafRoute.path && (leafRoute.path === "*" || leafRoute.path.endsWith("/*"))) {
3553
- // If we matched a splat, it might only be because we haven't yet fetched
3554
- // the children that would match with a higher score, so let's fetch
3555
- // around and find out
3564
+ if (Object.keys(matches[0].params).length > 0) {
3565
+ // If we matched a dynamic param or a splat, it might only be because
3566
+ // we haven't yet discovered other routes that would match with a
3567
+ // higher score. Call patchRoutesOnNavigation just to be sure
3556
3568
  let partialMatches = matchRoutesImpl(routesToUse, pathname, basename, true);
3557
3569
  return {
3558
3570
  active: true,
@@ -3568,12 +3580,11 @@ function createRouter(init) {
3568
3580
  }
3569
3581
  async function discoverRoutes(matches, pathname, signal) {
3570
3582
  let partialMatches = matches;
3571
- let route = partialMatches.length > 0 ? partialMatches[partialMatches.length - 1].route : null;
3572
3583
  while (true) {
3573
3584
  let isNonHMR = inFlightDataRoutes == null;
3574
3585
  let routesToUse = inFlightDataRoutes || dataRoutes;
3575
3586
  try {
3576
- await loadLazyRouteChildren(patchRoutesOnMissImpl, pathname, partialMatches, routesToUse, manifest, mapRouteProperties, pendingPatchRoutes, signal);
3587
+ await loadLazyRouteChildren(patchRoutesOnNavigationImpl, pathname, partialMatches, routesToUse, manifest, mapRouteProperties, pendingPatchRoutes, signal);
3577
3588
  } catch (e) {
3578
3589
  return {
3579
3590
  type: "error",
@@ -3597,52 +3608,32 @@ function createRouter(init) {
3597
3608
  };
3598
3609
  }
3599
3610
  let newMatches = matchRoutes(routesToUse, pathname, basename);
3600
- let matchedSplat = false;
3601
3611
  if (newMatches) {
3602
- let leafRoute = newMatches[newMatches.length - 1].route;
3603
- if (leafRoute.index) {
3604
- // If we found an index route, we can stop
3605
- return {
3606
- type: "success",
3607
- matches: newMatches
3608
- };
3609
- }
3610
- if (leafRoute.path && leafRoute.path.length > 0) {
3611
- if (leafRoute.path === "*") {
3612
- // If we found a splat route, we can't be sure there's not a
3613
- // higher-scoring route down some partial matches trail so we need
3614
- // to check that out
3615
- matchedSplat = true;
3616
- } else {
3617
- // If we found a non-splat route, we can stop
3618
- return {
3619
- type: "success",
3620
- matches: newMatches
3621
- };
3622
- }
3623
- }
3624
- }
3625
- let newPartialMatches = matchRoutesImpl(routesToUse, pathname, basename, true);
3626
-
3627
- // If we are no longer partially matching anything, this was either a
3628
- // legit splat match above, or it's a 404. Also avoid loops if the
3629
- // second pass results in the same partial matches
3630
- if (!newPartialMatches || partialMatches.map(m => m.route.id).join("-") === newPartialMatches.map(m => m.route.id).join("-")) {
3612
+ addToFifoQueue(pathname, discoveredRoutes);
3631
3613
  return {
3632
3614
  type: "success",
3633
- matches: matchedSplat ? newMatches : null
3615
+ matches: newMatches
3634
3616
  };
3635
3617
  }
3636
- partialMatches = newPartialMatches;
3637
- route = partialMatches[partialMatches.length - 1].route;
3638
- if (route.path === "*") {
3639
- // The splat is still our most accurate partial, so run with it
3618
+ let newPartialMatches = matchRoutesImpl(routesToUse, pathname, basename, true);
3619
+
3620
+ // Avoid loops if the second pass results in the same partial matches
3621
+ if (!newPartialMatches || partialMatches.length === newPartialMatches.length && partialMatches.every((m, i) => m.route.id === newPartialMatches[i].route.id)) {
3622
+ addToFifoQueue(pathname, discoveredRoutes);
3640
3623
  return {
3641
3624
  type: "success",
3642
- matches: partialMatches
3625
+ matches: null
3643
3626
  };
3644
3627
  }
3628
+ partialMatches = newPartialMatches;
3629
+ }
3630
+ }
3631
+ function addToFifoQueue(path, queue) {
3632
+ if (queue.size >= discoveredRoutesMaxSize) {
3633
+ let first = queue.values().next().value;
3634
+ queue.delete(first);
3645
3635
  }
3636
+ queue.add(path);
3646
3637
  }
3647
3638
  function _internalSetRoutes(newRoutes) {
3648
3639
  manifest = {};
@@ -4513,15 +4504,15 @@ function shouldRevalidateLoader(loaderMatch, arg) {
4513
4504
  }
4514
4505
 
4515
4506
  /**
4516
- * Idempotent utility to execute patchRoutesOnMiss() to lazily load route
4507
+ * Idempotent utility to execute patchRoutesOnNavigation() to lazily load route
4517
4508
  * definitions and update the routes/routeManifest
4518
4509
  */
4519
- async function loadLazyRouteChildren(patchRoutesOnMissImpl, path, matches, routes, manifest, mapRouteProperties, pendingRouteChildren, signal) {
4510
+ async function loadLazyRouteChildren(patchRoutesOnNavigationImpl, path, matches, routes, manifest, mapRouteProperties, pendingRouteChildren, signal) {
4520
4511
  let key = [path, ...matches.map(m => m.route.id)].join("-");
4521
4512
  try {
4522
4513
  let pending = pendingRouteChildren.get(key);
4523
4514
  if (!pending) {
4524
- pending = patchRoutesOnMissImpl({
4515
+ pending = patchRoutesOnNavigationImpl({
4525
4516
  path,
4526
4517
  matches,
4527
4518
  patch: (routeId, children) => {
@@ -5128,7 +5119,7 @@ function getInternalRouterError(status, _temp5) {
5128
5119
  if (status === 400) {
5129
5120
  statusText = "Bad Request";
5130
5121
  if (type === "route-discovery") {
5131
- errorMessage = "Unable to match URL \"" + pathname + "\" - the `unstable_patchRoutesOnMiss()` " + ("function threw the following error:\n" + message);
5122
+ errorMessage = "Unable to match URL \"" + pathname + "\" - the `unstable_patchRoutesOnNavigation()` " + ("function threw the following error:\n" + message);
5132
5123
  } else if (method && pathname && routeId) {
5133
5124
  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.";
5134
5125
  } else if (type === "defer-action") {