@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 +28 -0
- package/dist/index.d.ts +1 -1
- package/dist/router.cjs.js +83 -17
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.d.ts +2 -1
- package/dist/router.js +77 -18
- package/dist/router.js.map +1 -1
- package/dist/router.umd.js +83 -17
- package/dist/router.umd.js.map +1 -1
- package/dist/router.umd.min.js +2 -2
- package/dist/router.umd.min.js.map +1 -1
- package/dist/utils.d.ts +18 -1
- package/index.ts +2 -0
- package/package.json +1 -1
- package/router.ts +69 -12
- package/utils.ts +35 -2
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";
|
package/dist/router.cjs.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @remix-run/router v1.
|
|
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 &&
|
|
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.
|
|
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.
|
|
4408
|
-
// Always
|
|
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 :
|
|
4825
|
+
statusCode: isRouteErrorResponse(result) ? result.status : undefined
|
|
4774
4826
|
};
|
|
4775
4827
|
}
|
|
4776
4828
|
if (isDeferredData(result)) {
|
|
4777
|
-
var _result$
|
|
4829
|
+
var _result$init3, _result$init4;
|
|
4778
4830
|
return {
|
|
4779
4831
|
type: ResultType.deferred,
|
|
4780
4832
|
deferredData: result,
|
|
4781
|
-
statusCode: (_result$
|
|
4782
|
-
headers: ((_result$
|
|
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
|