@remix-run/router 1.10.0 → 1.11.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 +18 -0
- package/dist/router.cjs.js +68 -21
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.d.ts +3 -2
- package/dist/router.js +66 -21
- package/dist/router.js.map +1 -1
- package/dist/router.umd.js +68 -21
- 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/package.json +1 -1
- package/router.ts +39 -7
- package/utils.ts +19 -15
package/dist/router.d.ts
CHANGED
|
@@ -120,7 +120,7 @@ export interface Router {
|
|
|
120
120
|
* Get/create a fetcher for the given key
|
|
121
121
|
* @param key
|
|
122
122
|
*/
|
|
123
|
-
getFetcher<TData = any>(key
|
|
123
|
+
getFetcher<TData = any>(key: string): Fetcher<TData>;
|
|
124
124
|
/**
|
|
125
125
|
* @internal
|
|
126
126
|
* PRIVATE - DO NOT USE
|
|
@@ -128,7 +128,7 @@ export interface Router {
|
|
|
128
128
|
* Delete the fetcher for a given key
|
|
129
129
|
* @param key
|
|
130
130
|
*/
|
|
131
|
-
deleteFetcher(key
|
|
131
|
+
deleteFetcher(key: string): void;
|
|
132
132
|
/**
|
|
133
133
|
* @internal
|
|
134
134
|
* PRIVATE - DO NOT USE
|
|
@@ -246,6 +246,7 @@ export type HydrationState = Partial<Pick<RouterState, "loaderData" | "actionDat
|
|
|
246
246
|
* Future flags to toggle new feature behavior
|
|
247
247
|
*/
|
|
248
248
|
export interface FutureConfig {
|
|
249
|
+
v7_fetcherPersist: boolean;
|
|
249
250
|
v7_normalizeFormMethod: boolean;
|
|
250
251
|
v7_prependBasename: boolean;
|
|
251
252
|
}
|
package/dist/router.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @remix-run/router v1.
|
|
2
|
+
* @remix-run/router v1.11.0-pre.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -768,20 +768,29 @@ function matchPath(pattern, pathname) {
|
|
|
768
768
|
end: true
|
|
769
769
|
};
|
|
770
770
|
}
|
|
771
|
-
let [matcher,
|
|
771
|
+
let [matcher, compiledParams] = compilePath(pattern.path, pattern.caseSensitive, pattern.end);
|
|
772
772
|
let match = pathname.match(matcher);
|
|
773
773
|
if (!match) return null;
|
|
774
774
|
let matchedPathname = match[0];
|
|
775
775
|
let pathnameBase = matchedPathname.replace(/(.)\/+$/, "$1");
|
|
776
776
|
let captureGroups = match.slice(1);
|
|
777
|
-
let params =
|
|
777
|
+
let params = compiledParams.reduce((memo, _ref, index) => {
|
|
778
|
+
let {
|
|
779
|
+
paramName,
|
|
780
|
+
isOptional
|
|
781
|
+
} = _ref;
|
|
778
782
|
// We need to compute the pathnameBase here using the raw splat value
|
|
779
783
|
// instead of using params["*"] later because it will be decoded then
|
|
780
784
|
if (paramName === "*") {
|
|
781
785
|
let splatValue = captureGroups[index] || "";
|
|
782
786
|
pathnameBase = matchedPathname.slice(0, matchedPathname.length - splatValue.length).replace(/(.)\/+$/, "$1");
|
|
783
787
|
}
|
|
784
|
-
|
|
788
|
+
const value = captureGroups[index];
|
|
789
|
+
if (isOptional && !value) {
|
|
790
|
+
memo[paramName] = undefined;
|
|
791
|
+
} else {
|
|
792
|
+
memo[paramName] = safelyDecodeURIComponent(value || "", paramName);
|
|
793
|
+
}
|
|
785
794
|
return memo;
|
|
786
795
|
}, {});
|
|
787
796
|
return {
|
|
@@ -799,16 +808,21 @@ function compilePath(path, caseSensitive, end) {
|
|
|
799
808
|
end = true;
|
|
800
809
|
}
|
|
801
810
|
warning(path === "*" || !path.endsWith("*") || path.endsWith("/*"), "Route path \"" + path + "\" will be treated as if it were " + ("\"" + path.replace(/\*$/, "/*") + "\" because the `*` character must ") + "always follow a `/` in the pattern. To get rid of this warning, " + ("please change the route path to \"" + path.replace(/\*$/, "/*") + "\"."));
|
|
802
|
-
let
|
|
811
|
+
let params = [];
|
|
803
812
|
let regexpSource = "^" + path.replace(/\/*\*?$/, "") // Ignore trailing / and /*, we'll handle it below
|
|
804
813
|
.replace(/^\/*/, "/") // Make sure it has a leading /
|
|
805
|
-
.replace(/[
|
|
806
|
-
.replace(/\/:(\w+)
|
|
807
|
-
|
|
808
|
-
|
|
814
|
+
.replace(/[\\.*+^${}|()[\]]/g, "\\$&") // Escape special regex chars
|
|
815
|
+
.replace(/\/:(\w+)(\?)?/g, (_, paramName, isOptional) => {
|
|
816
|
+
params.push({
|
|
817
|
+
paramName,
|
|
818
|
+
isOptional: isOptional != null
|
|
819
|
+
});
|
|
820
|
+
return isOptional ? "/?([^\\/]+)?" : "/([^\\/]+)";
|
|
809
821
|
});
|
|
810
822
|
if (path.endsWith("*")) {
|
|
811
|
-
|
|
823
|
+
params.push({
|
|
824
|
+
paramName: "*"
|
|
825
|
+
});
|
|
812
826
|
regexpSource += path === "*" || path === "/*" ? "(.*)$" // Already matched the initial /, just match the rest
|
|
813
827
|
: "(?:\\/(.+)|\\/*)$"; // Don't include the / in params["*"]
|
|
814
828
|
} else if (end) {
|
|
@@ -825,7 +839,7 @@ function compilePath(path, caseSensitive, end) {
|
|
|
825
839
|
regexpSource += "(?:(?=\\/|$))";
|
|
826
840
|
} else ;
|
|
827
841
|
let matcher = new RegExp(regexpSource, caseSensitive ? undefined : "i");
|
|
828
|
-
return [matcher,
|
|
842
|
+
return [matcher, params];
|
|
829
843
|
}
|
|
830
844
|
function safelyDecodeURI(value) {
|
|
831
845
|
try {
|
|
@@ -1038,8 +1052,8 @@ class DeferredData {
|
|
|
1038
1052
|
let onAbort = () => reject(new AbortedDeferredError("Deferred data aborted"));
|
|
1039
1053
|
this.unlistenAbortSignal = () => this.controller.signal.removeEventListener("abort", onAbort);
|
|
1040
1054
|
this.controller.signal.addEventListener("abort", onAbort);
|
|
1041
|
-
this.data = Object.entries(data).reduce((acc,
|
|
1042
|
-
let [key, value] =
|
|
1055
|
+
this.data = Object.entries(data).reduce((acc, _ref2) => {
|
|
1056
|
+
let [key, value] = _ref2;
|
|
1043
1057
|
return Object.assign(acc, {
|
|
1044
1058
|
[key]: this.trackPromise(key, value)
|
|
1045
1059
|
});
|
|
@@ -1136,8 +1150,8 @@ class DeferredData {
|
|
|
1136
1150
|
}
|
|
1137
1151
|
get unwrappedData() {
|
|
1138
1152
|
invariant(this.data !== null && this.done, "Can only unwrap data on initialized and settled deferreds");
|
|
1139
|
-
return Object.entries(this.data).reduce((acc,
|
|
1140
|
-
let [key, value] =
|
|
1153
|
+
return Object.entries(this.data).reduce((acc, _ref3) => {
|
|
1154
|
+
let [key, value] = _ref3;
|
|
1141
1155
|
return Object.assign(acc, {
|
|
1142
1156
|
[key]: unwrapTrackedPromise(value)
|
|
1143
1157
|
});
|
|
@@ -1301,6 +1315,7 @@ function createRouter(init) {
|
|
|
1301
1315
|
let basename = init.basename || "/";
|
|
1302
1316
|
// Config driven behavior flags
|
|
1303
1317
|
let future = _extends({
|
|
1318
|
+
v7_fetcherPersist: false,
|
|
1304
1319
|
v7_normalizeFormMethod: false,
|
|
1305
1320
|
v7_prependBasename: false
|
|
1306
1321
|
}, init.future);
|
|
@@ -1403,6 +1418,9 @@ function createRouter(init) {
|
|
|
1403
1418
|
let fetchRedirectIds = new Set();
|
|
1404
1419
|
// Most recent href/match for fetcher.load calls for fetchers
|
|
1405
1420
|
let fetchLoadMatches = new Map();
|
|
1421
|
+
// Fetchers that have requested a delete when using v7_fetcherPersist,
|
|
1422
|
+
// they'll be officially removed after they return to idle
|
|
1423
|
+
let deletedFetchers = new Set();
|
|
1406
1424
|
// Store DeferredData instances for active route matches. When a
|
|
1407
1425
|
// route loader returns defer() we stick one in here. Then, when a nested
|
|
1408
1426
|
// promise resolves we update loaderData. If a new navigation starts we
|
|
@@ -1510,6 +1528,23 @@ function createRouter(init) {
|
|
|
1510
1528
|
subscribers.forEach(subscriber => subscriber(state, {
|
|
1511
1529
|
unstable_viewTransitionOpts: viewTransitionOpts
|
|
1512
1530
|
}));
|
|
1531
|
+
// Remove idle fetchers from state since we only care about in-flight fetchers.
|
|
1532
|
+
if (future.v7_fetcherPersist) {
|
|
1533
|
+
state.fetchers.forEach((fetcher, key) => {
|
|
1534
|
+
if (fetcher.state === "idle") {
|
|
1535
|
+
if (deletedFetchers.has(key)) {
|
|
1536
|
+
// If the fetcher has unmounted and called router.deleteFetcher(),
|
|
1537
|
+
// we can totally delete the fetcher
|
|
1538
|
+
deleteFetcher(key);
|
|
1539
|
+
} else {
|
|
1540
|
+
// Otherwise, it must still be mounted in the UI so we just remove
|
|
1541
|
+
// it from state now that we've handed off the data to the React
|
|
1542
|
+
// layer. Things such as fetchLoadMatches remain for revalidation.
|
|
1543
|
+
state.fetchers.delete(key);
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
});
|
|
1547
|
+
}
|
|
1513
1548
|
}
|
|
1514
1549
|
// Complete a navigation returning the state.navigation back to the IDLE_NAVIGATION
|
|
1515
1550
|
// and setting state.[historyAction/location/matches] to the new route.
|
|
@@ -2206,7 +2241,7 @@ function createRouter(init) {
|
|
|
2206
2241
|
let doneFetcher = getDoneFetcher(actionResult.data);
|
|
2207
2242
|
state.fetchers.set(key, doneFetcher);
|
|
2208
2243
|
}
|
|
2209
|
-
|
|
2244
|
+
abortStaleFetchLoads(loadId);
|
|
2210
2245
|
// If we are currently in a navigation loading state and this fetcher is
|
|
2211
2246
|
// more recent than the navigation, we want the newer data so abort the
|
|
2212
2247
|
// navigation and complete it with the fetcher data
|
|
@@ -2223,12 +2258,11 @@ function createRouter(init) {
|
|
|
2223
2258
|
// otherwise just update with the fetcher data, preserving any existing
|
|
2224
2259
|
// loaderData for loaders that did not need to reload. We have to
|
|
2225
2260
|
// manually merge here since we aren't going through completeNavigation
|
|
2226
|
-
updateState(
|
|
2261
|
+
updateState({
|
|
2227
2262
|
errors,
|
|
2228
|
-
loaderData: mergeLoaderData(state.loaderData, loaderData, matches, errors)
|
|
2229
|
-
}, didAbortFetchLoads || revalidatingFetchers.length > 0 ? {
|
|
2263
|
+
loaderData: mergeLoaderData(state.loaderData, loaderData, matches, errors),
|
|
2230
2264
|
fetchers: new Map(state.fetchers)
|
|
2231
|
-
}
|
|
2265
|
+
});
|
|
2232
2266
|
isRevalidationRequired = false;
|
|
2233
2267
|
}
|
|
2234
2268
|
}
|
|
@@ -2456,8 +2490,19 @@ function createRouter(init) {
|
|
|
2456
2490
|
fetchLoadMatches.delete(key);
|
|
2457
2491
|
fetchReloadIds.delete(key);
|
|
2458
2492
|
fetchRedirectIds.delete(key);
|
|
2493
|
+
deletedFetchers.delete(key);
|
|
2459
2494
|
state.fetchers.delete(key);
|
|
2460
2495
|
}
|
|
2496
|
+
function deleteFetcherAndUpdateState(key) {
|
|
2497
|
+
if (future.v7_fetcherPersist) {
|
|
2498
|
+
deletedFetchers.add(key);
|
|
2499
|
+
} else {
|
|
2500
|
+
deleteFetcher(key);
|
|
2501
|
+
}
|
|
2502
|
+
updateState({
|
|
2503
|
+
fetchers: new Map(state.fetchers)
|
|
2504
|
+
});
|
|
2505
|
+
}
|
|
2461
2506
|
function abortFetcher(key) {
|
|
2462
2507
|
let controller = fetchControllers.get(key);
|
|
2463
2508
|
invariant(controller, "Expected fetch controller: " + key);
|
|
@@ -2646,7 +2691,7 @@ function createRouter(init) {
|
|
|
2646
2691
|
createHref: to => init.history.createHref(to),
|
|
2647
2692
|
encodeLocation: to => init.history.encodeLocation(to),
|
|
2648
2693
|
getFetcher,
|
|
2649
|
-
deleteFetcher,
|
|
2694
|
+
deleteFetcher: deleteFetcherAndUpdateState,
|
|
2650
2695
|
dispose,
|
|
2651
2696
|
getBlocker,
|
|
2652
2697
|
deleteBlocker,
|