@remix-run/router 1.0.2 → 1.0.3-pre.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -2
- package/dist/index.d.ts +2 -2
- package/dist/router.cjs.js +317 -160
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.js +317 -161
- package/dist/router.js.map +1 -1
- package/dist/router.umd.js +3411 -0
- package/dist/router.umd.js.map +1 -0
- package/dist/router.umd.min.js +12 -0
- package/dist/router.umd.min.js.map +1 -0
- package/dist/utils.d.ts +24 -0
- package/history.ts +2 -2
- package/index.ts +5 -2
- package/package.json +2 -1
- package/router.ts +375 -158
- package/utils.ts +54 -1
package/dist/router.cjs.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @remix-run/router v1.0.
|
|
2
|
+
* @remix-run/router v1.0.3-pre.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -413,7 +413,7 @@ function getUrlBasedHistory(getLocation, createHref, validateLocation, options)
|
|
|
413
413
|
if (v5Compat && listener) {
|
|
414
414
|
listener({
|
|
415
415
|
action,
|
|
416
|
-
location
|
|
416
|
+
location: history.location
|
|
417
417
|
});
|
|
418
418
|
}
|
|
419
419
|
}
|
|
@@ -429,7 +429,7 @@ function getUrlBasedHistory(getLocation, createHref, validateLocation, options)
|
|
|
429
429
|
if (v5Compat && listener) {
|
|
430
430
|
listener({
|
|
431
431
|
action,
|
|
432
|
-
location: location
|
|
432
|
+
location: history.location
|
|
433
433
|
});
|
|
434
434
|
}
|
|
435
435
|
}
|
|
@@ -548,7 +548,10 @@ function matchRoutes(routes, locationArg, basename) {
|
|
|
548
548
|
let matches = null;
|
|
549
549
|
|
|
550
550
|
for (let i = 0; matches == null && i < branches.length; ++i) {
|
|
551
|
-
matches = matchRouteBranch(branches[i],
|
|
551
|
+
matches = matchRouteBranch(branches[i], // incoming pathnames are always encoded from either window.location or
|
|
552
|
+
// from route.navigate, but we want to match against the unencoded paths
|
|
553
|
+
// in the route definitions
|
|
554
|
+
safelyDecodeURI(pathname));
|
|
552
555
|
}
|
|
553
556
|
|
|
554
557
|
return matches;
|
|
@@ -795,6 +798,15 @@ function compilePath(path, caseSensitive, end) {
|
|
|
795
798
|
return [matcher, paramNames];
|
|
796
799
|
}
|
|
797
800
|
|
|
801
|
+
function safelyDecodeURI(value) {
|
|
802
|
+
try {
|
|
803
|
+
return decodeURI(value);
|
|
804
|
+
} catch (error) {
|
|
805
|
+
warning(false, "The URL path \"" + value + "\" could not be decoded because it is is a " + "malformed URL segment. This is probably due to a bad percent " + ("encoding (" + error + ")."));
|
|
806
|
+
return value;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
798
810
|
function safelyDecodeURIComponent(value, paramName) {
|
|
799
811
|
try {
|
|
800
812
|
return decodeURIComponent(value);
|
|
@@ -898,9 +910,36 @@ function getInvalidPathError(char, field, dest, path) {
|
|
|
898
910
|
}
|
|
899
911
|
/**
|
|
900
912
|
* @private
|
|
913
|
+
*
|
|
914
|
+
* When processing relative navigation we want to ignore ancestor routes that
|
|
915
|
+
* do not contribute to the path, such that index/pathless layout routes don't
|
|
916
|
+
* interfere.
|
|
917
|
+
*
|
|
918
|
+
* For example, when moving a route element into an index route and/or a
|
|
919
|
+
* pathless layout route, relative link behavior contained within should stay
|
|
920
|
+
* the same. Both of the following examples should link back to the root:
|
|
921
|
+
*
|
|
922
|
+
* <Route path="/">
|
|
923
|
+
* <Route path="accounts" element={<Link to=".."}>
|
|
924
|
+
* </Route>
|
|
925
|
+
*
|
|
926
|
+
* <Route path="/">
|
|
927
|
+
* <Route path="accounts">
|
|
928
|
+
* <Route element={<AccountsLayout />}> // <-- Does not contribute
|
|
929
|
+
* <Route index element={<Link to=".."} /> // <-- Does not contribute
|
|
930
|
+
* </Route
|
|
931
|
+
* </Route>
|
|
932
|
+
* </Route>
|
|
901
933
|
*/
|
|
902
934
|
|
|
903
935
|
|
|
936
|
+
function getPathContributingMatches(matches) {
|
|
937
|
+
return matches.filter((match, index) => index === 0 || match.route.path && match.route.path.length > 0);
|
|
938
|
+
}
|
|
939
|
+
/**
|
|
940
|
+
* @private
|
|
941
|
+
*/
|
|
942
|
+
|
|
904
943
|
function resolveTo(toArg, routePathnames, locationPathname, isPathRelative) {
|
|
905
944
|
if (isPathRelative === void 0) {
|
|
906
945
|
isPathRelative = false;
|
|
@@ -1228,7 +1267,9 @@ const IDLE_FETCHER = {
|
|
|
1228
1267
|
formAction: undefined,
|
|
1229
1268
|
formEncType: undefined,
|
|
1230
1269
|
formData: undefined
|
|
1231
|
-
};
|
|
1270
|
+
};
|
|
1271
|
+
const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined";
|
|
1272
|
+
const isServer = !isBrowser; //#endregion
|
|
1232
1273
|
////////////////////////////////////////////////////////////////////////////////
|
|
1233
1274
|
//#region createRouter
|
|
1234
1275
|
////////////////////////////////////////////////////////////////////////////////
|
|
@@ -1435,7 +1476,18 @@ function createRouter(init) {
|
|
|
1435
1476
|
submission,
|
|
1436
1477
|
error
|
|
1437
1478
|
} = normalizeNavigateOptions(to, opts);
|
|
1438
|
-
let location = createLocation(state.location, path, opts && opts.state);
|
|
1479
|
+
let location = createLocation(state.location, path, opts && opts.state); // When using navigate as a PUSH/REPLACE we aren't reading an already-encoded
|
|
1480
|
+
// URL from window.location, so we need to encode it here so the behavior
|
|
1481
|
+
// remains the same as POP and non-data-router usages. new URL() does all
|
|
1482
|
+
// the same encoding we'd get from a history.pushState/window.location read
|
|
1483
|
+
// without having to touch history
|
|
1484
|
+
|
|
1485
|
+
let url = createURL(createPath(location));
|
|
1486
|
+
location = _extends({}, location, {
|
|
1487
|
+
pathname: url.pathname,
|
|
1488
|
+
search: url.search,
|
|
1489
|
+
hash: url.hash
|
|
1490
|
+
});
|
|
1439
1491
|
let historyAction = (opts && opts.replace) === true || submission != null ? exports.Action.Replace : exports.Action.Push;
|
|
1440
1492
|
let preventScrollReset = opts && "preventScrollReset" in opts ? opts.preventScrollReset === true : undefined;
|
|
1441
1493
|
return await startNavigation(historyAction, location, {
|
|
@@ -1601,7 +1653,7 @@ function createRouter(init) {
|
|
|
1601
1653
|
if (!actionMatch.route.action) {
|
|
1602
1654
|
result = getMethodNotAllowedResult(location);
|
|
1603
1655
|
} else {
|
|
1604
|
-
result = await callLoaderOrAction("action", request, actionMatch);
|
|
1656
|
+
result = await callLoaderOrAction("action", request, actionMatch, matches, router.basename);
|
|
1605
1657
|
|
|
1606
1658
|
if (request.signal.aborted) {
|
|
1607
1659
|
return {
|
|
@@ -1696,7 +1748,7 @@ function createRouter(init) {
|
|
|
1696
1748
|
if (!isUninterruptedRevalidation) {
|
|
1697
1749
|
revalidatingFetchers.forEach(_ref2 => {
|
|
1698
1750
|
let [key] = _ref2;
|
|
1699
|
-
|
|
1751
|
+
let fetcher = state.fetchers.get(key);
|
|
1700
1752
|
let revalidatingFetcher = {
|
|
1701
1753
|
state: "loading",
|
|
1702
1754
|
data: fetcher && fetcher.data,
|
|
@@ -1724,7 +1776,7 @@ function createRouter(init) {
|
|
|
1724
1776
|
results,
|
|
1725
1777
|
loaderResults,
|
|
1726
1778
|
fetcherResults
|
|
1727
|
-
} = await callLoadersAndMaybeResolveData(state.matches, matchesToLoad, revalidatingFetchers, request);
|
|
1779
|
+
} = await callLoadersAndMaybeResolveData(state.matches, matches, matchesToLoad, revalidatingFetchers, request);
|
|
1728
1780
|
|
|
1729
1781
|
if (request.signal.aborted) {
|
|
1730
1782
|
return {
|
|
@@ -1782,7 +1834,7 @@ function createRouter(init) {
|
|
|
1782
1834
|
|
|
1783
1835
|
|
|
1784
1836
|
function fetch(key, routeId, href, opts) {
|
|
1785
|
-
if (
|
|
1837
|
+
if (isServer) {
|
|
1786
1838
|
throw new Error("router.fetch() was called during the server render, but it shouldn't be. " + "You are likely calling a useFetcher() method in the body of your component. " + "Try moving it to a useEffect or a callback.");
|
|
1787
1839
|
}
|
|
1788
1840
|
|
|
@@ -1801,19 +1853,19 @@ function createRouter(init) {
|
|
|
1801
1853
|
let match = getTargetMatch(matches, path);
|
|
1802
1854
|
|
|
1803
1855
|
if (submission) {
|
|
1804
|
-
handleFetcherAction(key, routeId, path, match, submission);
|
|
1856
|
+
handleFetcherAction(key, routeId, path, match, matches, submission);
|
|
1805
1857
|
return;
|
|
1806
1858
|
} // Store off the match so we can call it's shouldRevalidate on subsequent
|
|
1807
1859
|
// revalidations
|
|
1808
1860
|
|
|
1809
1861
|
|
|
1810
|
-
fetchLoadMatches.set(key, [path, match]);
|
|
1811
|
-
handleFetcherLoader(key, routeId, path, match);
|
|
1862
|
+
fetchLoadMatches.set(key, [path, match, matches]);
|
|
1863
|
+
handleFetcherLoader(key, routeId, path, match, matches);
|
|
1812
1864
|
} // Call the action for the matched fetcher.submit(), and then handle redirects,
|
|
1813
1865
|
// errors, and revalidation
|
|
1814
1866
|
|
|
1815
1867
|
|
|
1816
|
-
async function handleFetcherAction(key, routeId, path, match, submission) {
|
|
1868
|
+
async function handleFetcherAction(key, routeId, path, match, requestMatches, submission) {
|
|
1817
1869
|
interruptActiveLoads();
|
|
1818
1870
|
fetchLoadMatches.delete(key);
|
|
1819
1871
|
|
|
@@ -1842,7 +1894,7 @@ function createRouter(init) {
|
|
|
1842
1894
|
let abortController = new AbortController();
|
|
1843
1895
|
let fetchRequest = createRequest(path, abortController.signal, submission);
|
|
1844
1896
|
fetchControllers.set(key, abortController);
|
|
1845
|
-
let actionResult = await callLoaderOrAction("action", fetchRequest, match);
|
|
1897
|
+
let actionResult = await callLoaderOrAction("action", fetchRequest, match, requestMatches, router.basename);
|
|
1846
1898
|
|
|
1847
1899
|
if (fetchRequest.signal.aborted) {
|
|
1848
1900
|
// We can delete this so long as we weren't aborted by ou our own fetcher
|
|
@@ -1934,7 +1986,7 @@ function createRouter(init) {
|
|
|
1934
1986
|
results,
|
|
1935
1987
|
loaderResults,
|
|
1936
1988
|
fetcherResults
|
|
1937
|
-
} = await callLoadersAndMaybeResolveData(state.matches, matchesToLoad, revalidatingFetchers, revalidationRequest);
|
|
1989
|
+
} = await callLoadersAndMaybeResolveData(state.matches, matches, matchesToLoad, revalidatingFetchers, revalidationRequest);
|
|
1938
1990
|
|
|
1939
1991
|
if (abortController.signal.aborted) {
|
|
1940
1992
|
return;
|
|
@@ -1996,7 +2048,7 @@ function createRouter(init) {
|
|
|
1996
2048
|
} // Call the matched loader for fetcher.load(), handling redirects, errors, etc.
|
|
1997
2049
|
|
|
1998
2050
|
|
|
1999
|
-
async function handleFetcherLoader(key, routeId, path, match) {
|
|
2051
|
+
async function handleFetcherLoader(key, routeId, path, match, matches) {
|
|
2000
2052
|
let existingFetcher = state.fetchers.get(key); // Put this fetcher into it's loading state
|
|
2001
2053
|
|
|
2002
2054
|
let loadingFetcher = {
|
|
@@ -2015,7 +2067,7 @@ function createRouter(init) {
|
|
|
2015
2067
|
let abortController = new AbortController();
|
|
2016
2068
|
let fetchRequest = createRequest(path, abortController.signal);
|
|
2017
2069
|
fetchControllers.set(key, abortController);
|
|
2018
|
-
let result = await callLoaderOrAction("loader", fetchRequest, match); // Deferred isn't supported or fetcher loads, await everything and treat it
|
|
2070
|
+
let result = await callLoaderOrAction("loader", fetchRequest, match, matches, router.basename); // Deferred isn't supported or fetcher loads, await everything and treat it
|
|
2019
2071
|
// as a normal load. resolveDeferredData will return undefined if this
|
|
2020
2072
|
// fetcher gets aborted, so we just leave result untouched and short circuit
|
|
2021
2073
|
// below if that happens
|
|
@@ -2108,13 +2160,13 @@ function createRouter(init) {
|
|
|
2108
2160
|
});
|
|
2109
2161
|
}
|
|
2110
2162
|
|
|
2111
|
-
async function callLoadersAndMaybeResolveData(currentMatches, matchesToLoad, fetchersToLoad, request) {
|
|
2163
|
+
async function callLoadersAndMaybeResolveData(currentMatches, matches, matchesToLoad, fetchersToLoad, request) {
|
|
2112
2164
|
// Call all navigation loaders and revalidating fetcher loaders in parallel,
|
|
2113
2165
|
// then slice off the results into separate arrays so we can handle them
|
|
2114
2166
|
// accordingly
|
|
2115
|
-
let results = await Promise.all([...matchesToLoad.map(
|
|
2116
|
-
let [, href, match] = _ref8;
|
|
2117
|
-
return callLoaderOrAction("loader", createRequest(href, request.signal), match);
|
|
2167
|
+
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, router.basename)), ...fetchersToLoad.map(_ref8 => {
|
|
2168
|
+
let [, href, match, fetchMatches] = _ref8;
|
|
2169
|
+
return callLoaderOrAction("loader", createRequest(href, request.signal), match, fetchMatches, router.basename);
|
|
2118
2170
|
})]);
|
|
2119
2171
|
let loaderResults = results.slice(0, matchesToLoad.length);
|
|
2120
2172
|
let fetcherResults = results.slice(matchesToLoad.length);
|
|
@@ -2306,7 +2358,9 @@ function createRouter(init) {
|
|
|
2306
2358
|
navigate,
|
|
2307
2359
|
fetch,
|
|
2308
2360
|
revalidate,
|
|
2309
|
-
createHref
|
|
2361
|
+
// Passthrough to history-aware createHref used by useHref so we get proper
|
|
2362
|
+
// hash-aware URLs in DOM paths
|
|
2363
|
+
createHref: to => init.history.createHref(to),
|
|
2310
2364
|
getFetcher,
|
|
2311
2365
|
deleteFetcher,
|
|
2312
2366
|
dispose,
|
|
@@ -2319,15 +2373,75 @@ function createRouter(init) {
|
|
|
2319
2373
|
//#region createStaticHandler
|
|
2320
2374
|
////////////////////////////////////////////////////////////////////////////////
|
|
2321
2375
|
|
|
2376
|
+
const validActionMethods = new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
2377
|
+
const validRequestMethods = new Set(["GET", "HEAD", ...validActionMethods]);
|
|
2322
2378
|
function unstable_createStaticHandler(routes) {
|
|
2323
2379
|
invariant(routes.length > 0, "You must provide a non-empty routes array to unstable_createStaticHandler");
|
|
2324
2380
|
let dataRoutes = convertRoutesToDataRoutes(routes);
|
|
2381
|
+
/**
|
|
2382
|
+
* The query() method is intended for document requests, in which we want to
|
|
2383
|
+
* call an optional action and potentially multiple loaders for all nested
|
|
2384
|
+
* routes. It returns a StaticHandlerContext object, which is very similar
|
|
2385
|
+
* to the router state (location, loaderData, actionData, errors, etc.) and
|
|
2386
|
+
* also adds SSR-specific information such as the statusCode and headers
|
|
2387
|
+
* from action/loaders Responses.
|
|
2388
|
+
*
|
|
2389
|
+
* It _should_ never throw and should report all errors through the
|
|
2390
|
+
* returned context.errors object, properly associating errors to their error
|
|
2391
|
+
* boundary. Additionally, it tracks _deepestRenderedBoundaryId which can be
|
|
2392
|
+
* used to emulate React error boundaries during SSr by performing a second
|
|
2393
|
+
* pass only down to the boundaryId.
|
|
2394
|
+
*
|
|
2395
|
+
* The one exception where we do not return a StaticHandlerContext is when a
|
|
2396
|
+
* redirect response is returned or thrown from any action/loader. We
|
|
2397
|
+
* propagate that out and return the raw Response so the HTTP server can
|
|
2398
|
+
* return it directly.
|
|
2399
|
+
*/
|
|
2325
2400
|
|
|
2326
2401
|
async function query(request) {
|
|
2327
|
-
let
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2402
|
+
let url = new URL(request.url);
|
|
2403
|
+
let location = createLocation("", createPath(url), null, "default");
|
|
2404
|
+
let matches = matchRoutes(dataRoutes, location);
|
|
2405
|
+
|
|
2406
|
+
if (!validRequestMethods.has(request.method)) {
|
|
2407
|
+
let {
|
|
2408
|
+
matches: methodNotAllowedMatches,
|
|
2409
|
+
route,
|
|
2410
|
+
error
|
|
2411
|
+
} = getMethodNotAllowedMatches(dataRoutes);
|
|
2412
|
+
return {
|
|
2413
|
+
location,
|
|
2414
|
+
matches: methodNotAllowedMatches,
|
|
2415
|
+
loaderData: {},
|
|
2416
|
+
actionData: null,
|
|
2417
|
+
errors: {
|
|
2418
|
+
[route.id]: error
|
|
2419
|
+
},
|
|
2420
|
+
statusCode: error.status,
|
|
2421
|
+
loaderHeaders: {},
|
|
2422
|
+
actionHeaders: {}
|
|
2423
|
+
};
|
|
2424
|
+
} else if (!matches) {
|
|
2425
|
+
let {
|
|
2426
|
+
matches: notFoundMatches,
|
|
2427
|
+
route,
|
|
2428
|
+
error
|
|
2429
|
+
} = getNotFoundMatches(dataRoutes);
|
|
2430
|
+
return {
|
|
2431
|
+
location,
|
|
2432
|
+
matches: notFoundMatches,
|
|
2433
|
+
loaderData: {},
|
|
2434
|
+
actionData: null,
|
|
2435
|
+
errors: {
|
|
2436
|
+
[route.id]: error
|
|
2437
|
+
},
|
|
2438
|
+
statusCode: error.status,
|
|
2439
|
+
loaderHeaders: {},
|
|
2440
|
+
actionHeaders: {}
|
|
2441
|
+
};
|
|
2442
|
+
}
|
|
2443
|
+
|
|
2444
|
+
let result = await queryImpl(request, location, matches);
|
|
2331
2445
|
|
|
2332
2446
|
if (result instanceof Response) {
|
|
2333
2447
|
return result;
|
|
@@ -2340,11 +2454,52 @@ function unstable_createStaticHandler(routes) {
|
|
|
2340
2454
|
location
|
|
2341
2455
|
}, result);
|
|
2342
2456
|
}
|
|
2457
|
+
/**
|
|
2458
|
+
* The queryRoute() method is intended for targeted route requests, either
|
|
2459
|
+
* for fetch ?_data requests or resource route requests. In this case, we
|
|
2460
|
+
* are only ever calling a single action or loader, and we are returning the
|
|
2461
|
+
* returned value directly. In most cases, this will be a Response returned
|
|
2462
|
+
* from the action/loader, but it may be a primitive or other value as well -
|
|
2463
|
+
* and in such cases the calling context should handle that accordingly.
|
|
2464
|
+
*
|
|
2465
|
+
* We do respect the throw/return differentiation, so if an action/loader
|
|
2466
|
+
* throws, then this method will throw the value. This is important so we
|
|
2467
|
+
* can do proper boundary identification in Remix where a thrown Response
|
|
2468
|
+
* must go to the Catch Boundary but a returned Response is happy-path.
|
|
2469
|
+
*
|
|
2470
|
+
* One thing to note is that any Router-initiated thrown Response (such as a
|
|
2471
|
+
* 404 or 405) will have a custom X-Remix-Router-Error: "yes" header on it
|
|
2472
|
+
* in order to differentiate from responses thrown from user actions/loaders.
|
|
2473
|
+
*/
|
|
2474
|
+
|
|
2343
2475
|
|
|
2344
2476
|
async function queryRoute(request, routeId) {
|
|
2345
|
-
let
|
|
2346
|
-
|
|
2347
|
-
|
|
2477
|
+
let url = new URL(request.url);
|
|
2478
|
+
let location = createLocation("", createPath(url), null, "default");
|
|
2479
|
+
let matches = matchRoutes(dataRoutes, location);
|
|
2480
|
+
|
|
2481
|
+
if (!validRequestMethods.has(request.method)) {
|
|
2482
|
+
throw createRouterErrorResponse(null, {
|
|
2483
|
+
status: 405,
|
|
2484
|
+
statusText: "Method Not Allowed"
|
|
2485
|
+
});
|
|
2486
|
+
} else if (!matches) {
|
|
2487
|
+
throw createRouterErrorResponse(null, {
|
|
2488
|
+
status: 404,
|
|
2489
|
+
statusText: "Not Found"
|
|
2490
|
+
});
|
|
2491
|
+
}
|
|
2492
|
+
|
|
2493
|
+
let match = routeId ? matches.find(m => m.route.id === routeId) : getTargetMatch(matches, location);
|
|
2494
|
+
|
|
2495
|
+
if (!match) {
|
|
2496
|
+
throw createRouterErrorResponse(null, {
|
|
2497
|
+
status: 404,
|
|
2498
|
+
statusText: "Not Found"
|
|
2499
|
+
});
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
let result = await queryImpl(request, location, matches, match);
|
|
2348
2503
|
|
|
2349
2504
|
if (result instanceof Response) {
|
|
2350
2505
|
return result;
|
|
@@ -2353,77 +2508,48 @@ function unstable_createStaticHandler(routes) {
|
|
|
2353
2508
|
let error = result.errors ? Object.values(result.errors)[0] : undefined;
|
|
2354
2509
|
|
|
2355
2510
|
if (error !== undefined) {
|
|
2356
|
-
//
|
|
2357
|
-
// directly for route requests and prevent the unwrapping into an
|
|
2358
|
-
// ErrorResponse, we still need this for error cases _prior_ the
|
|
2359
|
-
// execution of the loader/action, such as a 404/405 error.
|
|
2360
|
-
if (isRouteErrorResponse(error)) {
|
|
2361
|
-
return new Response(error.data, {
|
|
2362
|
-
status: error.status,
|
|
2363
|
-
statusText: error.statusText
|
|
2364
|
-
});
|
|
2365
|
-
} // If we got back result.errors, that means the loader/action threw
|
|
2511
|
+
// If we got back result.errors, that means the loader/action threw
|
|
2366
2512
|
// _something_ that wasn't a Response, but it's not guaranteed/required
|
|
2367
2513
|
// to be an `instanceof Error` either, so we have to use throw here to
|
|
2368
2514
|
// preserve the "error" state outside of queryImpl.
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
2515
|
throw error;
|
|
2372
2516
|
} // Pick off the right state value to return
|
|
2373
2517
|
|
|
2374
2518
|
|
|
2375
2519
|
let routeData = [result.actionData, result.loaderData].find(v => v);
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
if (isRouteErrorResponse(value)) {
|
|
2379
|
-
return new Response(value.data, {
|
|
2380
|
-
status: value.status,
|
|
2381
|
-
statusText: value.statusText
|
|
2382
|
-
});
|
|
2383
|
-
}
|
|
2384
|
-
|
|
2385
|
-
return value;
|
|
2520
|
+
return Object.values(routeData || {})[0];
|
|
2386
2521
|
}
|
|
2387
2522
|
|
|
2388
|
-
async function queryImpl(request,
|
|
2389
|
-
invariant(request.method !== "HEAD", "query()/queryRoute() do not support HEAD requests");
|
|
2523
|
+
async function queryImpl(request, location, matches, routeMatch) {
|
|
2390
2524
|
invariant(request.signal, "query()/queryRoute() requests must contain an AbortController signal");
|
|
2391
|
-
let {
|
|
2392
|
-
location,
|
|
2393
|
-
matches,
|
|
2394
|
-
shortCircuitState
|
|
2395
|
-
} = matchRequest(request, routeId);
|
|
2396
2525
|
|
|
2397
2526
|
try {
|
|
2398
|
-
if (
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
result: shortCircuitState
|
|
2402
|
-
};
|
|
2527
|
+
if (validActionMethods.has(request.method)) {
|
|
2528
|
+
let result = await submit(request, matches, routeMatch || getTargetMatch(matches, location), routeMatch != null);
|
|
2529
|
+
return result;
|
|
2403
2530
|
}
|
|
2404
2531
|
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
};
|
|
2411
|
-
}
|
|
2412
|
-
|
|
2413
|
-
let result = await loadRouteData(request, matches, routeId != null);
|
|
2414
|
-
return {
|
|
2415
|
-
location,
|
|
2416
|
-
result: _extends({}, result, {
|
|
2417
|
-
actionData: null,
|
|
2418
|
-
actionHeaders: {}
|
|
2419
|
-
})
|
|
2420
|
-
};
|
|
2532
|
+
let result = await loadRouteData(request, matches, routeMatch);
|
|
2533
|
+
return result instanceof Response ? result : _extends({}, result, {
|
|
2534
|
+
actionData: null,
|
|
2535
|
+
actionHeaders: {}
|
|
2536
|
+
});
|
|
2421
2537
|
} catch (e) {
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2538
|
+
// If the user threw/returned a Response in callLoaderOrAction, we throw
|
|
2539
|
+
// it to bail out and then return or throw here based on whether the user
|
|
2540
|
+
// returned or threw
|
|
2541
|
+
if (isQueryRouteResponse(e)) {
|
|
2542
|
+
if (e.type === ResultType.error && !isRedirectResponse(e.response)) {
|
|
2543
|
+
throw e.response;
|
|
2544
|
+
}
|
|
2545
|
+
|
|
2546
|
+
return e.response;
|
|
2547
|
+
} // Redirects are always returned since they don't propagate to catch
|
|
2548
|
+
// boundaries
|
|
2549
|
+
|
|
2550
|
+
|
|
2551
|
+
if (isRedirectResponse(e)) {
|
|
2552
|
+
return e;
|
|
2427
2553
|
}
|
|
2428
2554
|
|
|
2429
2555
|
throw e;
|
|
@@ -2434,10 +2560,19 @@ function unstable_createStaticHandler(routes) {
|
|
|
2434
2560
|
let result;
|
|
2435
2561
|
|
|
2436
2562
|
if (!actionMatch.route.action) {
|
|
2437
|
-
let href =
|
|
2563
|
+
let href = createServerHref(new URL(request.url));
|
|
2564
|
+
|
|
2565
|
+
if (isRouteRequest) {
|
|
2566
|
+
throw createRouterErrorResponse(null, {
|
|
2567
|
+
status: 405,
|
|
2568
|
+
statusText: "Method Not Allowed"
|
|
2569
|
+
});
|
|
2570
|
+
}
|
|
2571
|
+
|
|
2438
2572
|
result = getMethodNotAllowedResult(href);
|
|
2439
2573
|
} else {
|
|
2440
|
-
result = await callLoaderOrAction("action", request, actionMatch,
|
|
2574
|
+
result = await callLoaderOrAction("action", request, actionMatch, matches, undefined, // Basename not currently supported in static handlers
|
|
2575
|
+
true, isRouteRequest);
|
|
2441
2576
|
|
|
2442
2577
|
if (request.signal.aborted) {
|
|
2443
2578
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
@@ -2447,7 +2582,7 @@ function unstable_createStaticHandler(routes) {
|
|
|
2447
2582
|
|
|
2448
2583
|
if (isRedirectResult(result)) {
|
|
2449
2584
|
// Uhhhh - this should never happen, we should always throw these from
|
|
2450
|
-
//
|
|
2585
|
+
// callLoaderOrAction, but the type narrowing here keeps TS happy and we
|
|
2451
2586
|
// can get back on the "throw all redirect responses" train here should
|
|
2452
2587
|
// this ever happen :/
|
|
2453
2588
|
throw new Response(null, {
|
|
@@ -2463,6 +2598,8 @@ function unstable_createStaticHandler(routes) {
|
|
|
2463
2598
|
}
|
|
2464
2599
|
|
|
2465
2600
|
if (isRouteRequest) {
|
|
2601
|
+
// Note: This should only be non-Response values if we get here, since
|
|
2602
|
+
// isRouteRequest should throw any Response received in callLoaderOrAction
|
|
2466
2603
|
if (isErrorResult(result)) {
|
|
2467
2604
|
let boundaryMatch = findNearestBoundary(matches, actionMatch.route.id);
|
|
2468
2605
|
return {
|
|
@@ -2499,7 +2636,7 @@ function unstable_createStaticHandler(routes) {
|
|
|
2499
2636
|
// Store off the pending error - we use it to determine which loaders
|
|
2500
2637
|
// to call and will commit it when we complete the navigation
|
|
2501
2638
|
let boundaryMatch = findNearestBoundary(matches, actionMatch.route.id);
|
|
2502
|
-
let context = await loadRouteData(request, matches,
|
|
2639
|
+
let context = await loadRouteData(request, matches, undefined, {
|
|
2503
2640
|
[boundaryMatch.route.id]: result.error
|
|
2504
2641
|
}); // action status codes take precedence over loader status codes
|
|
2505
2642
|
|
|
@@ -2512,7 +2649,7 @@ function unstable_createStaticHandler(routes) {
|
|
|
2512
2649
|
});
|
|
2513
2650
|
}
|
|
2514
2651
|
|
|
2515
|
-
let context = await loadRouteData(request, matches
|
|
2652
|
+
let context = await loadRouteData(request, matches);
|
|
2516
2653
|
return _extends({}, context, result.statusCode ? {
|
|
2517
2654
|
statusCode: result.statusCode
|
|
2518
2655
|
} : {}, {
|
|
@@ -2525,8 +2662,10 @@ function unstable_createStaticHandler(routes) {
|
|
|
2525
2662
|
});
|
|
2526
2663
|
}
|
|
2527
2664
|
|
|
2528
|
-
async function loadRouteData(request, matches,
|
|
2529
|
-
let
|
|
2665
|
+
async function loadRouteData(request, matches, routeMatch, pendingActionError) {
|
|
2666
|
+
let isRouteRequest = routeMatch != null;
|
|
2667
|
+
let requestMatches = routeMatch ? [routeMatch] : getLoaderMatchesUntilBoundary(matches, Object.keys(pendingActionError || {})[0]);
|
|
2668
|
+
let matchesToLoad = requestMatches.filter(m => m.route.loader); // Short circuit if we have no loaders to run
|
|
2530
2669
|
|
|
2531
2670
|
if (matchesToLoad.length === 0) {
|
|
2532
2671
|
return {
|
|
@@ -2538,7 +2677,8 @@ function unstable_createStaticHandler(routes) {
|
|
|
2538
2677
|
};
|
|
2539
2678
|
}
|
|
2540
2679
|
|
|
2541
|
-
let results = await Promise.all([...matchesToLoad.map(
|
|
2680
|
+
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, undefined, // Basename not currently supported in static handlers
|
|
2681
|
+
true, isRouteRequest))]);
|
|
2542
2682
|
|
|
2543
2683
|
if (request.signal.aborted) {
|
|
2544
2684
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
@@ -2559,43 +2699,12 @@ function unstable_createStaticHandler(routes) {
|
|
|
2559
2699
|
});
|
|
2560
2700
|
}
|
|
2561
2701
|
|
|
2562
|
-
function
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
matches = matches.filter(m => m.route.id === routeId);
|
|
2569
|
-
} // Short circuit with a 404 if we match nothing
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
if (!matches) {
|
|
2573
|
-
let {
|
|
2574
|
-
matches: notFoundMatches,
|
|
2575
|
-
route,
|
|
2576
|
-
error
|
|
2577
|
-
} = getNotFoundMatches(dataRoutes);
|
|
2578
|
-
return {
|
|
2579
|
-
location,
|
|
2580
|
-
matches: notFoundMatches,
|
|
2581
|
-
shortCircuitState: {
|
|
2582
|
-
matches: notFoundMatches,
|
|
2583
|
-
loaderData: {},
|
|
2584
|
-
actionData: null,
|
|
2585
|
-
errors: {
|
|
2586
|
-
[route.id]: error
|
|
2587
|
-
},
|
|
2588
|
-
statusCode: 404,
|
|
2589
|
-
loaderHeaders: {},
|
|
2590
|
-
actionHeaders: {}
|
|
2591
|
-
}
|
|
2592
|
-
};
|
|
2593
|
-
}
|
|
2594
|
-
|
|
2595
|
-
return {
|
|
2596
|
-
location,
|
|
2597
|
-
matches
|
|
2598
|
-
};
|
|
2702
|
+
function createRouterErrorResponse(body, init) {
|
|
2703
|
+
return new Response(body, _extends({}, init, {
|
|
2704
|
+
headers: _extends({}, init.headers, {
|
|
2705
|
+
"X-Remix-Router-Error": "yes"
|
|
2706
|
+
})
|
|
2707
|
+
}));
|
|
2599
2708
|
}
|
|
2600
2709
|
|
|
2601
2710
|
return {
|
|
@@ -2644,7 +2753,7 @@ function normalizeNavigateOptions(to, opts, isFetcher) {
|
|
|
2644
2753
|
path,
|
|
2645
2754
|
submission: {
|
|
2646
2755
|
formMethod: opts.formMethod,
|
|
2647
|
-
formAction:
|
|
2756
|
+
formAction: createServerHref(parsePath(path)),
|
|
2648
2757
|
formEncType: opts && opts.formEncType || "application/x-www-form-urlencoded",
|
|
2649
2758
|
formData: opts.formData
|
|
2650
2759
|
}
|
|
@@ -2727,16 +2836,16 @@ function getMatchesToLoad(state, matches, submission, location, isRevalidationRe
|
|
|
2727
2836
|
|
|
2728
2837
|
let revalidatingFetchers = [];
|
|
2729
2838
|
fetchLoadMatches && fetchLoadMatches.forEach((_ref10, key) => {
|
|
2730
|
-
let [href, match] = _ref10;
|
|
2839
|
+
let [href, match, fetchMatches] = _ref10;
|
|
2731
2840
|
|
|
2732
2841
|
// This fetcher was cancelled from a prior action submission - force reload
|
|
2733
2842
|
if (cancelledFetcherLoads.includes(key)) {
|
|
2734
|
-
revalidatingFetchers.push([key, href, match]);
|
|
2843
|
+
revalidatingFetchers.push([key, href, match, fetchMatches]);
|
|
2735
2844
|
} else if (isRevalidationRequired) {
|
|
2736
2845
|
let shouldRevalidate = shouldRevalidateLoader(href, match, submission, href, match, isRevalidationRequired, actionResult);
|
|
2737
2846
|
|
|
2738
2847
|
if (shouldRevalidate) {
|
|
2739
|
-
revalidatingFetchers.push([key, href, match]);
|
|
2848
|
+
revalidatingFetchers.push([key, href, match, fetchMatches]);
|
|
2740
2849
|
}
|
|
2741
2850
|
}
|
|
2742
2851
|
});
|
|
@@ -2798,9 +2907,9 @@ function shouldRevalidateLoader(currentLocation, currentMatch, submission, locat
|
|
|
2798
2907
|
return defaultShouldRevalidate;
|
|
2799
2908
|
}
|
|
2800
2909
|
|
|
2801
|
-
async function callLoaderOrAction(type, request, match,
|
|
2802
|
-
if (
|
|
2803
|
-
|
|
2910
|
+
async function callLoaderOrAction(type, request, match, matches, basename, isStaticRequest, isRouteRequest) {
|
|
2911
|
+
if (isStaticRequest === void 0) {
|
|
2912
|
+
isStaticRequest = false;
|
|
2804
2913
|
}
|
|
2805
2914
|
|
|
2806
2915
|
if (isRouteRequest === void 0) {
|
|
@@ -2832,20 +2941,30 @@ async function callLoaderOrAction(type, request, match, skipRedirects, isRouteRe
|
|
|
2832
2941
|
}
|
|
2833
2942
|
|
|
2834
2943
|
if (result instanceof Response) {
|
|
2835
|
-
// Process redirects
|
|
2836
|
-
let status = result.status;
|
|
2837
|
-
let location = result.headers.get("Location"); // For SSR single-route requests, we want to hand Responses back directly
|
|
2838
|
-
// without unwrapping
|
|
2944
|
+
let status = result.status; // Process redirects
|
|
2839
2945
|
|
|
2840
|
-
if (
|
|
2841
|
-
|
|
2842
|
-
|
|
2946
|
+
if (status >= 300 && status <= 399) {
|
|
2947
|
+
let location = result.headers.get("Location");
|
|
2948
|
+
invariant(location, "Redirects returned/thrown from loaders/actions must have a Location header"); // Support relative routing in redirects
|
|
2843
2949
|
|
|
2844
|
-
|
|
2845
|
-
|
|
2950
|
+
let activeMatches = matches.slice(0, matches.indexOf(match) + 1);
|
|
2951
|
+
let routePathnames = getPathContributingMatches(activeMatches).map(match => match.pathnameBase);
|
|
2952
|
+
let requestPath = createURL(request.url).pathname;
|
|
2953
|
+
let resolvedLocation = resolveTo(location, routePathnames, requestPath);
|
|
2954
|
+
invariant(createPath(resolvedLocation), "Unable to resolve redirect location: " + result.headers.get("Location")); // Prepend the basename to the redirect location if we have one
|
|
2955
|
+
|
|
2956
|
+
if (basename) {
|
|
2957
|
+
let path = resolvedLocation.pathname;
|
|
2958
|
+
resolvedLocation.pathname = path === "/" ? basename : joinPaths([basename, path]);
|
|
2959
|
+
}
|
|
2960
|
+
|
|
2961
|
+
location = createPath(resolvedLocation); // Don't process redirects in the router during static requests requests.
|
|
2846
2962
|
// Instead, throw the Response and let the server handle it with an HTTP
|
|
2847
|
-
// redirect
|
|
2848
|
-
|
|
2963
|
+
// redirect. We also update the Location header in place in this flow so
|
|
2964
|
+
// basename and relative routing is taken into account
|
|
2965
|
+
|
|
2966
|
+
if (isStaticRequest) {
|
|
2967
|
+
result.headers.set("Location", location);
|
|
2849
2968
|
throw result;
|
|
2850
2969
|
}
|
|
2851
2970
|
|
|
@@ -2855,6 +2974,17 @@ async function callLoaderOrAction(type, request, match, skipRedirects, isRouteRe
|
|
|
2855
2974
|
location,
|
|
2856
2975
|
revalidate: result.headers.get("X-Remix-Revalidate") !== null
|
|
2857
2976
|
};
|
|
2977
|
+
} // For SSR single-route requests, we want to hand Responses back directly
|
|
2978
|
+
// without unwrapping. We do this with the QueryRouteResponse wrapper
|
|
2979
|
+
// interface so we can know whether it was returned or thrown
|
|
2980
|
+
|
|
2981
|
+
|
|
2982
|
+
if (isRouteRequest) {
|
|
2983
|
+
// eslint-disable-next-line no-throw-literal
|
|
2984
|
+
throw {
|
|
2985
|
+
type: resultType || ResultType.data,
|
|
2986
|
+
response: result
|
|
2987
|
+
};
|
|
2858
2988
|
}
|
|
2859
2989
|
|
|
2860
2990
|
let data;
|
|
@@ -3070,10 +3200,10 @@ function findNearestBoundary(matches, routeId) {
|
|
|
3070
3200
|
return eligibleMatches.reverse().find(m => m.route.hasErrorBoundary === true) || matches[0];
|
|
3071
3201
|
}
|
|
3072
3202
|
|
|
3073
|
-
function
|
|
3203
|
+
function getShortCircuitMatches(routes, status, statusText) {
|
|
3074
3204
|
// Prefer a root layout route if present, otherwise shim in a route object
|
|
3075
|
-
let route = routes.find(r => r.index || r.path
|
|
3076
|
-
id: "__shim-
|
|
3205
|
+
let route = routes.find(r => r.index || !r.path || r.path === "/") || {
|
|
3206
|
+
id: "__shim-" + status + "-route__"
|
|
3077
3207
|
};
|
|
3078
3208
|
return {
|
|
3079
3209
|
matches: [{
|
|
@@ -3083,16 +3213,24 @@ function getNotFoundMatches(routes) {
|
|
|
3083
3213
|
route
|
|
3084
3214
|
}],
|
|
3085
3215
|
route,
|
|
3086
|
-
error: new ErrorResponse(
|
|
3216
|
+
error: new ErrorResponse(status, statusText, null)
|
|
3087
3217
|
};
|
|
3088
3218
|
}
|
|
3089
3219
|
|
|
3220
|
+
function getNotFoundMatches(routes) {
|
|
3221
|
+
return getShortCircuitMatches(routes, 404, "Not Found");
|
|
3222
|
+
}
|
|
3223
|
+
|
|
3224
|
+
function getMethodNotAllowedMatches(routes) {
|
|
3225
|
+
return getShortCircuitMatches(routes, 405, "Method Not Allowed");
|
|
3226
|
+
}
|
|
3227
|
+
|
|
3090
3228
|
function getMethodNotAllowedResult(path) {
|
|
3091
|
-
let href = typeof path === "string" ? path :
|
|
3229
|
+
let href = typeof path === "string" ? path : createServerHref(path);
|
|
3092
3230
|
console.warn("You're trying to submit to a route that does not have an action. To " + "fix this, please add an `action` function to the route for " + ("[" + href + "]"));
|
|
3093
3231
|
return {
|
|
3094
3232
|
type: ResultType.error,
|
|
3095
|
-
error: new ErrorResponse(405, "Method Not Allowed", "
|
|
3233
|
+
error: new ErrorResponse(405, "Method Not Allowed", "")
|
|
3096
3234
|
};
|
|
3097
3235
|
} // Find any returned redirect errors, starting from the lowest match
|
|
3098
3236
|
|
|
@@ -3108,7 +3246,7 @@ function findRedirect(results) {
|
|
|
3108
3246
|
} // Create an href to represent a "server" URL without the hash
|
|
3109
3247
|
|
|
3110
3248
|
|
|
3111
|
-
function
|
|
3249
|
+
function createServerHref(location) {
|
|
3112
3250
|
return (location.pathname || "") + (location.search || "");
|
|
3113
3251
|
}
|
|
3114
3252
|
|
|
@@ -3128,6 +3266,20 @@ function isRedirectResult(result) {
|
|
|
3128
3266
|
return (result && result.type) === ResultType.redirect;
|
|
3129
3267
|
}
|
|
3130
3268
|
|
|
3269
|
+
function isRedirectResponse(result) {
|
|
3270
|
+
if (!(result instanceof Response)) {
|
|
3271
|
+
return false;
|
|
3272
|
+
}
|
|
3273
|
+
|
|
3274
|
+
let status = result.status;
|
|
3275
|
+
let location = result.headers.get("Location");
|
|
3276
|
+
return status >= 300 && status <= 399 && location != null;
|
|
3277
|
+
}
|
|
3278
|
+
|
|
3279
|
+
function isQueryRouteResponse(obj) {
|
|
3280
|
+
return obj && obj.response instanceof Response && (obj.type === ResultType.data || ResultType.error);
|
|
3281
|
+
}
|
|
3282
|
+
|
|
3131
3283
|
async function resolveDeferredResults(currentMatches, matchesToLoad, results, signal, isFetcher, currentLoaderData) {
|
|
3132
3284
|
for (let index = 0; index < results.length; index++) {
|
|
3133
3285
|
let result = results[index];
|
|
@@ -3204,16 +3356,20 @@ function createUseMatchesMatch(match, loaderData) {
|
|
|
3204
3356
|
function getTargetMatch(matches, location) {
|
|
3205
3357
|
let search = typeof location === "string" ? parsePath(location).search : location.search;
|
|
3206
3358
|
|
|
3207
|
-
if (matches[matches.length - 1].route.index &&
|
|
3208
|
-
|
|
3209
|
-
|
|
3359
|
+
if (matches[matches.length - 1].route.index && hasNakedIndexQuery(search || "")) {
|
|
3360
|
+
// Return the leaf index route when index is present
|
|
3361
|
+
return matches[matches.length - 1];
|
|
3362
|
+
} // Otherwise grab the deepest "path contributing" match (ignoring index and
|
|
3363
|
+
// pathless layout routes)
|
|
3364
|
+
|
|
3210
3365
|
|
|
3211
|
-
|
|
3366
|
+
let pathMatches = getPathContributingMatches(matches);
|
|
3367
|
+
return pathMatches[pathMatches.length - 1];
|
|
3212
3368
|
}
|
|
3213
3369
|
|
|
3214
3370
|
function createURL(location) {
|
|
3215
3371
|
let base = typeof window !== "undefined" && typeof window.location !== "undefined" ? window.location.origin : "unknown://unknown";
|
|
3216
|
-
let href = typeof location === "string" ? location :
|
|
3372
|
+
let href = typeof location === "string" ? location : createServerHref(location);
|
|
3217
3373
|
return new URL(href, base);
|
|
3218
3374
|
} //#endregion
|
|
3219
3375
|
|
|
@@ -3222,6 +3378,7 @@ exports.ErrorResponse = ErrorResponse;
|
|
|
3222
3378
|
exports.IDLE_FETCHER = IDLE_FETCHER;
|
|
3223
3379
|
exports.IDLE_NAVIGATION = IDLE_NAVIGATION;
|
|
3224
3380
|
exports.UNSAFE_convertRoutesToDataRoutes = convertRoutesToDataRoutes;
|
|
3381
|
+
exports.UNSAFE_getPathContributingMatches = getPathContributingMatches;
|
|
3225
3382
|
exports.createBrowserHistory = createBrowserHistory;
|
|
3226
3383
|
exports.createHashHistory = createHashHistory;
|
|
3227
3384
|
exports.createMemoryHistory = createMemoryHistory;
|