@remix-run/router 1.6.3 → 1.7.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 +53 -0
- package/dist/history.d.ts +6 -6
- package/dist/router.cjs.js +378 -234
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.d.ts +69 -41
- package/dist/router.js +365 -229
- package/dist/router.js.map +1 -1
- package/dist/router.umd.js +378 -234
- 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 +54 -28
- package/package.json +1 -1
- package/router.ts +501 -277
- package/utils.ts +44 -20
package/dist/router.cjs.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @remix-run/router v1.
|
|
2
|
+
* @remix-run/router v1.7.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -567,6 +567,7 @@ let ResultType;
|
|
|
567
567
|
* In v7, active navigation/fetcher form methods are exposed in uppercase on the
|
|
568
568
|
* RouterState. This is to align with the normalization done via fetch().
|
|
569
569
|
*/
|
|
570
|
+
// Thanks https://github.com/sindresorhus/type-fest!
|
|
570
571
|
/**
|
|
571
572
|
* @private
|
|
572
573
|
* Internal interface to pass around for action submissions, not intended for
|
|
@@ -924,28 +925,22 @@ function generatePath(originalPath, params) {
|
|
|
924
925
|
|
|
925
926
|
// ensure `/` is added at the beginning if the path is absolute
|
|
926
927
|
const prefix = path.startsWith("/") ? "/" : "";
|
|
928
|
+
const stringify = p => p == null ? "" : typeof p === "string" ? p : String(p);
|
|
927
929
|
const segments = path.split(/\/+/).map((segment, index, array) => {
|
|
928
930
|
const isLastSegment = index === array.length - 1;
|
|
929
931
|
|
|
930
932
|
// only apply the splat if it's the last segment
|
|
931
933
|
if (isLastSegment && segment === "*") {
|
|
932
934
|
const star = "*";
|
|
933
|
-
const starParam = params[star];
|
|
934
|
-
|
|
935
935
|
// Apply the splat
|
|
936
|
-
return
|
|
936
|
+
return stringify(params[star]);
|
|
937
937
|
}
|
|
938
938
|
const keyMatch = segment.match(/^:(\w+)(\??)$/);
|
|
939
939
|
if (keyMatch) {
|
|
940
940
|
const [, key, optional] = keyMatch;
|
|
941
941
|
let param = params[key];
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
}
|
|
945
|
-
if (param == null) {
|
|
946
|
-
invariant(false, "Missing \":" + key + "\" param");
|
|
947
|
-
}
|
|
948
|
-
return param;
|
|
942
|
+
invariant(optional === "?" || param != null, "Missing \":" + key + "\" param");
|
|
943
|
+
return stringify(param);
|
|
949
944
|
}
|
|
950
945
|
|
|
951
946
|
// Remove any optional markers from optional static segments
|
|
@@ -1473,14 +1468,23 @@ function isRouteErrorResponse(error) {
|
|
|
1473
1468
|
/**
|
|
1474
1469
|
* Function signature for determining the current scroll position
|
|
1475
1470
|
*/
|
|
1471
|
+
// Allowed for any navigation or fetch
|
|
1472
|
+
// Only allowed for navigations
|
|
1473
|
+
// Only allowed for submission navigations
|
|
1476
1474
|
/**
|
|
1477
|
-
* Options for a navigate() call for a
|
|
1475
|
+
* Options for a navigate() call for a normal (non-submission) navigation
|
|
1478
1476
|
*/
|
|
1479
1477
|
/**
|
|
1480
|
-
* Options for a navigate() call for a
|
|
1478
|
+
* Options for a navigate() call for a submission navigation
|
|
1481
1479
|
*/
|
|
1482
1480
|
/**
|
|
1483
|
-
* Options to pass to navigate() for
|
|
1481
|
+
* Options to pass to navigate() for a navigation
|
|
1482
|
+
*/
|
|
1483
|
+
/**
|
|
1484
|
+
* Options for a fetch() load
|
|
1485
|
+
*/
|
|
1486
|
+
/**
|
|
1487
|
+
* Options for a fetch() submission
|
|
1484
1488
|
*/
|
|
1485
1489
|
/**
|
|
1486
1490
|
* Options to pass to fetch()
|
|
@@ -1515,7 +1519,9 @@ const IDLE_NAVIGATION = {
|
|
|
1515
1519
|
formMethod: undefined,
|
|
1516
1520
|
formAction: undefined,
|
|
1517
1521
|
formEncType: undefined,
|
|
1518
|
-
formData: undefined
|
|
1522
|
+
formData: undefined,
|
|
1523
|
+
json: undefined,
|
|
1524
|
+
text: undefined
|
|
1519
1525
|
};
|
|
1520
1526
|
const IDLE_FETCHER = {
|
|
1521
1527
|
state: "idle",
|
|
@@ -1523,7 +1529,9 @@ const IDLE_FETCHER = {
|
|
|
1523
1529
|
formMethod: undefined,
|
|
1524
1530
|
formAction: undefined,
|
|
1525
1531
|
formEncType: undefined,
|
|
1526
|
-
formData: undefined
|
|
1532
|
+
formData: undefined,
|
|
1533
|
+
json: undefined,
|
|
1534
|
+
text: undefined
|
|
1527
1535
|
};
|
|
1528
1536
|
const IDLE_BLOCKER = {
|
|
1529
1537
|
state: "unblocked",
|
|
@@ -1739,9 +1747,10 @@ function createRouter(init) {
|
|
|
1739
1747
|
init.history.go(delta);
|
|
1740
1748
|
},
|
|
1741
1749
|
reset() {
|
|
1742
|
-
|
|
1750
|
+
let blockers = new Map(state.blockers);
|
|
1751
|
+
blockers.set(blockerKey, IDLE_BLOCKER);
|
|
1743
1752
|
updateState({
|
|
1744
|
-
blockers
|
|
1753
|
+
blockers
|
|
1745
1754
|
});
|
|
1746
1755
|
}
|
|
1747
1756
|
});
|
|
@@ -1818,9 +1827,8 @@ function createRouter(init) {
|
|
|
1818
1827
|
|
|
1819
1828
|
// On a successful navigation we can assume we got through all blockers
|
|
1820
1829
|
// so we can start fresh
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
}
|
|
1830
|
+
let blockers = new Map();
|
|
1831
|
+
blockerFunctions.clear();
|
|
1824
1832
|
|
|
1825
1833
|
// Always respect the user flag. Otherwise don't reset on mutation
|
|
1826
1834
|
// submission navigations unless they redirect
|
|
@@ -1829,6 +1837,11 @@ function createRouter(init) {
|
|
|
1829
1837
|
dataRoutes = inFlightDataRoutes;
|
|
1830
1838
|
inFlightDataRoutes = undefined;
|
|
1831
1839
|
}
|
|
1840
|
+
if (isUninterruptedRevalidation) ; else if (pendingAction === exports.Action.Pop) ; else if (pendingAction === exports.Action.Push) {
|
|
1841
|
+
init.history.push(location, location.state);
|
|
1842
|
+
} else if (pendingAction === exports.Action.Replace) {
|
|
1843
|
+
init.history.replace(location, location.state);
|
|
1844
|
+
}
|
|
1832
1845
|
updateState(_extends({}, newState, {
|
|
1833
1846
|
// matches, errors, fetchers go through as-is
|
|
1834
1847
|
actionData,
|
|
@@ -1840,13 +1853,8 @@ function createRouter(init) {
|
|
|
1840
1853
|
revalidation: "idle",
|
|
1841
1854
|
restoreScrollPosition: getSavedScrollPosition(location, newState.matches || state.matches),
|
|
1842
1855
|
preventScrollReset,
|
|
1843
|
-
blockers
|
|
1856
|
+
blockers
|
|
1844
1857
|
}));
|
|
1845
|
-
if (isUninterruptedRevalidation) ; else if (pendingAction === exports.Action.Pop) ; else if (pendingAction === exports.Action.Push) {
|
|
1846
|
-
init.history.push(location, location.state);
|
|
1847
|
-
} else if (pendingAction === exports.Action.Replace) {
|
|
1848
|
-
init.history.replace(location, location.state);
|
|
1849
|
-
}
|
|
1850
1858
|
|
|
1851
1859
|
// Reset stateful navigation vars
|
|
1852
1860
|
pendingAction = exports.Action.Pop;
|
|
@@ -1912,9 +1920,10 @@ function createRouter(init) {
|
|
|
1912
1920
|
navigate(to, opts);
|
|
1913
1921
|
},
|
|
1914
1922
|
reset() {
|
|
1915
|
-
|
|
1923
|
+
let blockers = new Map(state.blockers);
|
|
1924
|
+
blockers.set(blockerKey, IDLE_BLOCKER);
|
|
1916
1925
|
updateState({
|
|
1917
|
-
blockers
|
|
1926
|
+
blockers
|
|
1918
1927
|
});
|
|
1919
1928
|
}
|
|
1920
1929
|
});
|
|
@@ -2040,11 +2049,7 @@ function createRouter(init) {
|
|
|
2040
2049
|
}
|
|
2041
2050
|
pendingActionData = actionOutput.pendingActionData;
|
|
2042
2051
|
pendingError = actionOutput.pendingActionError;
|
|
2043
|
-
|
|
2044
|
-
state: "loading",
|
|
2045
|
-
location
|
|
2046
|
-
}, opts.submission);
|
|
2047
|
-
loadingNavigation = navigation;
|
|
2052
|
+
loadingNavigation = getLoadingNavigation(location, opts.submission);
|
|
2048
2053
|
|
|
2049
2054
|
// Create a GET request for the loaders
|
|
2050
2055
|
request = new Request(request.url, {
|
|
@@ -2079,13 +2084,13 @@ function createRouter(init) {
|
|
|
2079
2084
|
// Call the action matched by the leaf route for this navigation and handle
|
|
2080
2085
|
// redirects/errors
|
|
2081
2086
|
async function handleAction(request, location, submission, matches, opts) {
|
|
2087
|
+
if (opts === void 0) {
|
|
2088
|
+
opts = {};
|
|
2089
|
+
}
|
|
2082
2090
|
interruptActiveLoads();
|
|
2083
2091
|
|
|
2084
2092
|
// Put us in a submitting state
|
|
2085
|
-
let navigation =
|
|
2086
|
-
state: "submitting",
|
|
2087
|
-
location
|
|
2088
|
-
}, submission);
|
|
2093
|
+
let navigation = getSubmittingNavigation(location, submission);
|
|
2089
2094
|
updateState({
|
|
2090
2095
|
navigation
|
|
2091
2096
|
});
|
|
@@ -2164,29 +2169,13 @@ function createRouter(init) {
|
|
|
2164
2169
|
// errors, etc.
|
|
2165
2170
|
async function handleLoaders(request, location, matches, overrideNavigation, submission, fetcherSubmission, replace, pendingActionData, pendingError) {
|
|
2166
2171
|
// Figure out the right navigation we want to use for data loading
|
|
2167
|
-
let loadingNavigation = overrideNavigation;
|
|
2168
|
-
if (!loadingNavigation) {
|
|
2169
|
-
let navigation = _extends({
|
|
2170
|
-
state: "loading",
|
|
2171
|
-
location,
|
|
2172
|
-
formMethod: undefined,
|
|
2173
|
-
formAction: undefined,
|
|
2174
|
-
formEncType: undefined,
|
|
2175
|
-
formData: undefined
|
|
2176
|
-
}, submission);
|
|
2177
|
-
loadingNavigation = navigation;
|
|
2178
|
-
}
|
|
2172
|
+
let loadingNavigation = overrideNavigation || getLoadingNavigation(location, submission);
|
|
2179
2173
|
|
|
2180
2174
|
// If this was a redirect from an action we don't have a "submission" but
|
|
2181
2175
|
// we have it on the loading navigation so use that if available
|
|
2182
|
-
let activeSubmission = submission || fetcherSubmission
|
|
2183
|
-
formMethod: loadingNavigation.formMethod,
|
|
2184
|
-
formAction: loadingNavigation.formAction,
|
|
2185
|
-
formData: loadingNavigation.formData,
|
|
2186
|
-
formEncType: loadingNavigation.formEncType
|
|
2187
|
-
} : undefined;
|
|
2176
|
+
let activeSubmission = submission || fetcherSubmission || getSubmissionFromNavigation(loadingNavigation);
|
|
2188
2177
|
let routesToUse = inFlightDataRoutes || dataRoutes;
|
|
2189
|
-
let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(init.history, state, matches, activeSubmission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, fetchLoadMatches, routesToUse, basename, pendingActionData, pendingError);
|
|
2178
|
+
let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(init.history, state, matches, activeSubmission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, pendingActionData, pendingError);
|
|
2190
2179
|
|
|
2191
2180
|
// Cancel pending deferreds for no-longer-matched routes or routes we're
|
|
2192
2181
|
// about to reload. Note that if this is an action reload we would have
|
|
@@ -2218,15 +2207,7 @@ function createRouter(init) {
|
|
|
2218
2207
|
if (!isUninterruptedRevalidation) {
|
|
2219
2208
|
revalidatingFetchers.forEach(rf => {
|
|
2220
2209
|
let fetcher = state.fetchers.get(rf.key);
|
|
2221
|
-
let revalidatingFetcher =
|
|
2222
|
-
state: "loading",
|
|
2223
|
-
data: fetcher && fetcher.data,
|
|
2224
|
-
formMethod: undefined,
|
|
2225
|
-
formAction: undefined,
|
|
2226
|
-
formEncType: undefined,
|
|
2227
|
-
formData: undefined,
|
|
2228
|
-
" _hasFetcherDoneAnything ": true
|
|
2229
|
-
};
|
|
2210
|
+
let revalidatingFetcher = getLoadingFetcher(undefined, fetcher ? fetcher.data : undefined);
|
|
2230
2211
|
state.fetchers.set(rf.key, revalidatingFetcher);
|
|
2231
2212
|
});
|
|
2232
2213
|
let actionData = pendingActionData || state.actionData;
|
|
@@ -2242,6 +2223,9 @@ function createRouter(init) {
|
|
|
2242
2223
|
}
|
|
2243
2224
|
pendingNavigationLoadId = ++incrementingLoadId;
|
|
2244
2225
|
revalidatingFetchers.forEach(rf => {
|
|
2226
|
+
if (fetchControllers.has(rf.key)) {
|
|
2227
|
+
abortFetcher(rf.key);
|
|
2228
|
+
}
|
|
2245
2229
|
if (rf.controller) {
|
|
2246
2230
|
// Fetchers use an independent AbortController so that aborting a fetcher
|
|
2247
2231
|
// (via deleteFetcher) does not abort the triggering navigation that
|
|
@@ -2333,8 +2317,13 @@ function createRouter(init) {
|
|
|
2333
2317
|
}
|
|
2334
2318
|
let {
|
|
2335
2319
|
path,
|
|
2336
|
-
submission
|
|
2320
|
+
submission,
|
|
2321
|
+
error
|
|
2337
2322
|
} = normalizeNavigateOptions(future.v7_normalizeFormMethod, true, normalizedPath, opts);
|
|
2323
|
+
if (error) {
|
|
2324
|
+
setFetcherError(key, routeId, error);
|
|
2325
|
+
return;
|
|
2326
|
+
}
|
|
2338
2327
|
let match = getTargetMatch(matches, path);
|
|
2339
2328
|
pendingPreventScrollReset = (opts && opts.preventScrollReset) === true;
|
|
2340
2329
|
if (submission && isMutationMethod(submission.formMethod)) {
|
|
@@ -2368,12 +2357,7 @@ function createRouter(init) {
|
|
|
2368
2357
|
|
|
2369
2358
|
// Put this fetcher into it's submitting state
|
|
2370
2359
|
let existingFetcher = state.fetchers.get(key);
|
|
2371
|
-
let fetcher =
|
|
2372
|
-
state: "submitting"
|
|
2373
|
-
}, submission, {
|
|
2374
|
-
data: existingFetcher && existingFetcher.data,
|
|
2375
|
-
" _hasFetcherDoneAnything ": true
|
|
2376
|
-
});
|
|
2360
|
+
let fetcher = getSubmittingFetcher(submission, existingFetcher);
|
|
2377
2361
|
state.fetchers.set(key, fetcher);
|
|
2378
2362
|
updateState({
|
|
2379
2363
|
fetchers: new Map(state.fetchers)
|
|
@@ -2395,12 +2379,7 @@ function createRouter(init) {
|
|
|
2395
2379
|
if (isRedirectResult(actionResult)) {
|
|
2396
2380
|
fetchControllers.delete(key);
|
|
2397
2381
|
fetchRedirectIds.add(key);
|
|
2398
|
-
let loadingFetcher =
|
|
2399
|
-
state: "loading"
|
|
2400
|
-
}, submission, {
|
|
2401
|
-
data: undefined,
|
|
2402
|
-
" _hasFetcherDoneAnything ": true
|
|
2403
|
-
});
|
|
2382
|
+
let loadingFetcher = getLoadingFetcher(submission);
|
|
2404
2383
|
state.fetchers.set(key, loadingFetcher);
|
|
2405
2384
|
updateState({
|
|
2406
2385
|
fetchers: new Map(state.fetchers)
|
|
@@ -2431,14 +2410,9 @@ function createRouter(init) {
|
|
|
2431
2410
|
invariant(matches, "Didn't find any matches after fetcher action");
|
|
2432
2411
|
let loadId = ++incrementingLoadId;
|
|
2433
2412
|
fetchReloadIds.set(key, loadId);
|
|
2434
|
-
let loadFetcher =
|
|
2435
|
-
state: "loading",
|
|
2436
|
-
data: actionResult.data
|
|
2437
|
-
}, submission, {
|
|
2438
|
-
" _hasFetcherDoneAnything ": true
|
|
2439
|
-
});
|
|
2413
|
+
let loadFetcher = getLoadingFetcher(submission, actionResult.data);
|
|
2440
2414
|
state.fetchers.set(key, loadFetcher);
|
|
2441
|
-
let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(init.history, state, matches, submission, nextLocation, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, fetchLoadMatches, routesToUse, basename, {
|
|
2415
|
+
let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(init.history, state, matches, submission, nextLocation, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, {
|
|
2442
2416
|
[match.route.id]: actionResult.data
|
|
2443
2417
|
}, undefined // No need to send through errors since we short circuit above
|
|
2444
2418
|
);
|
|
@@ -2449,16 +2423,11 @@ function createRouter(init) {
|
|
|
2449
2423
|
revalidatingFetchers.filter(rf => rf.key !== key).forEach(rf => {
|
|
2450
2424
|
let staleKey = rf.key;
|
|
2451
2425
|
let existingFetcher = state.fetchers.get(staleKey);
|
|
2452
|
-
let revalidatingFetcher =
|
|
2453
|
-
state: "loading",
|
|
2454
|
-
data: existingFetcher && existingFetcher.data,
|
|
2455
|
-
formMethod: undefined,
|
|
2456
|
-
formAction: undefined,
|
|
2457
|
-
formEncType: undefined,
|
|
2458
|
-
formData: undefined,
|
|
2459
|
-
" _hasFetcherDoneAnything ": true
|
|
2460
|
-
};
|
|
2426
|
+
let revalidatingFetcher = getLoadingFetcher(undefined, existingFetcher ? existingFetcher.data : undefined);
|
|
2461
2427
|
state.fetchers.set(staleKey, revalidatingFetcher);
|
|
2428
|
+
if (fetchControllers.has(staleKey)) {
|
|
2429
|
+
abortFetcher(staleKey);
|
|
2430
|
+
}
|
|
2462
2431
|
if (rf.controller) {
|
|
2463
2432
|
fetchControllers.set(staleKey, rf.controller);
|
|
2464
2433
|
}
|
|
@@ -2494,15 +2463,7 @@ function createRouter(init) {
|
|
|
2494
2463
|
// Since we let revalidations complete even if the submitting fetcher was
|
|
2495
2464
|
// deleted, only put it back to idle if it hasn't been deleted
|
|
2496
2465
|
if (state.fetchers.has(key)) {
|
|
2497
|
-
let doneFetcher =
|
|
2498
|
-
state: "idle",
|
|
2499
|
-
data: actionResult.data,
|
|
2500
|
-
formMethod: undefined,
|
|
2501
|
-
formAction: undefined,
|
|
2502
|
-
formEncType: undefined,
|
|
2503
|
-
formData: undefined,
|
|
2504
|
-
" _hasFetcherDoneAnything ": true
|
|
2505
|
-
};
|
|
2466
|
+
let doneFetcher = getDoneFetcher(actionResult.data);
|
|
2506
2467
|
state.fetchers.set(key, doneFetcher);
|
|
2507
2468
|
}
|
|
2508
2469
|
let didAbortFetchLoads = abortStaleFetchLoads(loadId);
|
|
@@ -2537,16 +2498,7 @@ function createRouter(init) {
|
|
|
2537
2498
|
async function handleFetcherLoader(key, routeId, path, match, matches, submission) {
|
|
2538
2499
|
let existingFetcher = state.fetchers.get(key);
|
|
2539
2500
|
// Put this fetcher into it's loading state
|
|
2540
|
-
let loadingFetcher =
|
|
2541
|
-
state: "loading",
|
|
2542
|
-
formMethod: undefined,
|
|
2543
|
-
formAction: undefined,
|
|
2544
|
-
formEncType: undefined,
|
|
2545
|
-
formData: undefined
|
|
2546
|
-
}, submission, {
|
|
2547
|
-
data: existingFetcher && existingFetcher.data,
|
|
2548
|
-
" _hasFetcherDoneAnything ": true
|
|
2549
|
-
});
|
|
2501
|
+
let loadingFetcher = getLoadingFetcher(submission, existingFetcher ? existingFetcher.data : undefined);
|
|
2550
2502
|
state.fetchers.set(key, loadingFetcher);
|
|
2551
2503
|
updateState({
|
|
2552
2504
|
fetchers: new Map(state.fetchers)
|
|
@@ -2600,15 +2552,7 @@ function createRouter(init) {
|
|
|
2600
2552
|
invariant(!isDeferredResult(result), "Unhandled fetcher deferred data");
|
|
2601
2553
|
|
|
2602
2554
|
// Put the fetcher back into an idle state
|
|
2603
|
-
let doneFetcher =
|
|
2604
|
-
state: "idle",
|
|
2605
|
-
data: result.data,
|
|
2606
|
-
formMethod: undefined,
|
|
2607
|
-
formAction: undefined,
|
|
2608
|
-
formEncType: undefined,
|
|
2609
|
-
formData: undefined,
|
|
2610
|
-
" _hasFetcherDoneAnything ": true
|
|
2611
|
-
};
|
|
2555
|
+
let doneFetcher = getDoneFetcher(result.data);
|
|
2612
2556
|
state.fetchers.set(key, doneFetcher);
|
|
2613
2557
|
updateState({
|
|
2614
2558
|
fetchers: new Map(state.fetchers)
|
|
@@ -2671,27 +2615,14 @@ function createRouter(init) {
|
|
|
2671
2615
|
|
|
2672
2616
|
// Use the incoming submission if provided, fallback on the active one in
|
|
2673
2617
|
// state.navigation
|
|
2674
|
-
let
|
|
2675
|
-
formMethod,
|
|
2676
|
-
formAction,
|
|
2677
|
-
formEncType,
|
|
2678
|
-
formData
|
|
2679
|
-
} = state.navigation;
|
|
2680
|
-
if (!submission && formMethod && formAction && formData && formEncType) {
|
|
2681
|
-
submission = {
|
|
2682
|
-
formMethod,
|
|
2683
|
-
formAction,
|
|
2684
|
-
formEncType,
|
|
2685
|
-
formData
|
|
2686
|
-
};
|
|
2687
|
-
}
|
|
2618
|
+
let activeSubmission = submission || getSubmissionFromNavigation(state.navigation);
|
|
2688
2619
|
|
|
2689
2620
|
// If this was a 307/308 submission we want to preserve the HTTP method and
|
|
2690
2621
|
// re-submit the GET/POST/PUT/PATCH/DELETE as a submission navigation to the
|
|
2691
2622
|
// redirected location
|
|
2692
|
-
if (redirectPreserveMethodStatusCodes.has(redirect.status) &&
|
|
2623
|
+
if (redirectPreserveMethodStatusCodes.has(redirect.status) && activeSubmission && isMutationMethod(activeSubmission.formMethod)) {
|
|
2693
2624
|
await startNavigation(redirectHistoryAction, redirectLocation, {
|
|
2694
|
-
submission: _extends({},
|
|
2625
|
+
submission: _extends({}, activeSubmission, {
|
|
2695
2626
|
formAction: redirect.location
|
|
2696
2627
|
}),
|
|
2697
2628
|
// Preserve this flag across redirects
|
|
@@ -2701,30 +2632,16 @@ function createRouter(init) {
|
|
|
2701
2632
|
// For a fetch action redirect, we kick off a new loading navigation
|
|
2702
2633
|
// without the fetcher submission, but we send it along for shouldRevalidate
|
|
2703
2634
|
await startNavigation(redirectHistoryAction, redirectLocation, {
|
|
2704
|
-
overrideNavigation:
|
|
2705
|
-
|
|
2706
|
-
location: redirectLocation,
|
|
2707
|
-
formMethod: undefined,
|
|
2708
|
-
formAction: undefined,
|
|
2709
|
-
formEncType: undefined,
|
|
2710
|
-
formData: undefined
|
|
2711
|
-
},
|
|
2712
|
-
fetcherSubmission: submission,
|
|
2635
|
+
overrideNavigation: getLoadingNavigation(redirectLocation),
|
|
2636
|
+
fetcherSubmission: activeSubmission,
|
|
2713
2637
|
// Preserve this flag across redirects
|
|
2714
2638
|
preventScrollReset: pendingPreventScrollReset
|
|
2715
2639
|
});
|
|
2716
2640
|
} else {
|
|
2717
|
-
//
|
|
2718
|
-
|
|
2641
|
+
// If we have a submission, we will preserve it through the redirect navigation
|
|
2642
|
+
let overrideNavigation = getLoadingNavigation(redirectLocation, activeSubmission);
|
|
2719
2643
|
await startNavigation(redirectHistoryAction, redirectLocation, {
|
|
2720
|
-
overrideNavigation
|
|
2721
|
-
state: "loading",
|
|
2722
|
-
location: redirectLocation,
|
|
2723
|
-
formMethod: submission ? submission.formMethod : undefined,
|
|
2724
|
-
formAction: submission ? submission.formAction : undefined,
|
|
2725
|
-
formEncType: submission ? submission.formEncType : undefined,
|
|
2726
|
-
formData: submission ? submission.formData : undefined
|
|
2727
|
-
},
|
|
2644
|
+
overrideNavigation,
|
|
2728
2645
|
// Preserve this flag across redirects
|
|
2729
2646
|
preventScrollReset: pendingPreventScrollReset
|
|
2730
2647
|
});
|
|
@@ -2804,15 +2721,7 @@ function createRouter(init) {
|
|
|
2804
2721
|
function markFetchersDone(keys) {
|
|
2805
2722
|
for (let key of keys) {
|
|
2806
2723
|
let fetcher = getFetcher(key);
|
|
2807
|
-
let doneFetcher =
|
|
2808
|
-
state: "idle",
|
|
2809
|
-
data: fetcher.data,
|
|
2810
|
-
formMethod: undefined,
|
|
2811
|
-
formAction: undefined,
|
|
2812
|
-
formEncType: undefined,
|
|
2813
|
-
formData: undefined,
|
|
2814
|
-
" _hasFetcherDoneAnything ": true
|
|
2815
|
-
};
|
|
2724
|
+
let doneFetcher = getDoneFetcher(fetcher.data);
|
|
2816
2725
|
state.fetchers.set(key, doneFetcher);
|
|
2817
2726
|
}
|
|
2818
2727
|
}
|
|
@@ -2866,9 +2775,10 @@ function createRouter(init) {
|
|
|
2866
2775
|
// Poor mans state machine :)
|
|
2867
2776
|
// https://mermaid.live/edit#pako:eNqVkc9OwzAMxl8l8nnjAYrEtDIOHEBIgwvKJTReGy3_lDpIqO27k6awMG0XcrLlnz87nwdonESogKXXBuE79rq75XZO3-yHds0RJVuv70YrPlUrCEe2HfrORS3rubqZfuhtpg5C9wk5tZ4VKcRUq88q9Z8RS0-48cE1iHJkL0ugbHuFLus9L6spZy8nX9MP2CNdomVaposqu3fGayT8T8-jJQwhepo_UtpgBQaDEUom04dZhAN1aJBDlUKJBxE1ceB2Smj0Mln-IBW5AFU2dwUiktt_2Qaq2dBfaKdEup85UV7Yd-dKjlnkabl2Pvr0DTkTreM
|
|
2868
2777
|
invariant(blocker.state === "unblocked" && newBlocker.state === "blocked" || blocker.state === "blocked" && newBlocker.state === "blocked" || blocker.state === "blocked" && newBlocker.state === "proceeding" || blocker.state === "blocked" && newBlocker.state === "unblocked" || blocker.state === "proceeding" && newBlocker.state === "unblocked", "Invalid blocker state transition: " + blocker.state + " -> " + newBlocker.state);
|
|
2869
|
-
state.blockers
|
|
2778
|
+
let blockers = new Map(state.blockers);
|
|
2779
|
+
blockers.set(key, newBlocker);
|
|
2870
2780
|
updateState({
|
|
2871
|
-
blockers
|
|
2781
|
+
blockers
|
|
2872
2782
|
});
|
|
2873
2783
|
}
|
|
2874
2784
|
function shouldBlockNavigation(_ref2) {
|
|
@@ -2925,7 +2835,7 @@ function createRouter(init) {
|
|
|
2925
2835
|
function enableScrollRestoration(positions, getPosition, getKey) {
|
|
2926
2836
|
savedScrollPositions = positions;
|
|
2927
2837
|
getScrollPosition = getPosition;
|
|
2928
|
-
getScrollRestorationKey = getKey ||
|
|
2838
|
+
getScrollRestorationKey = getKey || null;
|
|
2929
2839
|
|
|
2930
2840
|
// Perform initial hydration scroll restoration, since we miss the boat on
|
|
2931
2841
|
// the initial updateState() because we've not yet rendered <ScrollRestoration/>
|
|
@@ -2945,17 +2855,22 @@ function createRouter(init) {
|
|
|
2945
2855
|
getScrollRestorationKey = null;
|
|
2946
2856
|
};
|
|
2947
2857
|
}
|
|
2858
|
+
function getScrollKey(location, matches) {
|
|
2859
|
+
if (getScrollRestorationKey) {
|
|
2860
|
+
let key = getScrollRestorationKey(location, matches.map(m => createUseMatchesMatch(m, state.loaderData)));
|
|
2861
|
+
return key || location.key;
|
|
2862
|
+
}
|
|
2863
|
+
return location.key;
|
|
2864
|
+
}
|
|
2948
2865
|
function saveScrollPosition(location, matches) {
|
|
2949
|
-
if (savedScrollPositions &&
|
|
2950
|
-
let
|
|
2951
|
-
let key = getScrollRestorationKey(location, userMatches) || location.key;
|
|
2866
|
+
if (savedScrollPositions && getScrollPosition) {
|
|
2867
|
+
let key = getScrollKey(location, matches);
|
|
2952
2868
|
savedScrollPositions[key] = getScrollPosition();
|
|
2953
2869
|
}
|
|
2954
2870
|
}
|
|
2955
2871
|
function getSavedScrollPosition(location, matches) {
|
|
2956
|
-
if (savedScrollPositions
|
|
2957
|
-
let
|
|
2958
|
-
let key = getScrollRestorationKey(location, userMatches) || location.key;
|
|
2872
|
+
if (savedScrollPositions) {
|
|
2873
|
+
let key = getScrollKey(location, matches);
|
|
2959
2874
|
let y = savedScrollPositions[key];
|
|
2960
2875
|
if (typeof y === "number") {
|
|
2961
2876
|
return y;
|
|
@@ -3238,7 +3153,11 @@ function createStaticHandler(routes, opts) {
|
|
|
3238
3153
|
error
|
|
3239
3154
|
};
|
|
3240
3155
|
} else {
|
|
3241
|
-
result = await callLoaderOrAction("action", request, actionMatch, matches, manifest, mapRouteProperties, basename,
|
|
3156
|
+
result = await callLoaderOrAction("action", request, actionMatch, matches, manifest, mapRouteProperties, basename, {
|
|
3157
|
+
isStaticRequest: true,
|
|
3158
|
+
isRouteRequest,
|
|
3159
|
+
requestContext
|
|
3160
|
+
});
|
|
3242
3161
|
if (request.signal.aborted) {
|
|
3243
3162
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
3244
3163
|
throw new Error(method + "() call aborted");
|
|
@@ -3353,7 +3272,11 @@ function createStaticHandler(routes, opts) {
|
|
|
3353
3272
|
activeDeferreds: null
|
|
3354
3273
|
};
|
|
3355
3274
|
}
|
|
3356
|
-
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, manifest, mapRouteProperties, basename,
|
|
3275
|
+
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, manifest, mapRouteProperties, basename, {
|
|
3276
|
+
isStaticRequest: true,
|
|
3277
|
+
isRouteRequest,
|
|
3278
|
+
requestContext
|
|
3279
|
+
}))]);
|
|
3357
3280
|
if (request.signal.aborted) {
|
|
3358
3281
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
3359
3282
|
throw new Error(method + "() call aborted");
|
|
@@ -3402,7 +3325,7 @@ function getStaticContextFromError(routes, context, error) {
|
|
|
3402
3325
|
return newContext;
|
|
3403
3326
|
}
|
|
3404
3327
|
function isSubmissionNavigation(opts) {
|
|
3405
|
-
return opts != null && "formData" in opts;
|
|
3328
|
+
return opts != null && ("formData" in opts && opts.formData != null || "body" in opts && opts.body !== undefined);
|
|
3406
3329
|
}
|
|
3407
3330
|
function normalizeTo(location, matches, basename, prependBasename, to, fromRouteId, relative) {
|
|
3408
3331
|
let contextualMatches;
|
|
@@ -3468,28 +3391,103 @@ function normalizeNavigateOptions(normalizeFormMethod, isFetcher, path, opts) {
|
|
|
3468
3391
|
})
|
|
3469
3392
|
};
|
|
3470
3393
|
}
|
|
3394
|
+
let getInvalidBodyError = () => ({
|
|
3395
|
+
path,
|
|
3396
|
+
error: getInternalRouterError(400, {
|
|
3397
|
+
type: "invalid-body"
|
|
3398
|
+
})
|
|
3399
|
+
});
|
|
3471
3400
|
|
|
3472
3401
|
// Create a Submission on non-GET navigations
|
|
3473
|
-
let
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3402
|
+
let rawFormMethod = opts.formMethod || "get";
|
|
3403
|
+
let formMethod = normalizeFormMethod ? rawFormMethod.toUpperCase() : rawFormMethod.toLowerCase();
|
|
3404
|
+
let formAction = stripHashFromPath(path);
|
|
3405
|
+
if (opts.body !== undefined) {
|
|
3406
|
+
if (opts.formEncType === "text/plain") {
|
|
3407
|
+
// text only support POST/PUT/PATCH/DELETE submissions
|
|
3408
|
+
if (!isMutationMethod(formMethod)) {
|
|
3409
|
+
return getInvalidBodyError();
|
|
3410
|
+
}
|
|
3411
|
+
let text = typeof opts.body === "string" ? opts.body : opts.body instanceof FormData || opts.body instanceof URLSearchParams ?
|
|
3412
|
+
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#plain-text-form-data
|
|
3413
|
+
Array.from(opts.body.entries()).reduce((acc, _ref3) => {
|
|
3414
|
+
let [name, value] = _ref3;
|
|
3415
|
+
return "" + acc + name + "=" + value + "\n";
|
|
3416
|
+
}, "") : String(opts.body);
|
|
3483
3417
|
return {
|
|
3484
3418
|
path,
|
|
3485
|
-
submission
|
|
3419
|
+
submission: {
|
|
3420
|
+
formMethod,
|
|
3421
|
+
formAction,
|
|
3422
|
+
formEncType: opts.formEncType,
|
|
3423
|
+
formData: undefined,
|
|
3424
|
+
json: undefined,
|
|
3425
|
+
text
|
|
3426
|
+
}
|
|
3486
3427
|
};
|
|
3428
|
+
} else if (opts.formEncType === "application/json") {
|
|
3429
|
+
// json only supports POST/PUT/PATCH/DELETE submissions
|
|
3430
|
+
if (!isMutationMethod(formMethod)) {
|
|
3431
|
+
return getInvalidBodyError();
|
|
3432
|
+
}
|
|
3433
|
+
try {
|
|
3434
|
+
let json = typeof opts.body === "string" ? JSON.parse(opts.body) : opts.body;
|
|
3435
|
+
return {
|
|
3436
|
+
path,
|
|
3437
|
+
submission: {
|
|
3438
|
+
formMethod,
|
|
3439
|
+
formAction,
|
|
3440
|
+
formEncType: opts.formEncType,
|
|
3441
|
+
formData: undefined,
|
|
3442
|
+
json,
|
|
3443
|
+
text: undefined
|
|
3444
|
+
}
|
|
3445
|
+
};
|
|
3446
|
+
} catch (e) {
|
|
3447
|
+
return getInvalidBodyError();
|
|
3448
|
+
}
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3451
|
+
invariant(typeof FormData === "function", "FormData is not available in this environment");
|
|
3452
|
+
let searchParams;
|
|
3453
|
+
let formData;
|
|
3454
|
+
if (opts.formData) {
|
|
3455
|
+
searchParams = convertFormDataToSearchParams(opts.formData);
|
|
3456
|
+
formData = opts.formData;
|
|
3457
|
+
} else if (opts.body instanceof FormData) {
|
|
3458
|
+
searchParams = convertFormDataToSearchParams(opts.body);
|
|
3459
|
+
formData = opts.body;
|
|
3460
|
+
} else if (opts.body instanceof URLSearchParams) {
|
|
3461
|
+
searchParams = opts.body;
|
|
3462
|
+
formData = convertSearchParamsToFormData(searchParams);
|
|
3463
|
+
} else if (opts.body == null) {
|
|
3464
|
+
searchParams = new URLSearchParams();
|
|
3465
|
+
formData = new FormData();
|
|
3466
|
+
} else {
|
|
3467
|
+
try {
|
|
3468
|
+
searchParams = new URLSearchParams(opts.body);
|
|
3469
|
+
formData = convertSearchParamsToFormData(searchParams);
|
|
3470
|
+
} catch (e) {
|
|
3471
|
+
return getInvalidBodyError();
|
|
3487
3472
|
}
|
|
3488
3473
|
}
|
|
3474
|
+
let submission = {
|
|
3475
|
+
formMethod,
|
|
3476
|
+
formAction,
|
|
3477
|
+
formEncType: opts && opts.formEncType || "application/x-www-form-urlencoded",
|
|
3478
|
+
formData,
|
|
3479
|
+
json: undefined,
|
|
3480
|
+
text: undefined
|
|
3481
|
+
};
|
|
3482
|
+
if (isMutationMethod(submission.formMethod)) {
|
|
3483
|
+
return {
|
|
3484
|
+
path,
|
|
3485
|
+
submission
|
|
3486
|
+
};
|
|
3487
|
+
}
|
|
3489
3488
|
|
|
3490
3489
|
// Flatten submission onto URLSearchParams for GET submissions
|
|
3491
3490
|
let parsedPath = parsePath(path);
|
|
3492
|
-
let searchParams = convertFormDataToSearchParams(opts.formData);
|
|
3493
3491
|
// On GET navigation submissions we can drop the ?index param from the
|
|
3494
3492
|
// resulting location since all loaders will run. But fetcher GET submissions
|
|
3495
3493
|
// only run a single loader so we need to preserve any incoming ?index params
|
|
@@ -3515,7 +3513,7 @@ function getLoaderMatchesUntilBoundary(matches, boundaryId) {
|
|
|
3515
3513
|
}
|
|
3516
3514
|
return boundaryMatches;
|
|
3517
3515
|
}
|
|
3518
|
-
function getMatchesToLoad(history, state, matches, submission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, fetchLoadMatches, routesToUse, basename, pendingActionData, pendingError) {
|
|
3516
|
+
function getMatchesToLoad(history, state, matches, submission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, pendingActionData, pendingError) {
|
|
3519
3517
|
let actionResult = pendingError ? Object.values(pendingError)[0] : pendingActionData ? Object.values(pendingActionData)[0] : undefined;
|
|
3520
3518
|
let currentUrl = history.createURL(state.location);
|
|
3521
3519
|
let nextUrl = history.createURL(location);
|
|
@@ -3582,31 +3580,30 @@ function getMatchesToLoad(history, state, matches, submission, location, isReval
|
|
|
3582
3580
|
});
|
|
3583
3581
|
return;
|
|
3584
3582
|
}
|
|
3585
|
-
let fetcherMatch = getTargetMatch(fetcherMatches, f.path);
|
|
3586
|
-
if (cancelledFetcherLoads.includes(key)) {
|
|
3587
|
-
revalidatingFetchers.push({
|
|
3588
|
-
key,
|
|
3589
|
-
routeId: f.routeId,
|
|
3590
|
-
path: f.path,
|
|
3591
|
-
matches: fetcherMatches,
|
|
3592
|
-
match: fetcherMatch,
|
|
3593
|
-
controller: new AbortController()
|
|
3594
|
-
});
|
|
3595
|
-
return;
|
|
3596
|
-
}
|
|
3597
3583
|
|
|
3598
3584
|
// Revalidating fetchers are decoupled from the route matches since they
|
|
3599
|
-
//
|
|
3600
|
-
//
|
|
3601
|
-
//
|
|
3602
|
-
|
|
3585
|
+
// load from a static href. They only set `defaultShouldRevalidate` on
|
|
3586
|
+
// explicit revalidation due to submission, useRevalidator, or X-Remix-Revalidate
|
|
3587
|
+
//
|
|
3588
|
+
// They automatically revalidate without even calling shouldRevalidate if:
|
|
3589
|
+
// - They were cancelled
|
|
3590
|
+
// - They're in the middle of their first load and therefore this is still
|
|
3591
|
+
// an initial load and not a revalidation
|
|
3592
|
+
//
|
|
3593
|
+
// If neither of those is true, then they _always_ check shouldRevalidate
|
|
3594
|
+
let fetcher = state.fetchers.get(key);
|
|
3595
|
+
let isPerformingInitialLoad = fetcher && fetcher.state !== "idle" && fetcher.data === undefined &&
|
|
3596
|
+
// If a fetcher.load redirected then it'll be "loading" without any data
|
|
3597
|
+
// so ensure we're not processing the redirect from this fetcher
|
|
3598
|
+
!fetchRedirectIds.has(key);
|
|
3599
|
+
let fetcherMatch = getTargetMatch(fetcherMatches, f.path);
|
|
3600
|
+
let shouldRevalidate = cancelledFetcherLoads.includes(key) || isPerformingInitialLoad || shouldRevalidateLoader(fetcherMatch, _extends({
|
|
3603
3601
|
currentUrl,
|
|
3604
3602
|
currentParams: state.matches[state.matches.length - 1].params,
|
|
3605
3603
|
nextUrl,
|
|
3606
3604
|
nextParams: matches[matches.length - 1].params
|
|
3607
3605
|
}, submission, {
|
|
3608
3606
|
actionResult,
|
|
3609
|
-
// Forced revalidation due to submission, useRevalidator, or X-Remix-Revalidate
|
|
3610
3607
|
defaultShouldRevalidate: isRevalidationRequired
|
|
3611
3608
|
}));
|
|
3612
3609
|
if (shouldRevalidate) {
|
|
@@ -3708,12 +3705,9 @@ async function loadLazyRouteModule(route, mapRouteProperties, manifest) {
|
|
|
3708
3705
|
lazy: undefined
|
|
3709
3706
|
}));
|
|
3710
3707
|
}
|
|
3711
|
-
async function callLoaderOrAction(type, request, match, matches, manifest, mapRouteProperties, basename,
|
|
3712
|
-
if (
|
|
3713
|
-
|
|
3714
|
-
}
|
|
3715
|
-
if (isRouteRequest === void 0) {
|
|
3716
|
-
isRouteRequest = false;
|
|
3708
|
+
async function callLoaderOrAction(type, request, match, matches, manifest, mapRouteProperties, basename, opts) {
|
|
3709
|
+
if (opts === void 0) {
|
|
3710
|
+
opts = {};
|
|
3717
3711
|
}
|
|
3718
3712
|
let resultType;
|
|
3719
3713
|
let result;
|
|
@@ -3727,7 +3721,7 @@ async function callLoaderOrAction(type, request, match, matches, manifest, mapRo
|
|
|
3727
3721
|
return Promise.race([handler({
|
|
3728
3722
|
request,
|
|
3729
3723
|
params: match.params,
|
|
3730
|
-
context: requestContext
|
|
3724
|
+
context: opts.requestContext
|
|
3731
3725
|
}), abortPromise]);
|
|
3732
3726
|
};
|
|
3733
3727
|
try {
|
|
@@ -3792,7 +3786,7 @@ async function callLoaderOrAction(type, request, match, matches, manifest, mapRo
|
|
|
3792
3786
|
// Support relative routing in internal redirects
|
|
3793
3787
|
if (!ABSOLUTE_URL_REGEX.test(location)) {
|
|
3794
3788
|
location = normalizeTo(new URL(request.url), matches.slice(0, matches.indexOf(match) + 1), basename, true, location);
|
|
3795
|
-
} else if (!isStaticRequest) {
|
|
3789
|
+
} else if (!opts.isStaticRequest) {
|
|
3796
3790
|
// Strip off the protocol+origin for same-origin + same-basename absolute
|
|
3797
3791
|
// redirects. If this is a static request, we can let it go back to the
|
|
3798
3792
|
// browser as-is
|
|
@@ -3808,7 +3802,7 @@ async function callLoaderOrAction(type, request, match, matches, manifest, mapRo
|
|
|
3808
3802
|
// Instead, throw the Response and let the server handle it with an HTTP
|
|
3809
3803
|
// redirect. We also update the Location header in place in this flow so
|
|
3810
3804
|
// basename and relative routing is taken into account
|
|
3811
|
-
if (isStaticRequest) {
|
|
3805
|
+
if (opts.isStaticRequest) {
|
|
3812
3806
|
result.headers.set("Location", location);
|
|
3813
3807
|
throw result;
|
|
3814
3808
|
}
|
|
@@ -3823,7 +3817,7 @@ async function callLoaderOrAction(type, request, match, matches, manifest, mapRo
|
|
|
3823
3817
|
// For SSR single-route requests, we want to hand Responses back directly
|
|
3824
3818
|
// without unwrapping. We do this with the QueryRouteResponse wrapper
|
|
3825
3819
|
// interface so we can know whether it was returned or thrown
|
|
3826
|
-
if (isRouteRequest) {
|
|
3820
|
+
if (opts.isRouteRequest) {
|
|
3827
3821
|
// eslint-disable-next-line no-throw-literal
|
|
3828
3822
|
throw {
|
|
3829
3823
|
type: resultType || ResultType.data,
|
|
@@ -3885,27 +3879,45 @@ function createClientSideRequest(history, location, signal, submission) {
|
|
|
3885
3879
|
if (submission && isMutationMethod(submission.formMethod)) {
|
|
3886
3880
|
let {
|
|
3887
3881
|
formMethod,
|
|
3888
|
-
formEncType
|
|
3889
|
-
formData
|
|
3882
|
+
formEncType
|
|
3890
3883
|
} = submission;
|
|
3891
3884
|
// Didn't think we needed this but it turns out unlike other methods, patch
|
|
3892
3885
|
// won't be properly normalized to uppercase and results in a 405 error.
|
|
3893
3886
|
// See: https://fetch.spec.whatwg.org/#concept-method
|
|
3894
3887
|
init.method = formMethod.toUpperCase();
|
|
3895
|
-
|
|
3888
|
+
if (formEncType === "application/json") {
|
|
3889
|
+
init.headers = new Headers({
|
|
3890
|
+
"Content-Type": formEncType
|
|
3891
|
+
});
|
|
3892
|
+
init.body = JSON.stringify(submission.json);
|
|
3893
|
+
} else if (formEncType === "text/plain") {
|
|
3894
|
+
// Content-Type is inferred (https://fetch.spec.whatwg.org/#dom-request)
|
|
3895
|
+
init.body = submission.text;
|
|
3896
|
+
} else if (formEncType === "application/x-www-form-urlencoded" && submission.formData) {
|
|
3897
|
+
// Content-Type is inferred (https://fetch.spec.whatwg.org/#dom-request)
|
|
3898
|
+
init.body = convertFormDataToSearchParams(submission.formData);
|
|
3899
|
+
} else {
|
|
3900
|
+
// Content-Type is inferred (https://fetch.spec.whatwg.org/#dom-request)
|
|
3901
|
+
init.body = submission.formData;
|
|
3902
|
+
}
|
|
3896
3903
|
}
|
|
3897
|
-
|
|
3898
|
-
// Content-Type is inferred (https://fetch.spec.whatwg.org/#dom-request)
|
|
3899
3904
|
return new Request(url, init);
|
|
3900
3905
|
}
|
|
3901
3906
|
function convertFormDataToSearchParams(formData) {
|
|
3902
3907
|
let searchParams = new URLSearchParams();
|
|
3903
3908
|
for (let [key, value] of formData.entries()) {
|
|
3904
3909
|
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#converting-an-entry-list-to-a-list-of-name-value-pairs
|
|
3905
|
-
searchParams.append(key, value
|
|
3910
|
+
searchParams.append(key, typeof value === "string" ? value : value.name);
|
|
3906
3911
|
}
|
|
3907
3912
|
return searchParams;
|
|
3908
3913
|
}
|
|
3914
|
+
function convertSearchParamsToFormData(searchParams) {
|
|
3915
|
+
let formData = new FormData();
|
|
3916
|
+
for (let [key, value] of searchParams.entries()) {
|
|
3917
|
+
formData.append(key, value);
|
|
3918
|
+
}
|
|
3919
|
+
return formData;
|
|
3920
|
+
}
|
|
3909
3921
|
function processRouteLoaderData(matches, matchesToLoad, results, pendingError, activeDeferreds) {
|
|
3910
3922
|
// Fill in loaderData/errors from our loaders
|
|
3911
3923
|
let loaderData = {};
|
|
@@ -4019,15 +4031,7 @@ function processLoaderData(state, matches, matchesToLoad, results, pendingError,
|
|
|
4019
4031
|
// in resolveDeferredResults
|
|
4020
4032
|
invariant(false, "Unhandled fetcher deferred data");
|
|
4021
4033
|
} else {
|
|
4022
|
-
let doneFetcher =
|
|
4023
|
-
state: "idle",
|
|
4024
|
-
data: result.data,
|
|
4025
|
-
formMethod: undefined,
|
|
4026
|
-
formAction: undefined,
|
|
4027
|
-
formEncType: undefined,
|
|
4028
|
-
formData: undefined,
|
|
4029
|
-
" _hasFetcherDoneAnything ": true
|
|
4030
|
-
};
|
|
4034
|
+
let doneFetcher = getDoneFetcher(result.data);
|
|
4031
4035
|
state.fetchers.set(key, doneFetcher);
|
|
4032
4036
|
}
|
|
4033
4037
|
}
|
|
@@ -4094,6 +4098,8 @@ function getInternalRouterError(status, _temp4) {
|
|
|
4094
4098
|
errorMessage = "You made a " + method + " request to \"" + pathname + "\" but " + ("did not provide a `loader` for route \"" + routeId + "\", ") + "so there is no way to handle the request.";
|
|
4095
4099
|
} else if (type === "defer-action") {
|
|
4096
4100
|
errorMessage = "defer() is not supported in actions";
|
|
4101
|
+
} else if (type === "invalid-body") {
|
|
4102
|
+
errorMessage = "Unable to encode submission body";
|
|
4097
4103
|
}
|
|
4098
4104
|
} else if (status === 403) {
|
|
4099
4105
|
statusText = "Forbidden";
|
|
@@ -4263,6 +4269,144 @@ function getTargetMatch(matches, location) {
|
|
|
4263
4269
|
let pathMatches = getPathContributingMatches(matches);
|
|
4264
4270
|
return pathMatches[pathMatches.length - 1];
|
|
4265
4271
|
}
|
|
4272
|
+
function getSubmissionFromNavigation(navigation) {
|
|
4273
|
+
let {
|
|
4274
|
+
formMethod,
|
|
4275
|
+
formAction,
|
|
4276
|
+
formEncType,
|
|
4277
|
+
text,
|
|
4278
|
+
formData,
|
|
4279
|
+
json
|
|
4280
|
+
} = navigation;
|
|
4281
|
+
if (!formMethod || !formAction || !formEncType) {
|
|
4282
|
+
return;
|
|
4283
|
+
}
|
|
4284
|
+
if (text != null) {
|
|
4285
|
+
return {
|
|
4286
|
+
formMethod,
|
|
4287
|
+
formAction,
|
|
4288
|
+
formEncType,
|
|
4289
|
+
formData: undefined,
|
|
4290
|
+
json: undefined,
|
|
4291
|
+
text
|
|
4292
|
+
};
|
|
4293
|
+
} else if (formData != null) {
|
|
4294
|
+
return {
|
|
4295
|
+
formMethod,
|
|
4296
|
+
formAction,
|
|
4297
|
+
formEncType,
|
|
4298
|
+
formData,
|
|
4299
|
+
json: undefined,
|
|
4300
|
+
text: undefined
|
|
4301
|
+
};
|
|
4302
|
+
} else if (json !== undefined) {
|
|
4303
|
+
return {
|
|
4304
|
+
formMethod,
|
|
4305
|
+
formAction,
|
|
4306
|
+
formEncType,
|
|
4307
|
+
formData: undefined,
|
|
4308
|
+
json,
|
|
4309
|
+
text: undefined
|
|
4310
|
+
};
|
|
4311
|
+
}
|
|
4312
|
+
}
|
|
4313
|
+
function getLoadingNavigation(location, submission) {
|
|
4314
|
+
if (submission) {
|
|
4315
|
+
let navigation = {
|
|
4316
|
+
state: "loading",
|
|
4317
|
+
location,
|
|
4318
|
+
formMethod: submission.formMethod,
|
|
4319
|
+
formAction: submission.formAction,
|
|
4320
|
+
formEncType: submission.formEncType,
|
|
4321
|
+
formData: submission.formData,
|
|
4322
|
+
json: submission.json,
|
|
4323
|
+
text: submission.text
|
|
4324
|
+
};
|
|
4325
|
+
return navigation;
|
|
4326
|
+
} else {
|
|
4327
|
+
let navigation = {
|
|
4328
|
+
state: "loading",
|
|
4329
|
+
location,
|
|
4330
|
+
formMethod: undefined,
|
|
4331
|
+
formAction: undefined,
|
|
4332
|
+
formEncType: undefined,
|
|
4333
|
+
formData: undefined,
|
|
4334
|
+
json: undefined,
|
|
4335
|
+
text: undefined
|
|
4336
|
+
};
|
|
4337
|
+
return navigation;
|
|
4338
|
+
}
|
|
4339
|
+
}
|
|
4340
|
+
function getSubmittingNavigation(location, submission) {
|
|
4341
|
+
let navigation = {
|
|
4342
|
+
state: "submitting",
|
|
4343
|
+
location,
|
|
4344
|
+
formMethod: submission.formMethod,
|
|
4345
|
+
formAction: submission.formAction,
|
|
4346
|
+
formEncType: submission.formEncType,
|
|
4347
|
+
formData: submission.formData,
|
|
4348
|
+
json: submission.json,
|
|
4349
|
+
text: submission.text
|
|
4350
|
+
};
|
|
4351
|
+
return navigation;
|
|
4352
|
+
}
|
|
4353
|
+
function getLoadingFetcher(submission, data) {
|
|
4354
|
+
if (submission) {
|
|
4355
|
+
let fetcher = {
|
|
4356
|
+
state: "loading",
|
|
4357
|
+
formMethod: submission.formMethod,
|
|
4358
|
+
formAction: submission.formAction,
|
|
4359
|
+
formEncType: submission.formEncType,
|
|
4360
|
+
formData: submission.formData,
|
|
4361
|
+
json: submission.json,
|
|
4362
|
+
text: submission.text,
|
|
4363
|
+
data,
|
|
4364
|
+
" _hasFetcherDoneAnything ": true
|
|
4365
|
+
};
|
|
4366
|
+
return fetcher;
|
|
4367
|
+
} else {
|
|
4368
|
+
let fetcher = {
|
|
4369
|
+
state: "loading",
|
|
4370
|
+
formMethod: undefined,
|
|
4371
|
+
formAction: undefined,
|
|
4372
|
+
formEncType: undefined,
|
|
4373
|
+
formData: undefined,
|
|
4374
|
+
json: undefined,
|
|
4375
|
+
text: undefined,
|
|
4376
|
+
data,
|
|
4377
|
+
" _hasFetcherDoneAnything ": true
|
|
4378
|
+
};
|
|
4379
|
+
return fetcher;
|
|
4380
|
+
}
|
|
4381
|
+
}
|
|
4382
|
+
function getSubmittingFetcher(submission, existingFetcher) {
|
|
4383
|
+
let fetcher = {
|
|
4384
|
+
state: "submitting",
|
|
4385
|
+
formMethod: submission.formMethod,
|
|
4386
|
+
formAction: submission.formAction,
|
|
4387
|
+
formEncType: submission.formEncType,
|
|
4388
|
+
formData: submission.formData,
|
|
4389
|
+
json: submission.json,
|
|
4390
|
+
text: submission.text,
|
|
4391
|
+
data: existingFetcher ? existingFetcher.data : undefined,
|
|
4392
|
+
" _hasFetcherDoneAnything ": true
|
|
4393
|
+
};
|
|
4394
|
+
return fetcher;
|
|
4395
|
+
}
|
|
4396
|
+
function getDoneFetcher(data) {
|
|
4397
|
+
let fetcher = {
|
|
4398
|
+
state: "idle",
|
|
4399
|
+
formMethod: undefined,
|
|
4400
|
+
formAction: undefined,
|
|
4401
|
+
formEncType: undefined,
|
|
4402
|
+
formData: undefined,
|
|
4403
|
+
json: undefined,
|
|
4404
|
+
text: undefined,
|
|
4405
|
+
data,
|
|
4406
|
+
" _hasFetcherDoneAnything ": true
|
|
4407
|
+
};
|
|
4408
|
+
return fetcher;
|
|
4409
|
+
}
|
|
4266
4410
|
//#endregion
|
|
4267
4411
|
|
|
4268
4412
|
exports.AbortedDeferredError = AbortedDeferredError;
|