@remix-run/router 1.18.0 → 1.19.0-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,33 @@
1
1
  # `@remix-run/router`
2
2
 
3
+ ## 1.19.0-pre.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Add a new `unstable_data()` API for usage with Remix Single Fetch ([#11836](https://github.com/remix-run/react-router/pull/11836))
8
+
9
+ - This API is not intended for direct usage in React Router SPA applications
10
+ - 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
11
+ - This allows for more advanced serialization tactics via `unstable_dataStrategy` such as serializing via `turbo-stream` in Remix Single Fetch
12
+ - ⚠️ This removes the `status` field from `HandlerResult`
13
+ - If you need to return a specific `status` from `unstable_dataStrategy` you should instead do so via `unstable_data()`
14
+
15
+ - 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))
16
+
17
+ ### Patch Changes
18
+
19
+ - Fix internal cleanup of interrupted fetchers to avoid invalid revalidations on navigations ([#11839](https://github.com/remix-run/react-router/pull/11839))
20
+
21
+ - When a `fetcher.load` is interrupted by an `action` submission, we track it internally and force revalidation once the `action` completes
22
+ - We previously only cleared out this internal tracking info on a successful _navigation_ submission
23
+ - 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)
24
+ - 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
25
+ - This fix cleans up the internal tracking so it applies to both navigation submission and fetcher submissions
26
+
27
+ - Fix initial hydration behavior when using `future.v7_partialHydration` along with `unstable_patchRoutesOnMiss` ([#11838](https://github.com/remix-run/react-router/pull/11838))
28
+
29
+ - During initial hydration, `router.state.matches` will now include any partial matches so that we can render ancestor `HydrateFallback` components
30
+
3
31
  ## 1.18.0
4
32
 
5
33
  ### Minor Changes
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
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";
2
- export { AbortedDeferredError, defer, generatePath, getToPathname, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, redirect, redirectDocument, resolvePath, resolveTo, stripBasename, } from "./utils";
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";
5
5
  export * from "./router";
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @remix-run/router v1.18.0
2
+ * @remix-run/router v1.19.0-pre.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1327,6 +1327,23 @@ const json = function json(data, init) {
1327
1327
  headers
1328
1328
  }));
1329
1329
  };
1330
+ class DataWithResponseInit {
1331
+ constructor(data, init) {
1332
+ this.type = "DataWithResponseInit";
1333
+ this.data = data;
1334
+ this.init = init || null;
1335
+ }
1336
+ }
1337
+
1338
+ /**
1339
+ * Create "responses" that contain `status`/`headers` without forcing
1340
+ * serialization into an actual `Response` - used by Remix single fetch
1341
+ */
1342
+ function data(data, init) {
1343
+ return new DataWithResponseInit(data, typeof init === "number" ? {
1344
+ status: init
1345
+ } : init);
1346
+ }
1330
1347
  class AbortedDeferredError extends Error {}
1331
1348
  class DeferredData {
1332
1349
  constructor(data, responseInit) {
@@ -1509,6 +1526,18 @@ const redirectDocument = (url, init) => {
1509
1526
  response.headers.set("X-Remix-Reload-Document", "true");
1510
1527
  return response;
1511
1528
  };
1529
+
1530
+ /**
1531
+ * A redirect response that will perform a `history.replaceState` instead of a
1532
+ * `history.pushState` for client-side navigation redirects.
1533
+ * Sets the status code and the `Location` header.
1534
+ * Defaults to "302 Found".
1535
+ */
1536
+ const replace = (url, init) => {
1537
+ let response = redirect(url, init);
1538
+ response.headers.set("X-Remix-Replace", "true");
1539
+ return response;
1540
+ };
1512
1541
  /**
1513
1542
  * @private
1514
1543
  * Utility class we use to hold auto-unwrapped 4xx/5xx Response bodies
@@ -1736,7 +1765,7 @@ function createRouter(init) {
1736
1765
  // In SSR apps (with `hydrationData`), we expect that the server will send
1737
1766
  // up the proper matched routes so we don't want to run lazy discovery on
1738
1767
  // initial hydration and want to hydrate into the splat route.
1739
- if (initialMatches && patchRoutesOnMissImpl && !init.hydrationData) {
1768
+ if (initialMatches && !init.hydrationData) {
1740
1769
  let fogOfWar = checkFogOfWar(initialMatches, dataRoutes, init.history.location.pathname);
1741
1770
  if (fogOfWar.active) {
1742
1771
  initialMatches = null;
@@ -1744,9 +1773,18 @@ function createRouter(init) {
1744
1773
  }
1745
1774
  let initialized;
1746
1775
  if (!initialMatches) {
1747
- // We need to run patchRoutesOnMiss in initialize()
1748
1776
  initialized = false;
1749
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
+ }
1750
1788
  } else if (initialMatches.some(m => m.route.lazy)) {
1751
1789
  // All initialMatches need to be loaded before we're ready. If we have lazy
1752
1790
  // functions around still then we'll need to run them in initialize()
@@ -1839,7 +1877,7 @@ function createRouter(init) {
1839
1877
 
1840
1878
  // Use this internal array to capture fetcher loads that were cancelled by an
1841
1879
  // action navigation and require revalidation
1842
- let cancelledFetcherLoads = [];
1880
+ let cancelledFetcherLoads = new Set();
1843
1881
 
1844
1882
  // AbortControllers for any in-flight fetchers
1845
1883
  let fetchControllers = new Map();
@@ -2139,7 +2177,6 @@ function createRouter(init) {
2139
2177
  isUninterruptedRevalidation = false;
2140
2178
  isRevalidationRequired = false;
2141
2179
  cancelledDeferredRoutes = [];
2142
- cancelledFetcherLoads = [];
2143
2180
  }
2144
2181
 
2145
2182
  // Trigger a navigation event, which can either be a numerical POP or a PUSH
@@ -3141,7 +3178,7 @@ function createRouter(init) {
3141
3178
  // There's no need to abort on redirects, since we don't detect the
3142
3179
  // redirect until the action/loaders have settled
3143
3180
  pendingNavigationController = null;
3144
- let redirectHistoryAction = replace === true ? Action.Replace : Action.Push;
3181
+ let redirectHistoryAction = replace === true || redirect.response.headers.has("X-Remix-Replace") ? Action.Replace : Action.Push;
3145
3182
 
3146
3183
  // Use the incoming submission if provided, fallback on the active one in
3147
3184
  // state.navigation
@@ -3235,7 +3272,7 @@ function createRouter(init) {
3235
3272
  // Abort in-flight fetcher loads
3236
3273
  fetchLoadMatches.forEach((_, key) => {
3237
3274
  if (fetchControllers.has(key)) {
3238
- cancelledFetcherLoads.push(key);
3275
+ cancelledFetcherLoads.add(key);
3239
3276
  abortFetcher(key);
3240
3277
  }
3241
3278
  });
@@ -3289,6 +3326,7 @@ function createRouter(init) {
3289
3326
  fetchReloadIds.delete(key);
3290
3327
  fetchRedirectIds.delete(key);
3291
3328
  deletedFetchers.delete(key);
3329
+ cancelledFetcherLoads.delete(key);
3292
3330
  state.fetchers.delete(key);
3293
3331
  }
3294
3332
  function deleteFetcherAndUpdateState(key) {
@@ -4404,8 +4442,9 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
4404
4442
  if (fetchRedirectIds.has(key)) {
4405
4443
  // Never trigger a revalidation of an actively redirecting fetcher
4406
4444
  shouldRevalidate = false;
4407
- } else if (cancelledFetcherLoads.includes(key)) {
4408
- // Always revalidate if the fetcher was cancelled
4445
+ } else if (cancelledFetcherLoads.has(key)) {
4446
+ // Always mark for revalidation if the fetcher was cancelled
4447
+ cancelledFetcherLoads.delete(key);
4409
4448
  shouldRevalidate = true;
4410
4449
  } else if (fetcher && fetcher.state !== "idle" && fetcher.data === undefined) {
4411
4450
  // If the fetcher hasn't ever completed loading yet, then this isn't a
@@ -4727,8 +4766,7 @@ async function callLoaderOrAction(type, request, match, manifest, mapRouteProper
4727
4766
  async function convertHandlerResultToDataResult(handlerResult) {
4728
4767
  let {
4729
4768
  result,
4730
- type,
4731
- status
4769
+ type
4732
4770
  } = handlerResult;
4733
4771
  if (isResponse(result)) {
4734
4772
  let data;
@@ -4767,25 +4805,47 @@ async function convertHandlerResultToDataResult(handlerResult) {
4767
4805
  };
4768
4806
  }
4769
4807
  if (type === ResultType.error) {
4808
+ if (isDataWithResponseInit(result)) {
4809
+ var _result$init2;
4810
+ if (result.data instanceof Error) {
4811
+ var _result$init;
4812
+ return {
4813
+ type: ResultType.error,
4814
+ error: result.data,
4815
+ statusCode: (_result$init = result.init) == null ? void 0 : _result$init.status
4816
+ };
4817
+ }
4818
+
4819
+ // Convert thrown unstable_data() to ErrorResponse instances
4820
+ result = new ErrorResponseImpl(((_result$init2 = result.init) == null ? void 0 : _result$init2.status) || 500, undefined, result.data);
4821
+ }
4770
4822
  return {
4771
4823
  type: ResultType.error,
4772
4824
  error: result,
4773
- statusCode: isRouteErrorResponse(result) ? result.status : status
4825
+ statusCode: isRouteErrorResponse(result) ? result.status : undefined
4774
4826
  };
4775
4827
  }
4776
4828
  if (isDeferredData(result)) {
4777
- var _result$init, _result$init2;
4829
+ var _result$init3, _result$init4;
4778
4830
  return {
4779
4831
  type: ResultType.deferred,
4780
4832
  deferredData: result,
4781
- statusCode: (_result$init = result.init) == null ? void 0 : _result$init.status,
4782
- headers: ((_result$init2 = result.init) == null ? void 0 : _result$init2.headers) && new Headers(result.init.headers)
4833
+ statusCode: (_result$init3 = result.init) == null ? void 0 : _result$init3.status,
4834
+ headers: ((_result$init4 = result.init) == null ? void 0 : _result$init4.headers) && new Headers(result.init.headers)
4835
+ };
4836
+ }
4837
+ if (isDataWithResponseInit(result)) {
4838
+ var _result$init5, _result$init6;
4839
+ return {
4840
+ type: ResultType.data,
4841
+ data: result.data,
4842
+ statusCode: (_result$init5 = result.init) == null ? void 0 : _result$init5.status,
4843
+ headers: (_result$init6 = result.init) != null && _result$init6.headers ? new Headers(result.init.headers) : undefined
4783
4844
  };
4784
4845
  }
4785
4846
  return {
4786
4847
  type: ResultType.data,
4787
- data: result,
4788
- statusCode: status
4848
+ data: result
4789
4849
  };
4790
4850
  }
4791
4851
 
@@ -5148,6 +5208,9 @@ function isErrorResult(result) {
5148
5208
  function isRedirectResult(result) {
5149
5209
  return (result && result.type) === ResultType.redirect;
5150
5210
  }
5211
+ function isDataWithResponseInit(value) {
5212
+ return typeof value === "object" && value != null && "type" in value && "data" in value && "init" in value && value.type === "DataWithResponseInit";
5213
+ }
5151
5214
  function isDeferredData(value) {
5152
5215
  let deferred = value;
5153
5216
  return deferred && typeof deferred === "object" && typeof deferred.data === "object" && typeof deferred.subscribe === "function" && typeof deferred.cancel === "function" && typeof deferred.resolveData === "function";
@@ -5424,6 +5487,7 @@ exports.defer = defer;
5424
5487
  exports.generatePath = generatePath;
5425
5488
  exports.getStaticContextFromError = getStaticContextFromError;
5426
5489
  exports.getToPathname = getToPathname;
5490
+ exports.isDataWithResponseInit = isDataWithResponseInit;
5427
5491
  exports.isDeferredData = isDeferredData;
5428
5492
  exports.isRouteErrorResponse = isRouteErrorResponse;
5429
5493
  exports.joinPaths = joinPaths;
@@ -5434,7 +5498,9 @@ exports.normalizePathname = normalizePathname;
5434
5498
  exports.parsePath = parsePath;
5435
5499
  exports.redirect = redirect;
5436
5500
  exports.redirectDocument = redirectDocument;
5501
+ exports.replace = replace;
5437
5502
  exports.resolvePath = resolvePath;
5438
5503
  exports.resolveTo = resolveTo;
5439
5504
  exports.stripBasename = stripBasename;
5505
+ exports.unstable_data = data;
5440
5506
  //# sourceMappingURL=router.cjs.js.map