@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/package.json
CHANGED
package/router.ts
CHANGED
|
@@ -193,7 +193,7 @@ export interface Router {
|
|
|
193
193
|
* Get/create a fetcher for the given key
|
|
194
194
|
* @param key
|
|
195
195
|
*/
|
|
196
|
-
getFetcher<TData = any>(key
|
|
196
|
+
getFetcher<TData = any>(key: string): Fetcher<TData>;
|
|
197
197
|
|
|
198
198
|
/**
|
|
199
199
|
* @internal
|
|
@@ -202,7 +202,7 @@ export interface Router {
|
|
|
202
202
|
* Delete the fetcher for a given key
|
|
203
203
|
* @param key
|
|
204
204
|
*/
|
|
205
|
-
deleteFetcher(key
|
|
205
|
+
deleteFetcher(key: string): void;
|
|
206
206
|
|
|
207
207
|
/**
|
|
208
208
|
* @internal
|
|
@@ -343,6 +343,7 @@ export type HydrationState = Partial<
|
|
|
343
343
|
* Future flags to toggle new feature behavior
|
|
344
344
|
*/
|
|
345
345
|
export interface FutureConfig {
|
|
346
|
+
v7_fetcherPersist: boolean;
|
|
346
347
|
v7_normalizeFormMethod: boolean;
|
|
347
348
|
v7_prependBasename: boolean;
|
|
348
349
|
}
|
|
@@ -763,6 +764,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
763
764
|
let basename = init.basename || "/";
|
|
764
765
|
// Config driven behavior flags
|
|
765
766
|
let future: FutureConfig = {
|
|
767
|
+
v7_fetcherPersist: false,
|
|
766
768
|
v7_normalizeFormMethod: false,
|
|
767
769
|
v7_prependBasename: false,
|
|
768
770
|
...init.future,
|
|
@@ -885,6 +887,10 @@ export function createRouter(init: RouterInit): Router {
|
|
|
885
887
|
// Most recent href/match for fetcher.load calls for fetchers
|
|
886
888
|
let fetchLoadMatches = new Map<string, FetchLoadMatch>();
|
|
887
889
|
|
|
890
|
+
// Fetchers that have requested a delete when using v7_fetcherPersist,
|
|
891
|
+
// they'll be officially removed after they return to idle
|
|
892
|
+
let deletedFetchers = new Set<string>();
|
|
893
|
+
|
|
888
894
|
// Store DeferredData instances for active route matches. When a
|
|
889
895
|
// route loader returns defer() we stick one in here. Then, when a nested
|
|
890
896
|
// promise resolves we update loaderData. If a new navigation starts we
|
|
@@ -1017,6 +1023,24 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1017
1023
|
subscribers.forEach((subscriber) =>
|
|
1018
1024
|
subscriber(state, { unstable_viewTransitionOpts: viewTransitionOpts })
|
|
1019
1025
|
);
|
|
1026
|
+
|
|
1027
|
+
// Remove idle fetchers from state since we only care about in-flight fetchers.
|
|
1028
|
+
if (future.v7_fetcherPersist) {
|
|
1029
|
+
state.fetchers.forEach((fetcher, key) => {
|
|
1030
|
+
if (fetcher.state === "idle") {
|
|
1031
|
+
if (deletedFetchers.has(key)) {
|
|
1032
|
+
// If the fetcher has unmounted and called router.deleteFetcher(),
|
|
1033
|
+
// we can totally delete the fetcher
|
|
1034
|
+
deleteFetcher(key);
|
|
1035
|
+
} else {
|
|
1036
|
+
// Otherwise, it must still be mounted in the UI so we just remove
|
|
1037
|
+
// it from state now that we've handed off the data to the React
|
|
1038
|
+
// layer. Things such as fetchLoadMatches remain for revalidation.
|
|
1039
|
+
state.fetchers.delete(key);
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1020
1044
|
}
|
|
1021
1045
|
|
|
1022
1046
|
// Complete a navigation returning the state.navigation back to the IDLE_NAVIGATION
|
|
@@ -2009,7 +2033,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2009
2033
|
state.fetchers.set(key, doneFetcher);
|
|
2010
2034
|
}
|
|
2011
2035
|
|
|
2012
|
-
|
|
2036
|
+
abortStaleFetchLoads(loadId);
|
|
2013
2037
|
|
|
2014
2038
|
// If we are currently in a navigation loading state and this fetcher is
|
|
2015
2039
|
// more recent than the navigation, we want the newer data so abort the
|
|
@@ -2039,9 +2063,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2039
2063
|
matches,
|
|
2040
2064
|
errors
|
|
2041
2065
|
),
|
|
2042
|
-
|
|
2043
|
-
? { fetchers: new Map(state.fetchers) }
|
|
2044
|
-
: {}),
|
|
2066
|
+
fetchers: new Map(state.fetchers),
|
|
2045
2067
|
});
|
|
2046
2068
|
isRevalidationRequired = false;
|
|
2047
2069
|
}
|
|
@@ -2376,9 +2398,19 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2376
2398
|
fetchLoadMatches.delete(key);
|
|
2377
2399
|
fetchReloadIds.delete(key);
|
|
2378
2400
|
fetchRedirectIds.delete(key);
|
|
2401
|
+
deletedFetchers.delete(key);
|
|
2379
2402
|
state.fetchers.delete(key);
|
|
2380
2403
|
}
|
|
2381
2404
|
|
|
2405
|
+
function deleteFetcherAndUpdateState(key: string): void {
|
|
2406
|
+
if (future.v7_fetcherPersist) {
|
|
2407
|
+
deletedFetchers.add(key);
|
|
2408
|
+
} else {
|
|
2409
|
+
deleteFetcher(key);
|
|
2410
|
+
}
|
|
2411
|
+
updateState({ fetchers: new Map(state.fetchers) });
|
|
2412
|
+
}
|
|
2413
|
+
|
|
2382
2414
|
function abortFetcher(key: string) {
|
|
2383
2415
|
let controller = fetchControllers.get(key);
|
|
2384
2416
|
invariant(controller, `Expected fetch controller: ${key}`);
|
|
@@ -2613,7 +2645,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2613
2645
|
createHref: (to: To) => init.history.createHref(to),
|
|
2614
2646
|
encodeLocation: (to: To) => init.history.encodeLocation(to),
|
|
2615
2647
|
getFetcher,
|
|
2616
|
-
deleteFetcher,
|
|
2648
|
+
deleteFetcher: deleteFetcherAndUpdateState,
|
|
2617
2649
|
dispose,
|
|
2618
2650
|
getBlocker,
|
|
2619
2651
|
deleteBlocker,
|
package/utils.ts
CHANGED
|
@@ -903,7 +903,7 @@ export function matchPath<
|
|
|
903
903
|
pattern = { path: pattern, caseSensitive: false, end: true };
|
|
904
904
|
}
|
|
905
905
|
|
|
906
|
-
let [matcher,
|
|
906
|
+
let [matcher, compiledParams] = compilePath(
|
|
907
907
|
pattern.path,
|
|
908
908
|
pattern.caseSensitive,
|
|
909
909
|
pattern.end
|
|
@@ -915,8 +915,8 @@ export function matchPath<
|
|
|
915
915
|
let matchedPathname = match[0];
|
|
916
916
|
let pathnameBase = matchedPathname.replace(/(.)\/+$/, "$1");
|
|
917
917
|
let captureGroups = match.slice(1);
|
|
918
|
-
let params: Params =
|
|
919
|
-
(memo, paramName, index) => {
|
|
918
|
+
let params: Params = compiledParams.reduce<Mutable<Params>>(
|
|
919
|
+
(memo, { paramName, isOptional }, index) => {
|
|
920
920
|
// We need to compute the pathnameBase here using the raw splat value
|
|
921
921
|
// instead of using params["*"] later because it will be decoded then
|
|
922
922
|
if (paramName === "*") {
|
|
@@ -926,10 +926,12 @@ export function matchPath<
|
|
|
926
926
|
.replace(/(.)\/+$/, "$1");
|
|
927
927
|
}
|
|
928
928
|
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
paramName
|
|
932
|
-
|
|
929
|
+
const value = captureGroups[index];
|
|
930
|
+
if (isOptional && !value) {
|
|
931
|
+
memo[paramName] = undefined;
|
|
932
|
+
} else {
|
|
933
|
+
memo[paramName] = safelyDecodeURIComponent(value || "", paramName);
|
|
934
|
+
}
|
|
933
935
|
return memo;
|
|
934
936
|
},
|
|
935
937
|
{}
|
|
@@ -943,11 +945,13 @@ export function matchPath<
|
|
|
943
945
|
};
|
|
944
946
|
}
|
|
945
947
|
|
|
948
|
+
type CompiledPathParam = { paramName: string; isOptional?: boolean };
|
|
949
|
+
|
|
946
950
|
function compilePath(
|
|
947
951
|
path: string,
|
|
948
952
|
caseSensitive = false,
|
|
949
953
|
end = true
|
|
950
|
-
): [RegExp,
|
|
954
|
+
): [RegExp, CompiledPathParam[]] {
|
|
951
955
|
warning(
|
|
952
956
|
path === "*" || !path.endsWith("*") || path.endsWith("/*"),
|
|
953
957
|
`Route path "${path}" will be treated as if it were ` +
|
|
@@ -956,20 +960,20 @@ function compilePath(
|
|
|
956
960
|
`please change the route path to "${path.replace(/\*$/, "/*")}".`
|
|
957
961
|
);
|
|
958
962
|
|
|
959
|
-
let
|
|
963
|
+
let params: CompiledPathParam[] = [];
|
|
960
964
|
let regexpSource =
|
|
961
965
|
"^" +
|
|
962
966
|
path
|
|
963
967
|
.replace(/\/*\*?$/, "") // Ignore trailing / and /*, we'll handle it below
|
|
964
968
|
.replace(/^\/*/, "/") // Make sure it has a leading /
|
|
965
|
-
.replace(/[
|
|
966
|
-
.replace(/\/:(\w+)
|
|
967
|
-
|
|
968
|
-
return "/([^\\/]+)";
|
|
969
|
+
.replace(/[\\.*+^${}|()[\]]/g, "\\$&") // Escape special regex chars
|
|
970
|
+
.replace(/\/:(\w+)(\?)?/g, (_: string, paramName: string, isOptional) => {
|
|
971
|
+
params.push({ paramName, isOptional: isOptional != null });
|
|
972
|
+
return isOptional ? "/?([^\\/]+)?" : "/([^\\/]+)";
|
|
969
973
|
});
|
|
970
974
|
|
|
971
975
|
if (path.endsWith("*")) {
|
|
972
|
-
|
|
976
|
+
params.push({ paramName: "*" });
|
|
973
977
|
regexpSource +=
|
|
974
978
|
path === "*" || path === "/*"
|
|
975
979
|
? "(.*)$" // Already matched the initial /, just match the rest
|
|
@@ -992,7 +996,7 @@ function compilePath(
|
|
|
992
996
|
|
|
993
997
|
let matcher = new RegExp(regexpSource, caseSensitive ? undefined : "i");
|
|
994
998
|
|
|
995
|
-
return [matcher,
|
|
999
|
+
return [matcher, params];
|
|
996
1000
|
}
|
|
997
1001
|
|
|
998
1002
|
function safelyDecodeURI(value: string) {
|