@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.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
|
*
|
|
@@ -390,7 +390,7 @@ function getUrlBasedHistory(getLocation, createHref, validateLocation, options)
|
|
|
390
390
|
if (v5Compat && listener) {
|
|
391
391
|
listener({
|
|
392
392
|
action,
|
|
393
|
-
location
|
|
393
|
+
location: history.location
|
|
394
394
|
});
|
|
395
395
|
}
|
|
396
396
|
}
|
|
@@ -406,7 +406,7 @@ function getUrlBasedHistory(getLocation, createHref, validateLocation, options)
|
|
|
406
406
|
if (v5Compat && listener) {
|
|
407
407
|
listener({
|
|
408
408
|
action,
|
|
409
|
-
location: location
|
|
409
|
+
location: history.location
|
|
410
410
|
});
|
|
411
411
|
}
|
|
412
412
|
}
|
|
@@ -518,7 +518,10 @@ function matchRoutes(routes, locationArg, basename) {
|
|
|
518
518
|
let matches = null;
|
|
519
519
|
|
|
520
520
|
for (let i = 0; matches == null && i < branches.length; ++i) {
|
|
521
|
-
matches = matchRouteBranch(branches[i],
|
|
521
|
+
matches = matchRouteBranch(branches[i], // incoming pathnames are always encoded from either window.location or
|
|
522
|
+
// from route.navigate, but we want to match against the unencoded paths
|
|
523
|
+
// in the route definitions
|
|
524
|
+
safelyDecodeURI(pathname));
|
|
522
525
|
}
|
|
523
526
|
|
|
524
527
|
return matches;
|
|
@@ -762,6 +765,15 @@ function compilePath(path, caseSensitive, end) {
|
|
|
762
765
|
return [matcher, paramNames];
|
|
763
766
|
}
|
|
764
767
|
|
|
768
|
+
function safelyDecodeURI(value) {
|
|
769
|
+
try {
|
|
770
|
+
return decodeURI(value);
|
|
771
|
+
} catch (error) {
|
|
772
|
+
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 + ")."));
|
|
773
|
+
return value;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
765
777
|
function safelyDecodeURIComponent(value, paramName) {
|
|
766
778
|
try {
|
|
767
779
|
return decodeURIComponent(value);
|
|
@@ -861,9 +873,36 @@ function getInvalidPathError(char, field, dest, path) {
|
|
|
861
873
|
}
|
|
862
874
|
/**
|
|
863
875
|
* @private
|
|
876
|
+
*
|
|
877
|
+
* When processing relative navigation we want to ignore ancestor routes that
|
|
878
|
+
* do not contribute to the path, such that index/pathless layout routes don't
|
|
879
|
+
* interfere.
|
|
880
|
+
*
|
|
881
|
+
* For example, when moving a route element into an index route and/or a
|
|
882
|
+
* pathless layout route, relative link behavior contained within should stay
|
|
883
|
+
* the same. Both of the following examples should link back to the root:
|
|
884
|
+
*
|
|
885
|
+
* <Route path="/">
|
|
886
|
+
* <Route path="accounts" element={<Link to=".."}>
|
|
887
|
+
* </Route>
|
|
888
|
+
*
|
|
889
|
+
* <Route path="/">
|
|
890
|
+
* <Route path="accounts">
|
|
891
|
+
* <Route element={<AccountsLayout />}> // <-- Does not contribute
|
|
892
|
+
* <Route index element={<Link to=".."} /> // <-- Does not contribute
|
|
893
|
+
* </Route
|
|
894
|
+
* </Route>
|
|
895
|
+
* </Route>
|
|
864
896
|
*/
|
|
865
897
|
|
|
866
898
|
|
|
899
|
+
function getPathContributingMatches(matches) {
|
|
900
|
+
return matches.filter((match, index) => index === 0 || match.route.path && match.route.path.length > 0);
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* @private
|
|
904
|
+
*/
|
|
905
|
+
|
|
867
906
|
function resolveTo(toArg, routePathnames, locationPathname, isPathRelative) {
|
|
868
907
|
if (isPathRelative === void 0) {
|
|
869
908
|
isPathRelative = false;
|
|
@@ -1184,7 +1223,9 @@ const IDLE_FETCHER = {
|
|
|
1184
1223
|
formAction: undefined,
|
|
1185
1224
|
formEncType: undefined,
|
|
1186
1225
|
formData: undefined
|
|
1187
|
-
};
|
|
1226
|
+
};
|
|
1227
|
+
const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined";
|
|
1228
|
+
const isServer = !isBrowser; //#endregion
|
|
1188
1229
|
////////////////////////////////////////////////////////////////////////////////
|
|
1189
1230
|
//#region createRouter
|
|
1190
1231
|
////////////////////////////////////////////////////////////////////////////////
|
|
@@ -1391,7 +1432,18 @@ function createRouter(init) {
|
|
|
1391
1432
|
submission,
|
|
1392
1433
|
error
|
|
1393
1434
|
} = normalizeNavigateOptions(to, opts);
|
|
1394
|
-
let location = createLocation(state.location, path, opts && opts.state);
|
|
1435
|
+
let location = createLocation(state.location, path, opts && opts.state); // When using navigate as a PUSH/REPLACE we aren't reading an already-encoded
|
|
1436
|
+
// URL from window.location, so we need to encode it here so the behavior
|
|
1437
|
+
// remains the same as POP and non-data-router usages. new URL() does all
|
|
1438
|
+
// the same encoding we'd get from a history.pushState/window.location read
|
|
1439
|
+
// without having to touch history
|
|
1440
|
+
|
|
1441
|
+
let url = createURL(createPath(location));
|
|
1442
|
+
location = _extends({}, location, {
|
|
1443
|
+
pathname: url.pathname,
|
|
1444
|
+
search: url.search,
|
|
1445
|
+
hash: url.hash
|
|
1446
|
+
});
|
|
1395
1447
|
let historyAction = (opts && opts.replace) === true || submission != null ? Action.Replace : Action.Push;
|
|
1396
1448
|
let preventScrollReset = opts && "preventScrollReset" in opts ? opts.preventScrollReset === true : undefined;
|
|
1397
1449
|
return await startNavigation(historyAction, location, {
|
|
@@ -1557,7 +1609,7 @@ function createRouter(init) {
|
|
|
1557
1609
|
if (!actionMatch.route.action) {
|
|
1558
1610
|
result = getMethodNotAllowedResult(location);
|
|
1559
1611
|
} else {
|
|
1560
|
-
result = await callLoaderOrAction("action", request, actionMatch);
|
|
1612
|
+
result = await callLoaderOrAction("action", request, actionMatch, matches, router.basename);
|
|
1561
1613
|
|
|
1562
1614
|
if (request.signal.aborted) {
|
|
1563
1615
|
return {
|
|
@@ -1652,7 +1704,7 @@ function createRouter(init) {
|
|
|
1652
1704
|
if (!isUninterruptedRevalidation) {
|
|
1653
1705
|
revalidatingFetchers.forEach(_ref2 => {
|
|
1654
1706
|
let [key] = _ref2;
|
|
1655
|
-
|
|
1707
|
+
let fetcher = state.fetchers.get(key);
|
|
1656
1708
|
let revalidatingFetcher = {
|
|
1657
1709
|
state: "loading",
|
|
1658
1710
|
data: fetcher && fetcher.data,
|
|
@@ -1680,7 +1732,7 @@ function createRouter(init) {
|
|
|
1680
1732
|
results,
|
|
1681
1733
|
loaderResults,
|
|
1682
1734
|
fetcherResults
|
|
1683
|
-
} = await callLoadersAndMaybeResolveData(state.matches, matchesToLoad, revalidatingFetchers, request);
|
|
1735
|
+
} = await callLoadersAndMaybeResolveData(state.matches, matches, matchesToLoad, revalidatingFetchers, request);
|
|
1684
1736
|
|
|
1685
1737
|
if (request.signal.aborted) {
|
|
1686
1738
|
return {
|
|
@@ -1738,7 +1790,7 @@ function createRouter(init) {
|
|
|
1738
1790
|
|
|
1739
1791
|
|
|
1740
1792
|
function fetch(key, routeId, href, opts) {
|
|
1741
|
-
if (
|
|
1793
|
+
if (isServer) {
|
|
1742
1794
|
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.");
|
|
1743
1795
|
}
|
|
1744
1796
|
|
|
@@ -1757,19 +1809,19 @@ function createRouter(init) {
|
|
|
1757
1809
|
let match = getTargetMatch(matches, path);
|
|
1758
1810
|
|
|
1759
1811
|
if (submission) {
|
|
1760
|
-
handleFetcherAction(key, routeId, path, match, submission);
|
|
1812
|
+
handleFetcherAction(key, routeId, path, match, matches, submission);
|
|
1761
1813
|
return;
|
|
1762
1814
|
} // Store off the match so we can call it's shouldRevalidate on subsequent
|
|
1763
1815
|
// revalidations
|
|
1764
1816
|
|
|
1765
1817
|
|
|
1766
|
-
fetchLoadMatches.set(key, [path, match]);
|
|
1767
|
-
handleFetcherLoader(key, routeId, path, match);
|
|
1818
|
+
fetchLoadMatches.set(key, [path, match, matches]);
|
|
1819
|
+
handleFetcherLoader(key, routeId, path, match, matches);
|
|
1768
1820
|
} // Call the action for the matched fetcher.submit(), and then handle redirects,
|
|
1769
1821
|
// errors, and revalidation
|
|
1770
1822
|
|
|
1771
1823
|
|
|
1772
|
-
async function handleFetcherAction(key, routeId, path, match, submission) {
|
|
1824
|
+
async function handleFetcherAction(key, routeId, path, match, requestMatches, submission) {
|
|
1773
1825
|
interruptActiveLoads();
|
|
1774
1826
|
fetchLoadMatches.delete(key);
|
|
1775
1827
|
|
|
@@ -1798,7 +1850,7 @@ function createRouter(init) {
|
|
|
1798
1850
|
let abortController = new AbortController();
|
|
1799
1851
|
let fetchRequest = createRequest(path, abortController.signal, submission);
|
|
1800
1852
|
fetchControllers.set(key, abortController);
|
|
1801
|
-
let actionResult = await callLoaderOrAction("action", fetchRequest, match);
|
|
1853
|
+
let actionResult = await callLoaderOrAction("action", fetchRequest, match, requestMatches, router.basename);
|
|
1802
1854
|
|
|
1803
1855
|
if (fetchRequest.signal.aborted) {
|
|
1804
1856
|
// We can delete this so long as we weren't aborted by ou our own fetcher
|
|
@@ -1890,7 +1942,7 @@ function createRouter(init) {
|
|
|
1890
1942
|
results,
|
|
1891
1943
|
loaderResults,
|
|
1892
1944
|
fetcherResults
|
|
1893
|
-
} = await callLoadersAndMaybeResolveData(state.matches, matchesToLoad, revalidatingFetchers, revalidationRequest);
|
|
1945
|
+
} = await callLoadersAndMaybeResolveData(state.matches, matches, matchesToLoad, revalidatingFetchers, revalidationRequest);
|
|
1894
1946
|
|
|
1895
1947
|
if (abortController.signal.aborted) {
|
|
1896
1948
|
return;
|
|
@@ -1952,7 +2004,7 @@ function createRouter(init) {
|
|
|
1952
2004
|
} // Call the matched loader for fetcher.load(), handling redirects, errors, etc.
|
|
1953
2005
|
|
|
1954
2006
|
|
|
1955
|
-
async function handleFetcherLoader(key, routeId, path, match) {
|
|
2007
|
+
async function handleFetcherLoader(key, routeId, path, match, matches) {
|
|
1956
2008
|
let existingFetcher = state.fetchers.get(key); // Put this fetcher into it's loading state
|
|
1957
2009
|
|
|
1958
2010
|
let loadingFetcher = {
|
|
@@ -1971,7 +2023,7 @@ function createRouter(init) {
|
|
|
1971
2023
|
let abortController = new AbortController();
|
|
1972
2024
|
let fetchRequest = createRequest(path, abortController.signal);
|
|
1973
2025
|
fetchControllers.set(key, abortController);
|
|
1974
|
-
let result = await callLoaderOrAction("loader", fetchRequest, match); // Deferred isn't supported or fetcher loads, await everything and treat it
|
|
2026
|
+
let result = await callLoaderOrAction("loader", fetchRequest, match, matches, router.basename); // Deferred isn't supported or fetcher loads, await everything and treat it
|
|
1975
2027
|
// as a normal load. resolveDeferredData will return undefined if this
|
|
1976
2028
|
// fetcher gets aborted, so we just leave result untouched and short circuit
|
|
1977
2029
|
// below if that happens
|
|
@@ -2064,13 +2116,13 @@ function createRouter(init) {
|
|
|
2064
2116
|
});
|
|
2065
2117
|
}
|
|
2066
2118
|
|
|
2067
|
-
async function callLoadersAndMaybeResolveData(currentMatches, matchesToLoad, fetchersToLoad, request) {
|
|
2119
|
+
async function callLoadersAndMaybeResolveData(currentMatches, matches, matchesToLoad, fetchersToLoad, request) {
|
|
2068
2120
|
// Call all navigation loaders and revalidating fetcher loaders in parallel,
|
|
2069
2121
|
// then slice off the results into separate arrays so we can handle them
|
|
2070
2122
|
// accordingly
|
|
2071
|
-
let results = await Promise.all([...matchesToLoad.map(
|
|
2072
|
-
let [, href, match] = _ref8;
|
|
2073
|
-
return callLoaderOrAction("loader", createRequest(href, request.signal), match);
|
|
2123
|
+
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, router.basename)), ...fetchersToLoad.map(_ref8 => {
|
|
2124
|
+
let [, href, match, fetchMatches] = _ref8;
|
|
2125
|
+
return callLoaderOrAction("loader", createRequest(href, request.signal), match, fetchMatches, router.basename);
|
|
2074
2126
|
})]);
|
|
2075
2127
|
let loaderResults = results.slice(0, matchesToLoad.length);
|
|
2076
2128
|
let fetcherResults = results.slice(matchesToLoad.length);
|
|
@@ -2262,7 +2314,9 @@ function createRouter(init) {
|
|
|
2262
2314
|
navigate,
|
|
2263
2315
|
fetch,
|
|
2264
2316
|
revalidate,
|
|
2265
|
-
createHref
|
|
2317
|
+
// Passthrough to history-aware createHref used by useHref so we get proper
|
|
2318
|
+
// hash-aware URLs in DOM paths
|
|
2319
|
+
createHref: to => init.history.createHref(to),
|
|
2266
2320
|
getFetcher,
|
|
2267
2321
|
deleteFetcher,
|
|
2268
2322
|
dispose,
|
|
@@ -2275,15 +2329,75 @@ function createRouter(init) {
|
|
|
2275
2329
|
//#region createStaticHandler
|
|
2276
2330
|
////////////////////////////////////////////////////////////////////////////////
|
|
2277
2331
|
|
|
2332
|
+
const validActionMethods = new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
2333
|
+
const validRequestMethods = new Set(["GET", "HEAD", ...validActionMethods]);
|
|
2278
2334
|
function unstable_createStaticHandler(routes) {
|
|
2279
2335
|
invariant(routes.length > 0, "You must provide a non-empty routes array to unstable_createStaticHandler");
|
|
2280
2336
|
let dataRoutes = convertRoutesToDataRoutes(routes);
|
|
2337
|
+
/**
|
|
2338
|
+
* The query() method is intended for document requests, in which we want to
|
|
2339
|
+
* call an optional action and potentially multiple loaders for all nested
|
|
2340
|
+
* routes. It returns a StaticHandlerContext object, which is very similar
|
|
2341
|
+
* to the router state (location, loaderData, actionData, errors, etc.) and
|
|
2342
|
+
* also adds SSR-specific information such as the statusCode and headers
|
|
2343
|
+
* from action/loaders Responses.
|
|
2344
|
+
*
|
|
2345
|
+
* It _should_ never throw and should report all errors through the
|
|
2346
|
+
* returned context.errors object, properly associating errors to their error
|
|
2347
|
+
* boundary. Additionally, it tracks _deepestRenderedBoundaryId which can be
|
|
2348
|
+
* used to emulate React error boundaries during SSr by performing a second
|
|
2349
|
+
* pass only down to the boundaryId.
|
|
2350
|
+
*
|
|
2351
|
+
* The one exception where we do not return a StaticHandlerContext is when a
|
|
2352
|
+
* redirect response is returned or thrown from any action/loader. We
|
|
2353
|
+
* propagate that out and return the raw Response so the HTTP server can
|
|
2354
|
+
* return it directly.
|
|
2355
|
+
*/
|
|
2281
2356
|
|
|
2282
2357
|
async function query(request) {
|
|
2283
|
-
let
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2358
|
+
let url = new URL(request.url);
|
|
2359
|
+
let location = createLocation("", createPath(url), null, "default");
|
|
2360
|
+
let matches = matchRoutes(dataRoutes, location);
|
|
2361
|
+
|
|
2362
|
+
if (!validRequestMethods.has(request.method)) {
|
|
2363
|
+
let {
|
|
2364
|
+
matches: methodNotAllowedMatches,
|
|
2365
|
+
route,
|
|
2366
|
+
error
|
|
2367
|
+
} = getMethodNotAllowedMatches(dataRoutes);
|
|
2368
|
+
return {
|
|
2369
|
+
location,
|
|
2370
|
+
matches: methodNotAllowedMatches,
|
|
2371
|
+
loaderData: {},
|
|
2372
|
+
actionData: null,
|
|
2373
|
+
errors: {
|
|
2374
|
+
[route.id]: error
|
|
2375
|
+
},
|
|
2376
|
+
statusCode: error.status,
|
|
2377
|
+
loaderHeaders: {},
|
|
2378
|
+
actionHeaders: {}
|
|
2379
|
+
};
|
|
2380
|
+
} else if (!matches) {
|
|
2381
|
+
let {
|
|
2382
|
+
matches: notFoundMatches,
|
|
2383
|
+
route,
|
|
2384
|
+
error
|
|
2385
|
+
} = getNotFoundMatches(dataRoutes);
|
|
2386
|
+
return {
|
|
2387
|
+
location,
|
|
2388
|
+
matches: notFoundMatches,
|
|
2389
|
+
loaderData: {},
|
|
2390
|
+
actionData: null,
|
|
2391
|
+
errors: {
|
|
2392
|
+
[route.id]: error
|
|
2393
|
+
},
|
|
2394
|
+
statusCode: error.status,
|
|
2395
|
+
loaderHeaders: {},
|
|
2396
|
+
actionHeaders: {}
|
|
2397
|
+
};
|
|
2398
|
+
}
|
|
2399
|
+
|
|
2400
|
+
let result = await queryImpl(request, location, matches);
|
|
2287
2401
|
|
|
2288
2402
|
if (result instanceof Response) {
|
|
2289
2403
|
return result;
|
|
@@ -2296,11 +2410,52 @@ function unstable_createStaticHandler(routes) {
|
|
|
2296
2410
|
location
|
|
2297
2411
|
}, result);
|
|
2298
2412
|
}
|
|
2413
|
+
/**
|
|
2414
|
+
* The queryRoute() method is intended for targeted route requests, either
|
|
2415
|
+
* for fetch ?_data requests or resource route requests. In this case, we
|
|
2416
|
+
* are only ever calling a single action or loader, and we are returning the
|
|
2417
|
+
* returned value directly. In most cases, this will be a Response returned
|
|
2418
|
+
* from the action/loader, but it may be a primitive or other value as well -
|
|
2419
|
+
* and in such cases the calling context should handle that accordingly.
|
|
2420
|
+
*
|
|
2421
|
+
* We do respect the throw/return differentiation, so if an action/loader
|
|
2422
|
+
* throws, then this method will throw the value. This is important so we
|
|
2423
|
+
* can do proper boundary identification in Remix where a thrown Response
|
|
2424
|
+
* must go to the Catch Boundary but a returned Response is happy-path.
|
|
2425
|
+
*
|
|
2426
|
+
* One thing to note is that any Router-initiated thrown Response (such as a
|
|
2427
|
+
* 404 or 405) will have a custom X-Remix-Router-Error: "yes" header on it
|
|
2428
|
+
* in order to differentiate from responses thrown from user actions/loaders.
|
|
2429
|
+
*/
|
|
2430
|
+
|
|
2299
2431
|
|
|
2300
2432
|
async function queryRoute(request, routeId) {
|
|
2301
|
-
let
|
|
2302
|
-
|
|
2303
|
-
|
|
2433
|
+
let url = new URL(request.url);
|
|
2434
|
+
let location = createLocation("", createPath(url), null, "default");
|
|
2435
|
+
let matches = matchRoutes(dataRoutes, location);
|
|
2436
|
+
|
|
2437
|
+
if (!validRequestMethods.has(request.method)) {
|
|
2438
|
+
throw createRouterErrorResponse(null, {
|
|
2439
|
+
status: 405,
|
|
2440
|
+
statusText: "Method Not Allowed"
|
|
2441
|
+
});
|
|
2442
|
+
} else if (!matches) {
|
|
2443
|
+
throw createRouterErrorResponse(null, {
|
|
2444
|
+
status: 404,
|
|
2445
|
+
statusText: "Not Found"
|
|
2446
|
+
});
|
|
2447
|
+
}
|
|
2448
|
+
|
|
2449
|
+
let match = routeId ? matches.find(m => m.route.id === routeId) : getTargetMatch(matches, location);
|
|
2450
|
+
|
|
2451
|
+
if (!match) {
|
|
2452
|
+
throw createRouterErrorResponse(null, {
|
|
2453
|
+
status: 404,
|
|
2454
|
+
statusText: "Not Found"
|
|
2455
|
+
});
|
|
2456
|
+
}
|
|
2457
|
+
|
|
2458
|
+
let result = await queryImpl(request, location, matches, match);
|
|
2304
2459
|
|
|
2305
2460
|
if (result instanceof Response) {
|
|
2306
2461
|
return result;
|
|
@@ -2309,77 +2464,48 @@ function unstable_createStaticHandler(routes) {
|
|
|
2309
2464
|
let error = result.errors ? Object.values(result.errors)[0] : undefined;
|
|
2310
2465
|
|
|
2311
2466
|
if (error !== undefined) {
|
|
2312
|
-
//
|
|
2313
|
-
// directly for route requests and prevent the unwrapping into an
|
|
2314
|
-
// ErrorResponse, we still need this for error cases _prior_ the
|
|
2315
|
-
// execution of the loader/action, such as a 404/405 error.
|
|
2316
|
-
if (isRouteErrorResponse(error)) {
|
|
2317
|
-
return new Response(error.data, {
|
|
2318
|
-
status: error.status,
|
|
2319
|
-
statusText: error.statusText
|
|
2320
|
-
});
|
|
2321
|
-
} // If we got back result.errors, that means the loader/action threw
|
|
2467
|
+
// If we got back result.errors, that means the loader/action threw
|
|
2322
2468
|
// _something_ that wasn't a Response, but it's not guaranteed/required
|
|
2323
2469
|
// to be an `instanceof Error` either, so we have to use throw here to
|
|
2324
2470
|
// preserve the "error" state outside of queryImpl.
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
2471
|
throw error;
|
|
2328
2472
|
} // Pick off the right state value to return
|
|
2329
2473
|
|
|
2330
2474
|
|
|
2331
2475
|
let routeData = [result.actionData, result.loaderData].find(v => v);
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
if (isRouteErrorResponse(value)) {
|
|
2335
|
-
return new Response(value.data, {
|
|
2336
|
-
status: value.status,
|
|
2337
|
-
statusText: value.statusText
|
|
2338
|
-
});
|
|
2339
|
-
}
|
|
2340
|
-
|
|
2341
|
-
return value;
|
|
2476
|
+
return Object.values(routeData || {})[0];
|
|
2342
2477
|
}
|
|
2343
2478
|
|
|
2344
|
-
async function queryImpl(request,
|
|
2345
|
-
invariant(request.method !== "HEAD", "query()/queryRoute() do not support HEAD requests");
|
|
2479
|
+
async function queryImpl(request, location, matches, routeMatch) {
|
|
2346
2480
|
invariant(request.signal, "query()/queryRoute() requests must contain an AbortController signal");
|
|
2347
|
-
let {
|
|
2348
|
-
location,
|
|
2349
|
-
matches,
|
|
2350
|
-
shortCircuitState
|
|
2351
|
-
} = matchRequest(request, routeId);
|
|
2352
2481
|
|
|
2353
2482
|
try {
|
|
2354
|
-
if (
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
result: shortCircuitState
|
|
2358
|
-
};
|
|
2483
|
+
if (validActionMethods.has(request.method)) {
|
|
2484
|
+
let result = await submit(request, matches, routeMatch || getTargetMatch(matches, location), routeMatch != null);
|
|
2485
|
+
return result;
|
|
2359
2486
|
}
|
|
2360
2487
|
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
};
|
|
2367
|
-
}
|
|
2368
|
-
|
|
2369
|
-
let result = await loadRouteData(request, matches, routeId != null);
|
|
2370
|
-
return {
|
|
2371
|
-
location,
|
|
2372
|
-
result: _extends({}, result, {
|
|
2373
|
-
actionData: null,
|
|
2374
|
-
actionHeaders: {}
|
|
2375
|
-
})
|
|
2376
|
-
};
|
|
2488
|
+
let result = await loadRouteData(request, matches, routeMatch);
|
|
2489
|
+
return result instanceof Response ? result : _extends({}, result, {
|
|
2490
|
+
actionData: null,
|
|
2491
|
+
actionHeaders: {}
|
|
2492
|
+
});
|
|
2377
2493
|
} catch (e) {
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2494
|
+
// If the user threw/returned a Response in callLoaderOrAction, we throw
|
|
2495
|
+
// it to bail out and then return or throw here based on whether the user
|
|
2496
|
+
// returned or threw
|
|
2497
|
+
if (isQueryRouteResponse(e)) {
|
|
2498
|
+
if (e.type === ResultType.error && !isRedirectResponse(e.response)) {
|
|
2499
|
+
throw e.response;
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
return e.response;
|
|
2503
|
+
} // Redirects are always returned since they don't propagate to catch
|
|
2504
|
+
// boundaries
|
|
2505
|
+
|
|
2506
|
+
|
|
2507
|
+
if (isRedirectResponse(e)) {
|
|
2508
|
+
return e;
|
|
2383
2509
|
}
|
|
2384
2510
|
|
|
2385
2511
|
throw e;
|
|
@@ -2390,10 +2516,19 @@ function unstable_createStaticHandler(routes) {
|
|
|
2390
2516
|
let result;
|
|
2391
2517
|
|
|
2392
2518
|
if (!actionMatch.route.action) {
|
|
2393
|
-
let href =
|
|
2519
|
+
let href = createServerHref(new URL(request.url));
|
|
2520
|
+
|
|
2521
|
+
if (isRouteRequest) {
|
|
2522
|
+
throw createRouterErrorResponse(null, {
|
|
2523
|
+
status: 405,
|
|
2524
|
+
statusText: "Method Not Allowed"
|
|
2525
|
+
});
|
|
2526
|
+
}
|
|
2527
|
+
|
|
2394
2528
|
result = getMethodNotAllowedResult(href);
|
|
2395
2529
|
} else {
|
|
2396
|
-
result = await callLoaderOrAction("action", request, actionMatch,
|
|
2530
|
+
result = await callLoaderOrAction("action", request, actionMatch, matches, undefined, // Basename not currently supported in static handlers
|
|
2531
|
+
true, isRouteRequest);
|
|
2397
2532
|
|
|
2398
2533
|
if (request.signal.aborted) {
|
|
2399
2534
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
@@ -2403,7 +2538,7 @@ function unstable_createStaticHandler(routes) {
|
|
|
2403
2538
|
|
|
2404
2539
|
if (isRedirectResult(result)) {
|
|
2405
2540
|
// Uhhhh - this should never happen, we should always throw these from
|
|
2406
|
-
//
|
|
2541
|
+
// callLoaderOrAction, but the type narrowing here keeps TS happy and we
|
|
2407
2542
|
// can get back on the "throw all redirect responses" train here should
|
|
2408
2543
|
// this ever happen :/
|
|
2409
2544
|
throw new Response(null, {
|
|
@@ -2419,6 +2554,8 @@ function unstable_createStaticHandler(routes) {
|
|
|
2419
2554
|
}
|
|
2420
2555
|
|
|
2421
2556
|
if (isRouteRequest) {
|
|
2557
|
+
// Note: This should only be non-Response values if we get here, since
|
|
2558
|
+
// isRouteRequest should throw any Response received in callLoaderOrAction
|
|
2422
2559
|
if (isErrorResult(result)) {
|
|
2423
2560
|
let boundaryMatch = findNearestBoundary(matches, actionMatch.route.id);
|
|
2424
2561
|
return {
|
|
@@ -2455,7 +2592,7 @@ function unstable_createStaticHandler(routes) {
|
|
|
2455
2592
|
// Store off the pending error - we use it to determine which loaders
|
|
2456
2593
|
// to call and will commit it when we complete the navigation
|
|
2457
2594
|
let boundaryMatch = findNearestBoundary(matches, actionMatch.route.id);
|
|
2458
|
-
let context = await loadRouteData(request, matches,
|
|
2595
|
+
let context = await loadRouteData(request, matches, undefined, {
|
|
2459
2596
|
[boundaryMatch.route.id]: result.error
|
|
2460
2597
|
}); // action status codes take precedence over loader status codes
|
|
2461
2598
|
|
|
@@ -2468,7 +2605,7 @@ function unstable_createStaticHandler(routes) {
|
|
|
2468
2605
|
});
|
|
2469
2606
|
}
|
|
2470
2607
|
|
|
2471
|
-
let context = await loadRouteData(request, matches
|
|
2608
|
+
let context = await loadRouteData(request, matches);
|
|
2472
2609
|
return _extends({}, context, result.statusCode ? {
|
|
2473
2610
|
statusCode: result.statusCode
|
|
2474
2611
|
} : {}, {
|
|
@@ -2481,8 +2618,10 @@ function unstable_createStaticHandler(routes) {
|
|
|
2481
2618
|
});
|
|
2482
2619
|
}
|
|
2483
2620
|
|
|
2484
|
-
async function loadRouteData(request, matches,
|
|
2485
|
-
let
|
|
2621
|
+
async function loadRouteData(request, matches, routeMatch, pendingActionError) {
|
|
2622
|
+
let isRouteRequest = routeMatch != null;
|
|
2623
|
+
let requestMatches = routeMatch ? [routeMatch] : getLoaderMatchesUntilBoundary(matches, Object.keys(pendingActionError || {})[0]);
|
|
2624
|
+
let matchesToLoad = requestMatches.filter(m => m.route.loader); // Short circuit if we have no loaders to run
|
|
2486
2625
|
|
|
2487
2626
|
if (matchesToLoad.length === 0) {
|
|
2488
2627
|
return {
|
|
@@ -2494,7 +2633,8 @@ function unstable_createStaticHandler(routes) {
|
|
|
2494
2633
|
};
|
|
2495
2634
|
}
|
|
2496
2635
|
|
|
2497
|
-
let results = await Promise.all([...matchesToLoad.map(
|
|
2636
|
+
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, undefined, // Basename not currently supported in static handlers
|
|
2637
|
+
true, isRouteRequest))]);
|
|
2498
2638
|
|
|
2499
2639
|
if (request.signal.aborted) {
|
|
2500
2640
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
@@ -2515,43 +2655,12 @@ function unstable_createStaticHandler(routes) {
|
|
|
2515
2655
|
});
|
|
2516
2656
|
}
|
|
2517
2657
|
|
|
2518
|
-
function
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
matches = matches.filter(m => m.route.id === routeId);
|
|
2525
|
-
} // Short circuit with a 404 if we match nothing
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
if (!matches) {
|
|
2529
|
-
let {
|
|
2530
|
-
matches: notFoundMatches,
|
|
2531
|
-
route,
|
|
2532
|
-
error
|
|
2533
|
-
} = getNotFoundMatches(dataRoutes);
|
|
2534
|
-
return {
|
|
2535
|
-
location,
|
|
2536
|
-
matches: notFoundMatches,
|
|
2537
|
-
shortCircuitState: {
|
|
2538
|
-
matches: notFoundMatches,
|
|
2539
|
-
loaderData: {},
|
|
2540
|
-
actionData: null,
|
|
2541
|
-
errors: {
|
|
2542
|
-
[route.id]: error
|
|
2543
|
-
},
|
|
2544
|
-
statusCode: 404,
|
|
2545
|
-
loaderHeaders: {},
|
|
2546
|
-
actionHeaders: {}
|
|
2547
|
-
}
|
|
2548
|
-
};
|
|
2549
|
-
}
|
|
2550
|
-
|
|
2551
|
-
return {
|
|
2552
|
-
location,
|
|
2553
|
-
matches
|
|
2554
|
-
};
|
|
2658
|
+
function createRouterErrorResponse(body, init) {
|
|
2659
|
+
return new Response(body, _extends({}, init, {
|
|
2660
|
+
headers: _extends({}, init.headers, {
|
|
2661
|
+
"X-Remix-Router-Error": "yes"
|
|
2662
|
+
})
|
|
2663
|
+
}));
|
|
2555
2664
|
}
|
|
2556
2665
|
|
|
2557
2666
|
return {
|
|
@@ -2600,7 +2709,7 @@ function normalizeNavigateOptions(to, opts, isFetcher) {
|
|
|
2600
2709
|
path,
|
|
2601
2710
|
submission: {
|
|
2602
2711
|
formMethod: opts.formMethod,
|
|
2603
|
-
formAction:
|
|
2712
|
+
formAction: createServerHref(parsePath(path)),
|
|
2604
2713
|
formEncType: opts && opts.formEncType || "application/x-www-form-urlencoded",
|
|
2605
2714
|
formData: opts.formData
|
|
2606
2715
|
}
|
|
@@ -2683,16 +2792,16 @@ function getMatchesToLoad(state, matches, submission, location, isRevalidationRe
|
|
|
2683
2792
|
|
|
2684
2793
|
let revalidatingFetchers = [];
|
|
2685
2794
|
fetchLoadMatches && fetchLoadMatches.forEach((_ref10, key) => {
|
|
2686
|
-
let [href, match] = _ref10;
|
|
2795
|
+
let [href, match, fetchMatches] = _ref10;
|
|
2687
2796
|
|
|
2688
2797
|
// This fetcher was cancelled from a prior action submission - force reload
|
|
2689
2798
|
if (cancelledFetcherLoads.includes(key)) {
|
|
2690
|
-
revalidatingFetchers.push([key, href, match]);
|
|
2799
|
+
revalidatingFetchers.push([key, href, match, fetchMatches]);
|
|
2691
2800
|
} else if (isRevalidationRequired) {
|
|
2692
2801
|
let shouldRevalidate = shouldRevalidateLoader(href, match, submission, href, match, isRevalidationRequired, actionResult);
|
|
2693
2802
|
|
|
2694
2803
|
if (shouldRevalidate) {
|
|
2695
|
-
revalidatingFetchers.push([key, href, match]);
|
|
2804
|
+
revalidatingFetchers.push([key, href, match, fetchMatches]);
|
|
2696
2805
|
}
|
|
2697
2806
|
}
|
|
2698
2807
|
});
|
|
@@ -2754,9 +2863,9 @@ function shouldRevalidateLoader(currentLocation, currentMatch, submission, locat
|
|
|
2754
2863
|
return defaultShouldRevalidate;
|
|
2755
2864
|
}
|
|
2756
2865
|
|
|
2757
|
-
async function callLoaderOrAction(type, request, match,
|
|
2758
|
-
if (
|
|
2759
|
-
|
|
2866
|
+
async function callLoaderOrAction(type, request, match, matches, basename, isStaticRequest, isRouteRequest) {
|
|
2867
|
+
if (isStaticRequest === void 0) {
|
|
2868
|
+
isStaticRequest = false;
|
|
2760
2869
|
}
|
|
2761
2870
|
|
|
2762
2871
|
if (isRouteRequest === void 0) {
|
|
@@ -2788,20 +2897,30 @@ async function callLoaderOrAction(type, request, match, skipRedirects, isRouteRe
|
|
|
2788
2897
|
}
|
|
2789
2898
|
|
|
2790
2899
|
if (result instanceof Response) {
|
|
2791
|
-
// Process redirects
|
|
2792
|
-
let status = result.status;
|
|
2793
|
-
let location = result.headers.get("Location"); // For SSR single-route requests, we want to hand Responses back directly
|
|
2794
|
-
// without unwrapping
|
|
2900
|
+
let status = result.status; // Process redirects
|
|
2795
2901
|
|
|
2796
|
-
if (
|
|
2797
|
-
|
|
2798
|
-
|
|
2902
|
+
if (status >= 300 && status <= 399) {
|
|
2903
|
+
let location = result.headers.get("Location");
|
|
2904
|
+
invariant(location, "Redirects returned/thrown from loaders/actions must have a Location header"); // Support relative routing in redirects
|
|
2799
2905
|
|
|
2800
|
-
|
|
2801
|
-
|
|
2906
|
+
let activeMatches = matches.slice(0, matches.indexOf(match) + 1);
|
|
2907
|
+
let routePathnames = getPathContributingMatches(activeMatches).map(match => match.pathnameBase);
|
|
2908
|
+
let requestPath = createURL(request.url).pathname;
|
|
2909
|
+
let resolvedLocation = resolveTo(location, routePathnames, requestPath);
|
|
2910
|
+
invariant(createPath(resolvedLocation), "Unable to resolve redirect location: " + result.headers.get("Location")); // Prepend the basename to the redirect location if we have one
|
|
2911
|
+
|
|
2912
|
+
if (basename) {
|
|
2913
|
+
let path = resolvedLocation.pathname;
|
|
2914
|
+
resolvedLocation.pathname = path === "/" ? basename : joinPaths([basename, path]);
|
|
2915
|
+
}
|
|
2916
|
+
|
|
2917
|
+
location = createPath(resolvedLocation); // Don't process redirects in the router during static requests requests.
|
|
2802
2918
|
// Instead, throw the Response and let the server handle it with an HTTP
|
|
2803
|
-
// redirect
|
|
2804
|
-
|
|
2919
|
+
// redirect. We also update the Location header in place in this flow so
|
|
2920
|
+
// basename and relative routing is taken into account
|
|
2921
|
+
|
|
2922
|
+
if (isStaticRequest) {
|
|
2923
|
+
result.headers.set("Location", location);
|
|
2805
2924
|
throw result;
|
|
2806
2925
|
}
|
|
2807
2926
|
|
|
@@ -2811,6 +2930,17 @@ async function callLoaderOrAction(type, request, match, skipRedirects, isRouteRe
|
|
|
2811
2930
|
location,
|
|
2812
2931
|
revalidate: result.headers.get("X-Remix-Revalidate") !== null
|
|
2813
2932
|
};
|
|
2933
|
+
} // For SSR single-route requests, we want to hand Responses back directly
|
|
2934
|
+
// without unwrapping. We do this with the QueryRouteResponse wrapper
|
|
2935
|
+
// interface so we can know whether it was returned or thrown
|
|
2936
|
+
|
|
2937
|
+
|
|
2938
|
+
if (isRouteRequest) {
|
|
2939
|
+
// eslint-disable-next-line no-throw-literal
|
|
2940
|
+
throw {
|
|
2941
|
+
type: resultType || ResultType.data,
|
|
2942
|
+
response: result
|
|
2943
|
+
};
|
|
2814
2944
|
}
|
|
2815
2945
|
|
|
2816
2946
|
let data;
|
|
@@ -3026,10 +3156,10 @@ function findNearestBoundary(matches, routeId) {
|
|
|
3026
3156
|
return eligibleMatches.reverse().find(m => m.route.hasErrorBoundary === true) || matches[0];
|
|
3027
3157
|
}
|
|
3028
3158
|
|
|
3029
|
-
function
|
|
3159
|
+
function getShortCircuitMatches(routes, status, statusText) {
|
|
3030
3160
|
// Prefer a root layout route if present, otherwise shim in a route object
|
|
3031
|
-
let route = routes.find(r => r.index || r.path
|
|
3032
|
-
id: "__shim-
|
|
3161
|
+
let route = routes.find(r => r.index || !r.path || r.path === "/") || {
|
|
3162
|
+
id: "__shim-" + status + "-route__"
|
|
3033
3163
|
};
|
|
3034
3164
|
return {
|
|
3035
3165
|
matches: [{
|
|
@@ -3039,16 +3169,24 @@ function getNotFoundMatches(routes) {
|
|
|
3039
3169
|
route
|
|
3040
3170
|
}],
|
|
3041
3171
|
route,
|
|
3042
|
-
error: new ErrorResponse(
|
|
3172
|
+
error: new ErrorResponse(status, statusText, null)
|
|
3043
3173
|
};
|
|
3044
3174
|
}
|
|
3045
3175
|
|
|
3176
|
+
function getNotFoundMatches(routes) {
|
|
3177
|
+
return getShortCircuitMatches(routes, 404, "Not Found");
|
|
3178
|
+
}
|
|
3179
|
+
|
|
3180
|
+
function getMethodNotAllowedMatches(routes) {
|
|
3181
|
+
return getShortCircuitMatches(routes, 405, "Method Not Allowed");
|
|
3182
|
+
}
|
|
3183
|
+
|
|
3046
3184
|
function getMethodNotAllowedResult(path) {
|
|
3047
|
-
let href = typeof path === "string" ? path :
|
|
3185
|
+
let href = typeof path === "string" ? path : createServerHref(path);
|
|
3048
3186
|
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 + "]"));
|
|
3049
3187
|
return {
|
|
3050
3188
|
type: ResultType.error,
|
|
3051
|
-
error: new ErrorResponse(405, "Method Not Allowed", "
|
|
3189
|
+
error: new ErrorResponse(405, "Method Not Allowed", "")
|
|
3052
3190
|
};
|
|
3053
3191
|
} // Find any returned redirect errors, starting from the lowest match
|
|
3054
3192
|
|
|
@@ -3064,7 +3202,7 @@ function findRedirect(results) {
|
|
|
3064
3202
|
} // Create an href to represent a "server" URL without the hash
|
|
3065
3203
|
|
|
3066
3204
|
|
|
3067
|
-
function
|
|
3205
|
+
function createServerHref(location) {
|
|
3068
3206
|
return (location.pathname || "") + (location.search || "");
|
|
3069
3207
|
}
|
|
3070
3208
|
|
|
@@ -3084,6 +3222,20 @@ function isRedirectResult(result) {
|
|
|
3084
3222
|
return (result && result.type) === ResultType.redirect;
|
|
3085
3223
|
}
|
|
3086
3224
|
|
|
3225
|
+
function isRedirectResponse(result) {
|
|
3226
|
+
if (!(result instanceof Response)) {
|
|
3227
|
+
return false;
|
|
3228
|
+
}
|
|
3229
|
+
|
|
3230
|
+
let status = result.status;
|
|
3231
|
+
let location = result.headers.get("Location");
|
|
3232
|
+
return status >= 300 && status <= 399 && location != null;
|
|
3233
|
+
}
|
|
3234
|
+
|
|
3235
|
+
function isQueryRouteResponse(obj) {
|
|
3236
|
+
return obj && obj.response instanceof Response && (obj.type === ResultType.data || ResultType.error);
|
|
3237
|
+
}
|
|
3238
|
+
|
|
3087
3239
|
async function resolveDeferredResults(currentMatches, matchesToLoad, results, signal, isFetcher, currentLoaderData) {
|
|
3088
3240
|
for (let index = 0; index < results.length; index++) {
|
|
3089
3241
|
let result = results[index];
|
|
@@ -3160,18 +3312,22 @@ function createUseMatchesMatch(match, loaderData) {
|
|
|
3160
3312
|
function getTargetMatch(matches, location) {
|
|
3161
3313
|
let search = typeof location === "string" ? parsePath(location).search : location.search;
|
|
3162
3314
|
|
|
3163
|
-
if (matches[matches.length - 1].route.index &&
|
|
3164
|
-
|
|
3165
|
-
|
|
3315
|
+
if (matches[matches.length - 1].route.index && hasNakedIndexQuery(search || "")) {
|
|
3316
|
+
// Return the leaf index route when index is present
|
|
3317
|
+
return matches[matches.length - 1];
|
|
3318
|
+
} // Otherwise grab the deepest "path contributing" match (ignoring index and
|
|
3319
|
+
// pathless layout routes)
|
|
3320
|
+
|
|
3166
3321
|
|
|
3167
|
-
|
|
3322
|
+
let pathMatches = getPathContributingMatches(matches);
|
|
3323
|
+
return pathMatches[pathMatches.length - 1];
|
|
3168
3324
|
}
|
|
3169
3325
|
|
|
3170
3326
|
function createURL(location) {
|
|
3171
3327
|
let base = typeof window !== "undefined" && typeof window.location !== "undefined" ? window.location.origin : "unknown://unknown";
|
|
3172
|
-
let href = typeof location === "string" ? location :
|
|
3328
|
+
let href = typeof location === "string" ? location : createServerHref(location);
|
|
3173
3329
|
return new URL(href, base);
|
|
3174
3330
|
} //#endregion
|
|
3175
3331
|
|
|
3176
|
-
export { AbortedDeferredError, Action, ErrorResponse, IDLE_FETCHER, IDLE_NAVIGATION, convertRoutesToDataRoutes as UNSAFE_convertRoutesToDataRoutes, createBrowserHistory, createHashHistory, createMemoryHistory, createPath, createRouter, defer, generatePath, getStaticContextFromError, getToPathname, invariant, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, parsePath, redirect, resolvePath, resolveTo, stripBasename, unstable_createStaticHandler, warning };
|
|
3332
|
+
export { AbortedDeferredError, Action, ErrorResponse, IDLE_FETCHER, IDLE_NAVIGATION, convertRoutesToDataRoutes as UNSAFE_convertRoutesToDataRoutes, getPathContributingMatches as UNSAFE_getPathContributingMatches, createBrowserHistory, createHashHistory, createMemoryHistory, createPath, createRouter, defer, generatePath, getStaticContextFromError, getToPathname, invariant, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, parsePath, redirect, resolvePath, resolveTo, stripBasename, unstable_createStaticHandler, warning };
|
|
3177
3333
|
//# sourceMappingURL=router.js.map
|