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