@remix-run/router 1.3.0 → 1.3.1
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 +14 -0
- package/dist/history.d.ts +1 -1
- package/dist/router.cjs.js +121 -117
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.js +121 -117
- package/dist/router.js.map +1 -1
- package/dist/router.umd.js +121 -117
- 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 +2 -2
- package/history.ts +6 -22
- package/package.json +1 -1
- package/router.ts +133 -135
- package/utils.ts +14 -3
package/dist/utils.d.ts
CHANGED
|
@@ -383,7 +383,7 @@ export declare class ErrorResponse {
|
|
|
383
383
|
}
|
|
384
384
|
/**
|
|
385
385
|
* Check if the given error is an ErrorResponse generated from a 4xx/5xx
|
|
386
|
-
* Response
|
|
386
|
+
* Response thrown from an action/loader
|
|
387
387
|
*/
|
|
388
|
-
export declare function isRouteErrorResponse(
|
|
388
|
+
export declare function isRouteErrorResponse(error: any): error is ErrorResponse;
|
|
389
389
|
export {};
|
package/history.ts
CHANGED
|
@@ -85,7 +85,7 @@ export interface Update {
|
|
|
85
85
|
/**
|
|
86
86
|
* The delta between this location and the former location in the history stack
|
|
87
87
|
*/
|
|
88
|
-
delta: number;
|
|
88
|
+
delta: number | null;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
/**
|
|
@@ -612,28 +612,12 @@ function getUrlBasedHistory(
|
|
|
612
612
|
}
|
|
613
613
|
|
|
614
614
|
function handlePop() {
|
|
615
|
-
|
|
615
|
+
action = Action.Pop;
|
|
616
616
|
let nextIndex = getIndex();
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
action
|
|
621
|
-
index = nextIndex;
|
|
622
|
-
if (listener) {
|
|
623
|
-
listener({ action, location: history.location, delta });
|
|
624
|
-
}
|
|
625
|
-
} else {
|
|
626
|
-
warning(
|
|
627
|
-
false,
|
|
628
|
-
// TODO: Write up a doc that explains our blocking strategy in detail
|
|
629
|
-
// and link to it here so people can understand better what is going on
|
|
630
|
-
// and how to avoid it.
|
|
631
|
-
`You are trying to block a POP navigation to a location that was not ` +
|
|
632
|
-
`created by @remix-run/router. The block will fail silently in ` +
|
|
633
|
-
`production, but in general you should do all navigation with the ` +
|
|
634
|
-
`router (instead of using window.history.pushState directly) ` +
|
|
635
|
-
`to avoid this situation.`
|
|
636
|
-
);
|
|
617
|
+
let delta = nextIndex == null ? null : nextIndex - index;
|
|
618
|
+
index = nextIndex;
|
|
619
|
+
if (listener) {
|
|
620
|
+
listener({ action, location: history.location, delta });
|
|
637
621
|
}
|
|
638
622
|
}
|
|
639
623
|
|
package/package.json
CHANGED
package/router.ts
CHANGED
|
@@ -21,6 +21,7 @@ import type {
|
|
|
21
21
|
SuccessResult,
|
|
22
22
|
AgnosticRouteMatch,
|
|
23
23
|
MutationFormMethod,
|
|
24
|
+
ShouldRevalidateFunction,
|
|
24
25
|
} from "./utils";
|
|
25
26
|
import {
|
|
26
27
|
DeferredData,
|
|
@@ -549,25 +550,22 @@ interface HandleLoadersResult extends ShortCircuitable {
|
|
|
549
550
|
}
|
|
550
551
|
|
|
551
552
|
/**
|
|
552
|
-
*
|
|
553
|
-
*
|
|
553
|
+
* Cached info for active fetcher.load() instances so they can participate
|
|
554
|
+
* in revalidation
|
|
554
555
|
*/
|
|
555
|
-
|
|
556
|
-
string
|
|
557
|
-
string
|
|
558
|
-
AgnosticDataRouteMatch
|
|
559
|
-
AgnosticDataRouteMatch[]
|
|
560
|
-
|
|
556
|
+
interface FetchLoadMatch {
|
|
557
|
+
routeId: string;
|
|
558
|
+
path: string;
|
|
559
|
+
match: AgnosticDataRouteMatch;
|
|
560
|
+
matches: AgnosticDataRouteMatch[];
|
|
561
|
+
}
|
|
561
562
|
|
|
562
563
|
/**
|
|
563
|
-
*
|
|
564
|
-
* fetcher.load()
|
|
564
|
+
* Identified fetcher.load() calls that need to be revalidated
|
|
565
565
|
*/
|
|
566
|
-
|
|
567
|
-
string
|
|
568
|
-
|
|
569
|
-
AgnosticDataRouteMatch[]
|
|
570
|
-
];
|
|
566
|
+
interface RevalidatingFetcher extends FetchLoadMatch {
|
|
567
|
+
key: string;
|
|
568
|
+
}
|
|
571
569
|
|
|
572
570
|
/**
|
|
573
571
|
* Wrapper object to allow us to throw any response out from callLoaderOrAction
|
|
@@ -783,12 +781,23 @@ export function createRouter(init: RouterInit): Router {
|
|
|
783
781
|
return;
|
|
784
782
|
}
|
|
785
783
|
|
|
784
|
+
warning(
|
|
785
|
+
activeBlocker != null && delta === null,
|
|
786
|
+
"You are trying to use a blocker on a POP navigation to a location " +
|
|
787
|
+
"that was not created by @remix-run/router. This will fail silently in " +
|
|
788
|
+
"production. This can happen if you are navigating outside the router " +
|
|
789
|
+
"via `window.history.pushState`/`window.location.hash` instead of using " +
|
|
790
|
+
"router navigation APIs. This can also happen if you are using " +
|
|
791
|
+
"createHashRouter and the user manually changes the URL."
|
|
792
|
+
);
|
|
793
|
+
|
|
786
794
|
let blockerKey = shouldBlockNavigation({
|
|
787
795
|
currentLocation: state.location,
|
|
788
796
|
nextLocation: location,
|
|
789
797
|
historyAction,
|
|
790
798
|
});
|
|
791
|
-
|
|
799
|
+
|
|
800
|
+
if (blockerKey && delta != null) {
|
|
792
801
|
// Restore the URL to match the current UI, but don't update router state
|
|
793
802
|
ignoreNextHistoryUpdate = true;
|
|
794
803
|
init.history.go(delta * -1);
|
|
@@ -1121,8 +1130,13 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1121
1130
|
return;
|
|
1122
1131
|
}
|
|
1123
1132
|
|
|
1124
|
-
// Short circuit if it's only a hash change
|
|
1125
|
-
|
|
1133
|
+
// Short circuit if it's only a hash change and not a mutation submission
|
|
1134
|
+
// For example, on /page#hash and submit a <Form method="post"> which will
|
|
1135
|
+
// default to a navigation to /page
|
|
1136
|
+
if (
|
|
1137
|
+
isHashChangeOnly(state.location, location) &&
|
|
1138
|
+
!(opts && opts.submission && isMutationMethod(opts.submission.formMethod))
|
|
1139
|
+
) {
|
|
1126
1140
|
completeNavigation(location, { matches });
|
|
1127
1141
|
return;
|
|
1128
1142
|
}
|
|
@@ -1380,8 +1394,8 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1380
1394
|
// preserving any new action data or existing action data (in the case of
|
|
1381
1395
|
// a revalidation interrupting an actionReload)
|
|
1382
1396
|
if (!isUninterruptedRevalidation) {
|
|
1383
|
-
revalidatingFetchers.forEach((
|
|
1384
|
-
let fetcher = state.fetchers.get(key);
|
|
1397
|
+
revalidatingFetchers.forEach((rf) => {
|
|
1398
|
+
let fetcher = state.fetchers.get(rf.key);
|
|
1385
1399
|
let revalidatingFetcher: FetcherStates["Loading"] = {
|
|
1386
1400
|
state: "loading",
|
|
1387
1401
|
data: fetcher && fetcher.data,
|
|
@@ -1391,7 +1405,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1391
1405
|
formData: undefined,
|
|
1392
1406
|
" _hasFetcherDoneAnything ": true,
|
|
1393
1407
|
};
|
|
1394
|
-
state.fetchers.set(key, revalidatingFetcher);
|
|
1408
|
+
state.fetchers.set(rf.key, revalidatingFetcher);
|
|
1395
1409
|
});
|
|
1396
1410
|
let actionData = pendingActionData || state.actionData;
|
|
1397
1411
|
updateState({
|
|
@@ -1408,8 +1422,8 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1408
1422
|
}
|
|
1409
1423
|
|
|
1410
1424
|
pendingNavigationLoadId = ++incrementingLoadId;
|
|
1411
|
-
revalidatingFetchers.forEach((
|
|
1412
|
-
fetchControllers.set(key, pendingNavigationController!)
|
|
1425
|
+
revalidatingFetchers.forEach((rf) =>
|
|
1426
|
+
fetchControllers.set(rf.key, pendingNavigationController!)
|
|
1413
1427
|
);
|
|
1414
1428
|
|
|
1415
1429
|
let { results, loaderResults, fetcherResults } =
|
|
@@ -1428,7 +1442,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1428
1442
|
// Clean up _after_ loaders have completed. Don't clean up if we short
|
|
1429
1443
|
// circuited because fetchControllers would have been aborted and
|
|
1430
1444
|
// reassigned to new controllers for the next navigation
|
|
1431
|
-
revalidatingFetchers.forEach((
|
|
1445
|
+
revalidatingFetchers.forEach((rf) => fetchControllers.delete(rf.key));
|
|
1432
1446
|
|
|
1433
1447
|
// If any loaders returned a redirect Response, start a new REPLACE navigation
|
|
1434
1448
|
let redirect = findRedirect(results);
|
|
@@ -1507,6 +1521,8 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1507
1521
|
let { path, submission } = normalizeNavigateOptions(href, opts, true);
|
|
1508
1522
|
let match = getTargetMatch(matches, path);
|
|
1509
1523
|
|
|
1524
|
+
pendingPreventScrollReset = (opts && opts.preventScrollReset) === true;
|
|
1525
|
+
|
|
1510
1526
|
if (submission && isMutationMethod(submission.formMethod)) {
|
|
1511
1527
|
handleFetcherAction(key, routeId, path, match, matches, submission);
|
|
1512
1528
|
return;
|
|
@@ -1514,7 +1530,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1514
1530
|
|
|
1515
1531
|
// Store off the match so we can call it's shouldRevalidate on subsequent
|
|
1516
1532
|
// revalidations
|
|
1517
|
-
fetchLoadMatches.set(key,
|
|
1533
|
+
fetchLoadMatches.set(key, { routeId, path, match, matches });
|
|
1518
1534
|
handleFetcherLoader(key, routeId, path, match, matches, submission);
|
|
1519
1535
|
}
|
|
1520
1536
|
|
|
@@ -1651,8 +1667,9 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1651
1667
|
// current fetcher which we want to keep in it's current loading state which
|
|
1652
1668
|
// contains it's action submission info + action data
|
|
1653
1669
|
revalidatingFetchers
|
|
1654
|
-
.filter((
|
|
1655
|
-
.forEach((
|
|
1670
|
+
.filter((rf) => rf.key !== key)
|
|
1671
|
+
.forEach((rf) => {
|
|
1672
|
+
let staleKey = rf.key;
|
|
1656
1673
|
let existingFetcher = state.fetchers.get(staleKey);
|
|
1657
1674
|
let revalidatingFetcher: FetcherStates["Loading"] = {
|
|
1658
1675
|
state: "loading",
|
|
@@ -1684,9 +1701,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1684
1701
|
|
|
1685
1702
|
fetchReloadIds.delete(key);
|
|
1686
1703
|
fetchControllers.delete(key);
|
|
1687
|
-
revalidatingFetchers.forEach((
|
|
1688
|
-
fetchControllers.delete(staleKey)
|
|
1689
|
-
);
|
|
1704
|
+
revalidatingFetchers.forEach((r) => fetchControllers.delete(r.key));
|
|
1690
1705
|
|
|
1691
1706
|
let redirect = findRedirect(results);
|
|
1692
1707
|
if (redirect) {
|
|
@@ -1980,12 +1995,12 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1980
1995
|
...matchesToLoad.map((match) =>
|
|
1981
1996
|
callLoaderOrAction("loader", request, match, matches, router.basename)
|
|
1982
1997
|
),
|
|
1983
|
-
...fetchersToLoad.map((
|
|
1998
|
+
...fetchersToLoad.map((f) =>
|
|
1984
1999
|
callLoaderOrAction(
|
|
1985
2000
|
"loader",
|
|
1986
|
-
createClientSideRequest(init.history,
|
|
1987
|
-
match,
|
|
1988
|
-
|
|
2001
|
+
createClientSideRequest(init.history, f.path, request.signal),
|
|
2002
|
+
f.match,
|
|
2003
|
+
f.matches,
|
|
1989
2004
|
router.basename
|
|
1990
2005
|
)
|
|
1991
2006
|
),
|
|
@@ -2004,7 +2019,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2004
2019
|
),
|
|
2005
2020
|
resolveDeferredResults(
|
|
2006
2021
|
currentMatches,
|
|
2007
|
-
fetchersToLoad.map((
|
|
2022
|
+
fetchersToLoad.map((f) => f.match),
|
|
2008
2023
|
fetcherResults,
|
|
2009
2024
|
request.signal,
|
|
2010
2025
|
true
|
|
@@ -2845,25 +2860,14 @@ function normalizeNavigateOptions(
|
|
|
2845
2860
|
|
|
2846
2861
|
// Flatten submission onto URLSearchParams for GET submissions
|
|
2847
2862
|
let parsedPath = parsePath(path);
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
isFetcher &&
|
|
2855
|
-
parsedPath.search &&
|
|
2856
|
-
hasNakedIndexQuery(parsedPath.search)
|
|
2857
|
-
) {
|
|
2858
|
-
searchParams.append("index", "");
|
|
2859
|
-
}
|
|
2860
|
-
parsedPath.search = `?${searchParams}`;
|
|
2861
|
-
} catch (e) {
|
|
2862
|
-
return {
|
|
2863
|
-
path,
|
|
2864
|
-
error: getInternalRouterError(400),
|
|
2865
|
-
};
|
|
2863
|
+
let searchParams = convertFormDataToSearchParams(opts.formData);
|
|
2864
|
+
// Since fetcher GET submissions only run a single loader (as opposed to
|
|
2865
|
+
// navigation GET submissions which run all loaders), we need to preserve
|
|
2866
|
+
// any incoming ?index params
|
|
2867
|
+
if (isFetcher && parsedPath.search && hasNakedIndexQuery(parsedPath.search)) {
|
|
2868
|
+
searchParams.append("index", "");
|
|
2866
2869
|
}
|
|
2870
|
+
parsedPath.search = `?${searchParams}`;
|
|
2867
2871
|
|
|
2868
2872
|
return { path: createPath(parsedPath), submission };
|
|
2869
2873
|
}
|
|
@@ -2903,47 +2907,81 @@ function getMatchesToLoad(
|
|
|
2903
2907
|
? Object.values(pendingActionData)[0]
|
|
2904
2908
|
: undefined;
|
|
2905
2909
|
|
|
2910
|
+
let currentUrl = history.createURL(state.location);
|
|
2911
|
+
let nextUrl = history.createURL(location);
|
|
2912
|
+
|
|
2913
|
+
let defaultShouldRevalidate =
|
|
2914
|
+
// Forced revalidation due to submission, useRevalidate, or X-Remix-Revalidate
|
|
2915
|
+
isRevalidationRequired ||
|
|
2916
|
+
// Clicked the same link, resubmitted a GET form
|
|
2917
|
+
currentUrl.toString() === nextUrl.toString() ||
|
|
2918
|
+
// Search params affect all loaders
|
|
2919
|
+
currentUrl.search !== nextUrl.search;
|
|
2920
|
+
|
|
2906
2921
|
// Pick navigation matches that are net-new or qualify for revalidation
|
|
2907
2922
|
let boundaryId = pendingError ? Object.keys(pendingError)[0] : undefined;
|
|
2908
2923
|
let boundaryMatches = getLoaderMatchesUntilBoundary(matches, boundaryId);
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2924
|
+
|
|
2925
|
+
let navigationMatches = boundaryMatches.filter((match, index) => {
|
|
2926
|
+
if (match.route.loader == null) {
|
|
2927
|
+
return false;
|
|
2928
|
+
}
|
|
2929
|
+
|
|
2930
|
+
// Always call the loader on new route instances and pending defer cancellations
|
|
2931
|
+
if (
|
|
2932
|
+
isNewLoader(state.loaderData, state.matches[index], match) ||
|
|
2933
|
+
cancelledDeferredRoutes.some((id) => id === match.route.id)
|
|
2934
|
+
) {
|
|
2935
|
+
return true;
|
|
2936
|
+
}
|
|
2937
|
+
|
|
2938
|
+
// This is the default implementation for when we revalidate. If the route
|
|
2939
|
+
// provides it's own implementation, then we give them full control but
|
|
2940
|
+
// provide this value so they can leverage it if needed after they check
|
|
2941
|
+
// their own specific use cases
|
|
2942
|
+
let currentRouteMatch = state.matches[index];
|
|
2943
|
+
let nextRouteMatch = match;
|
|
2944
|
+
|
|
2945
|
+
return shouldRevalidateLoader(match, {
|
|
2946
|
+
currentUrl,
|
|
2947
|
+
currentParams: currentRouteMatch.params,
|
|
2948
|
+
nextUrl,
|
|
2949
|
+
nextParams: nextRouteMatch.params,
|
|
2950
|
+
...submission,
|
|
2951
|
+
actionResult,
|
|
2952
|
+
defaultShouldRevalidate:
|
|
2953
|
+
defaultShouldRevalidate ||
|
|
2954
|
+
isNewRouteInstance(currentRouteMatch, nextRouteMatch),
|
|
2955
|
+
});
|
|
2956
|
+
});
|
|
2926
2957
|
|
|
2927
2958
|
// Pick fetcher.loads that need to be revalidated
|
|
2928
2959
|
let revalidatingFetchers: RevalidatingFetcher[] = [];
|
|
2929
2960
|
fetchLoadMatches &&
|
|
2930
|
-
fetchLoadMatches.forEach((
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2961
|
+
fetchLoadMatches.forEach((f, key) => {
|
|
2962
|
+
if (!matches.some((m) => m.route.id === f.routeId)) {
|
|
2963
|
+
// This fetcher is not going to be present in the subsequent render so
|
|
2964
|
+
// there's no need to revalidate it
|
|
2965
|
+
return;
|
|
2966
|
+
} else if (cancelledFetcherLoads.includes(key)) {
|
|
2967
|
+
// This fetcher was cancelled from a prior action submission - force reload
|
|
2968
|
+
revalidatingFetchers.push({ key, ...f });
|
|
2969
|
+
} else {
|
|
2970
|
+
// Revalidating fetchers are decoupled from the route matches since they
|
|
2971
|
+
// hit a static href, so they _always_ check shouldRevalidate and the
|
|
2972
|
+
// default is strictly if a revalidation is explicitly required (action
|
|
2973
|
+
// submissions, useRevalidator, X-Remix-Revalidate).
|
|
2974
|
+
let shouldRevalidate = shouldRevalidateLoader(f.match, {
|
|
2975
|
+
currentUrl,
|
|
2976
|
+
currentParams: state.matches[state.matches.length - 1].params,
|
|
2977
|
+
nextUrl,
|
|
2978
|
+
nextParams: matches[matches.length - 1].params,
|
|
2979
|
+
...submission,
|
|
2980
|
+
actionResult,
|
|
2981
|
+
defaultShouldRevalidate,
|
|
2982
|
+
});
|
|
2945
2983
|
if (shouldRevalidate) {
|
|
2946
|
-
revalidatingFetchers.push(
|
|
2984
|
+
revalidatingFetchers.push({ key, ...f });
|
|
2947
2985
|
}
|
|
2948
2986
|
}
|
|
2949
2987
|
});
|
|
@@ -2980,58 +3018,24 @@ function isNewRouteInstance(
|
|
|
2980
3018
|
currentMatch.pathname !== match.pathname ||
|
|
2981
3019
|
// splat param changed, which is not present in match.path
|
|
2982
3020
|
// e.g. /files/images/avatar.jpg -> files/finances.xls
|
|
2983
|
-
(currentPath &&
|
|
3021
|
+
(currentPath != null &&
|
|
2984
3022
|
currentPath.endsWith("*") &&
|
|
2985
3023
|
currentMatch.params["*"] !== match.params["*"])
|
|
2986
3024
|
);
|
|
2987
3025
|
}
|
|
2988
3026
|
|
|
2989
3027
|
function shouldRevalidateLoader(
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
currentMatch: AgnosticDataRouteMatch,
|
|
2993
|
-
submission: Submission | undefined,
|
|
2994
|
-
location: string | Location,
|
|
2995
|
-
match: AgnosticDataRouteMatch,
|
|
2996
|
-
isRevalidationRequired: boolean,
|
|
2997
|
-
actionResult: DataResult | undefined
|
|
3028
|
+
loaderMatch: AgnosticDataRouteMatch,
|
|
3029
|
+
arg: Parameters<ShouldRevalidateFunction>[0]
|
|
2998
3030
|
) {
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
let nextUrl = history.createURL(location);
|
|
3002
|
-
let nextParams = match.params;
|
|
3003
|
-
|
|
3004
|
-
// This is the default implementation as to when we revalidate. If the route
|
|
3005
|
-
// provides it's own implementation, then we give them full control but
|
|
3006
|
-
// provide this value so they can leverage it if needed after they check
|
|
3007
|
-
// their own specific use cases
|
|
3008
|
-
// Note that fetchers always provide the same current/next locations so the
|
|
3009
|
-
// URL-based checks here don't apply to fetcher shouldRevalidate calls
|
|
3010
|
-
let defaultShouldRevalidate =
|
|
3011
|
-
isNewRouteInstance(currentMatch, match) ||
|
|
3012
|
-
// Clicked the same link, resubmitted a GET form
|
|
3013
|
-
currentUrl.toString() === nextUrl.toString() ||
|
|
3014
|
-
// Search params affect all loaders
|
|
3015
|
-
currentUrl.search !== nextUrl.search ||
|
|
3016
|
-
// Forced revalidation due to submission, useRevalidate, or X-Remix-Revalidate
|
|
3017
|
-
isRevalidationRequired;
|
|
3018
|
-
|
|
3019
|
-
if (match.route.shouldRevalidate) {
|
|
3020
|
-
let routeChoice = match.route.shouldRevalidate({
|
|
3021
|
-
currentUrl,
|
|
3022
|
-
currentParams,
|
|
3023
|
-
nextUrl,
|
|
3024
|
-
nextParams,
|
|
3025
|
-
...submission,
|
|
3026
|
-
actionResult,
|
|
3027
|
-
defaultShouldRevalidate,
|
|
3028
|
-
});
|
|
3031
|
+
if (loaderMatch.route.shouldRevalidate) {
|
|
3032
|
+
let routeChoice = loaderMatch.route.shouldRevalidate(arg);
|
|
3029
3033
|
if (typeof routeChoice === "boolean") {
|
|
3030
3034
|
return routeChoice;
|
|
3031
3035
|
}
|
|
3032
3036
|
}
|
|
3033
3037
|
|
|
3034
|
-
return defaultShouldRevalidate;
|
|
3038
|
+
return arg.defaultShouldRevalidate;
|
|
3035
3039
|
}
|
|
3036
3040
|
|
|
3037
3041
|
async function callLoaderOrAction(
|
|
@@ -3222,12 +3226,8 @@ function convertFormDataToSearchParams(formData: FormData): URLSearchParams {
|
|
|
3222
3226
|
let searchParams = new URLSearchParams();
|
|
3223
3227
|
|
|
3224
3228
|
for (let [key, value] of formData.entries()) {
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
'File inputs are not supported with encType "application/x-www-form-urlencoded", ' +
|
|
3228
|
-
'please use "multipart/form-data" instead.'
|
|
3229
|
-
);
|
|
3230
|
-
searchParams.append(key, value);
|
|
3229
|
+
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#converting-an-entry-list-to-a-list-of-name-value-pairs
|
|
3230
|
+
searchParams.append(key, value instanceof File ? value.name : value);
|
|
3231
3231
|
}
|
|
3232
3232
|
|
|
3233
3233
|
return searchParams;
|
|
@@ -3355,7 +3355,7 @@ function processLoaderData(
|
|
|
3355
3355
|
|
|
3356
3356
|
// Process results from our revalidating fetchers
|
|
3357
3357
|
for (let index = 0; index < revalidatingFetchers.length; index++) {
|
|
3358
|
-
let
|
|
3358
|
+
let { key, match } = revalidatingFetchers[index];
|
|
3359
3359
|
invariant(
|
|
3360
3360
|
fetcherResults !== undefined && fetcherResults[index] !== undefined,
|
|
3361
3361
|
"Did not find corresponding fetcher result"
|
|
@@ -3490,8 +3490,6 @@ function getInternalRouterError(
|
|
|
3490
3490
|
`so there is no way to handle the request.`;
|
|
3491
3491
|
} else if (type === "defer-action") {
|
|
3492
3492
|
errorMessage = "defer() is not supported in actions";
|
|
3493
|
-
} else {
|
|
3494
|
-
errorMessage = "Cannot submit binary form data using GET";
|
|
3495
3493
|
}
|
|
3496
3494
|
} else if (status === 403) {
|
|
3497
3495
|
statusText = "Forbidden";
|
package/utils.ts
CHANGED
|
@@ -1192,6 +1192,11 @@ export class DeferredData {
|
|
|
1192
1192
|
{}
|
|
1193
1193
|
);
|
|
1194
1194
|
|
|
1195
|
+
if (this.done) {
|
|
1196
|
+
// All incoming values were resolved
|
|
1197
|
+
this.unlistenAbortSignal();
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1195
1200
|
this.init = responseInit;
|
|
1196
1201
|
}
|
|
1197
1202
|
|
|
@@ -1395,8 +1400,14 @@ export class ErrorResponse {
|
|
|
1395
1400
|
|
|
1396
1401
|
/**
|
|
1397
1402
|
* Check if the given error is an ErrorResponse generated from a 4xx/5xx
|
|
1398
|
-
* Response
|
|
1403
|
+
* Response thrown from an action/loader
|
|
1399
1404
|
*/
|
|
1400
|
-
export function isRouteErrorResponse(
|
|
1401
|
-
return
|
|
1405
|
+
export function isRouteErrorResponse(error: any): error is ErrorResponse {
|
|
1406
|
+
return (
|
|
1407
|
+
error != null &&
|
|
1408
|
+
typeof error.status === "number" &&
|
|
1409
|
+
typeof error.statusText === "string" &&
|
|
1410
|
+
typeof error.internal === "boolean" &&
|
|
1411
|
+
"data" in error
|
|
1412
|
+
);
|
|
1402
1413
|
}
|