@remix-run/router 0.0.0-experimental-9ffbba722 → 0.0.0-experimental-b3c9fc11b

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,39 @@
1
1
  # `@remix-run/router`
2
2
 
3
+ ## 1.19.0-pre.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [REMOVE] Export DataWithResponseInit for Remix usage ([#11859](https://github.com/remix-run/react-router/pull/11859))
8
+
9
+ ## 1.19.0-pre.0
10
+
11
+ ### Minor Changes
12
+
13
+ - Add a new `unstable_data()` API for usage with Remix Single Fetch ([#11836](https://github.com/remix-run/react-router/pull/11836))
14
+
15
+ - This API is not intended for direct usage in React Router SPA applications
16
+ - It is primarily intended for usage with `createStaticHandler.query()` to allow loaders/actions to return arbitrary data + `status`/`headers` without forcing the serialization of data into a `Response` instance
17
+ - This allows for more advanced serialization tactics via `unstable_dataStrategy` such as serializing via `turbo-stream` in Remix Single Fetch
18
+ - ⚠️ This removes the `status` field from `HandlerResult`
19
+ - If you need to return a specific `status` from `unstable_dataStrategy` you should instead do so via `unstable_data()`
20
+
21
+ - Add a new `replace(url, init?)` alternative to `redirect(url, init?)` that performs a `history.replaceState` instead of a `history.pushState` on client-side navigation redirects ([#11811](https://github.com/remix-run/react-router/pull/11811))
22
+
23
+ ### Patch Changes
24
+
25
+ - Fix internal cleanup of interrupted fetchers to avoid invalid revalidations on navigations ([#11839](https://github.com/remix-run/react-router/pull/11839))
26
+
27
+ - When a `fetcher.load` is interrupted by an `action` submission, we track it internally and force revalidation once the `action` completes
28
+ - We previously only cleared out this internal tracking info on a successful _navigation_ submission
29
+ - Therefore, if the `fetcher.load` was interrupted by a `fetcher.submit`, then we wouldn't remove it from this internal tracking info on successful load (incorrectly)
30
+ - And then on the next navigation it's presence in the internal tracking would automatically trigger execution of the `fetcher.load` again, ignoring any `shouldRevalidate` logic
31
+ - This fix cleans up the internal tracking so it applies to both navigation submission and fetcher submissions
32
+
33
+ - Fix initial hydration behavior when using `future.v7_partialHydration` along with `unstable_patchRoutesOnMiss` ([#11838](https://github.com/remix-run/react-router/pull/11838))
34
+
35
+ - During initial hydration, `router.state.matches` will now include any partial matches so that we can render ancestor `HydrateFallback` components
36
+
3
37
  ## 1.18.0
4
38
 
5
39
  ### 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, } 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, AgnosticPatchRoutesOnMissFunction as unstable_AgnosticPatchRoutesOnMissFunction, 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 v0.0.0-experimental-9ffbba722
2
+ * @remix-run/router v0.0.0-experimental-b3c9fc11b
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1765,7 +1765,7 @@ function createRouter(init) {
1765
1765
  // In SSR apps (with `hydrationData`), we expect that the server will send
1766
1766
  // up the proper matched routes so we don't want to run lazy discovery on
1767
1767
  // initial hydration and want to hydrate into the splat route.
1768
- if (initialMatches && patchRoutesOnMissImpl && !init.hydrationData) {
1768
+ if (initialMatches && !init.hydrationData) {
1769
1769
  let fogOfWar = checkFogOfWar(initialMatches, dataRoutes, init.history.location.pathname);
1770
1770
  if (fogOfWar.active) {
1771
1771
  initialMatches = null;
@@ -1773,9 +1773,18 @@ function createRouter(init) {
1773
1773
  }
1774
1774
  let initialized;
1775
1775
  if (!initialMatches) {
1776
- // We need to run patchRoutesOnMiss in initialize()
1777
1776
  initialized = false;
1778
1777
  initialMatches = [];
1778
+
1779
+ // If partial hydration and fog of war is enabled, we will be running
1780
+ // `patchRoutesOnMiss` during hydration so include any partial matches as
1781
+ // the initial matches so we can properly render `HydrateFallback`'s
1782
+ if (future.v7_partialHydration) {
1783
+ let fogOfWar = checkFogOfWar(null, dataRoutes, init.history.location.pathname);
1784
+ if (fogOfWar.active && fogOfWar.matches) {
1785
+ initialMatches = fogOfWar.matches;
1786
+ }
1787
+ }
1779
1788
  } else if (initialMatches.some(m => m.route.lazy)) {
1780
1789
  // All initialMatches need to be loaded before we're ready. If we have lazy
1781
1790
  // functions around still then we'll need to run them in initialize()
@@ -1868,7 +1877,7 @@ function createRouter(init) {
1868
1877
 
1869
1878
  // Use this internal array to capture fetcher loads that were cancelled by an
1870
1879
  // action navigation and require revalidation
1871
- let cancelledFetcherLoads = [];
1880
+ let cancelledFetcherLoads = new Set();
1872
1881
 
1873
1882
  // AbortControllers for any in-flight fetchers
1874
1883
  let fetchControllers = new Map();
@@ -2168,7 +2177,6 @@ function createRouter(init) {
2168
2177
  isUninterruptedRevalidation = false;
2169
2178
  isRevalidationRequired = false;
2170
2179
  cancelledDeferredRoutes = [];
2171
- cancelledFetcherLoads = [];
2172
2180
  }
2173
2181
 
2174
2182
  // Trigger a navigation event, which can either be a numerical POP or a PUSH
@@ -3264,7 +3272,7 @@ function createRouter(init) {
3264
3272
  // Abort in-flight fetcher loads
3265
3273
  fetchLoadMatches.forEach((_, key) => {
3266
3274
  if (fetchControllers.has(key)) {
3267
- cancelledFetcherLoads.push(key);
3275
+ cancelledFetcherLoads.add(key);
3268
3276
  abortFetcher(key);
3269
3277
  }
3270
3278
  });
@@ -3318,6 +3326,7 @@ function createRouter(init) {
3318
3326
  fetchReloadIds.delete(key);
3319
3327
  fetchRedirectIds.delete(key);
3320
3328
  deletedFetchers.delete(key);
3329
+ cancelledFetcherLoads.delete(key);
3321
3330
  state.fetchers.delete(key);
3322
3331
  }
3323
3332
  function deleteFetcherAndUpdateState(key) {
@@ -3539,11 +3548,10 @@ function createRouter(init) {
3539
3548
  matches: fogMatches || []
3540
3549
  };
3541
3550
  } else {
3542
- let leafRoute = matches[matches.length - 1].route;
3543
- if (leafRoute.path && (leafRoute.path === "*" || leafRoute.path.endsWith("/*"))) {
3544
- // If we matched a splat, it might only be because we haven't yet fetched
3545
- // the children that would match with a higher score, so let's fetch
3546
- // around and find out
3551
+ if (Object.keys(matches[0].params).length > 0) {
3552
+ // If we matched a dynamic param or a splat, it might only be because
3553
+ // we haven't yet discovered other routes that would match with a
3554
+ // higher score. Call patchRoutesOnMiss just to be sure
3547
3555
  let partialMatches = matchRoutesImpl(routesToUse, pathname, basename, true);
3548
3556
  return {
3549
3557
  active: true,
@@ -3559,7 +3567,6 @@ function createRouter(init) {
3559
3567
  }
3560
3568
  async function discoverRoutes(matches, pathname, signal) {
3561
3569
  let partialMatches = matches;
3562
- let route = partialMatches.length > 0 ? partialMatches[partialMatches.length - 1].route : null;
3563
3570
  while (true) {
3564
3571
  let isNonHMR = inFlightDataRoutes == null;
3565
3572
  let routesToUse = inFlightDataRoutes || dataRoutes;
@@ -3588,51 +3595,22 @@ function createRouter(init) {
3588
3595
  };
3589
3596
  }
3590
3597
  let newMatches = matchRoutes(routesToUse, pathname, basename);
3591
- let matchedSplat = false;
3592
3598
  if (newMatches) {
3593
- let leafRoute = newMatches[newMatches.length - 1].route;
3594
- if (leafRoute.index) {
3595
- // If we found an index route, we can stop
3596
- return {
3597
- type: "success",
3598
- matches: newMatches
3599
- };
3600
- }
3601
- if (leafRoute.path && leafRoute.path.length > 0) {
3602
- if (leafRoute.path === "*") {
3603
- // If we found a splat route, we can't be sure there's not a
3604
- // higher-scoring route down some partial matches trail so we need
3605
- // to check that out
3606
- matchedSplat = true;
3607
- } else {
3608
- // If we found a non-splat route, we can stop
3609
- return {
3610
- type: "success",
3611
- matches: newMatches
3612
- };
3613
- }
3614
- }
3615
- }
3616
- let newPartialMatches = matchRoutesImpl(routesToUse, pathname, basename, true);
3617
-
3618
- // If we are no longer partially matching anything, this was either a
3619
- // legit splat match above, or it's a 404. Also avoid loops if the
3620
- // second pass results in the same partial matches
3621
- if (!newPartialMatches || partialMatches.map(m => m.route.id).join("-") === newPartialMatches.map(m => m.route.id).join("-")) {
3622
3599
  return {
3623
3600
  type: "success",
3624
- matches: matchedSplat ? newMatches : null
3601
+ matches: newMatches
3625
3602
  };
3626
3603
  }
3627
- partialMatches = newPartialMatches;
3628
- route = partialMatches[partialMatches.length - 1].route;
3629
- if (route.path === "*") {
3630
- // The splat is still our most accurate partial, so run with it
3604
+ let newPartialMatches = matchRoutesImpl(routesToUse, pathname, basename, true);
3605
+
3606
+ // Avoid loops if the second pass results in the same partial matches
3607
+ if (!newPartialMatches || partialMatches.length === newPartialMatches.length && partialMatches.every((m, i) => m.route.id === newPartialMatches[i].route.id)) {
3631
3608
  return {
3632
3609
  type: "success",
3633
- matches: partialMatches
3610
+ matches: null
3634
3611
  };
3635
3612
  }
3613
+ partialMatches = newPartialMatches;
3636
3614
  }
3637
3615
  }
3638
3616
  function _internalSetRoutes(newRoutes) {
@@ -4433,8 +4411,9 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
4433
4411
  if (fetchRedirectIds.has(key)) {
4434
4412
  // Never trigger a revalidation of an actively redirecting fetcher
4435
4413
  shouldRevalidate = false;
4436
- } else if (cancelledFetcherLoads.includes(key)) {
4437
- // Always revalidate if the fetcher was cancelled
4414
+ } else if (cancelledFetcherLoads.has(key)) {
4415
+ // Always mark for revalidation if the fetcher was cancelled
4416
+ cancelledFetcherLoads.delete(key);
4438
4417
  shouldRevalidate = true;
4439
4418
  } else if (fetcher && fetcher.state !== "idle" && fetcher.data === undefined) {
4440
4419
  // If the fetcher hasn't ever completed loading yet, then this isn't a