@remix-run/router 0.0.0-experimental-e960cf1a → 0.0.0-experimental-bc2c864b
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 +199 -8
- package/dist/index.d.ts +2 -2
- package/dist/router.cjs.js +290 -257
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.d.ts +2 -1
- package/dist/router.js +279 -244
- package/dist/router.js.map +1 -1
- package/dist/router.umd.js +290 -257
- 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 +19 -19
- package/index.ts +1 -3
- package/package.json +1 -1
- package/router.ts +453 -405
- package/utils.ts +44 -33
package/dist/router.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @remix-run/router v0.0.0-experimental-
|
|
2
|
+
* @remix-run/router v0.0.0-experimental-bc2c864b
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -653,7 +653,7 @@ function rankRouteBranches(branches) {
|
|
|
653
653
|
branches.sort((a, b) => a.score !== b.score ? b.score - a.score // Higher score first
|
|
654
654
|
: compareIndexes(a.routesMeta.map(meta => meta.childrenIndex), b.routesMeta.map(meta => meta.childrenIndex)));
|
|
655
655
|
}
|
|
656
|
-
const paramRe =
|
|
656
|
+
const paramRe = /^:[\w-]+$/;
|
|
657
657
|
const dynamicSegmentValue = 3;
|
|
658
658
|
const indexRouteValue = 2;
|
|
659
659
|
const emptySegmentValue = 1;
|
|
@@ -740,7 +740,7 @@ function generatePath(originalPath, params) {
|
|
|
740
740
|
// Apply the splat
|
|
741
741
|
return stringify(params[star]);
|
|
742
742
|
}
|
|
743
|
-
const keyMatch = segment.match(/^:(\w+)(\??)$/);
|
|
743
|
+
const keyMatch = segment.match(/^:([\w-]+)(\??)$/);
|
|
744
744
|
if (keyMatch) {
|
|
745
745
|
const [, key, optional] = keyMatch;
|
|
746
746
|
let param = params[key];
|
|
@@ -812,7 +812,7 @@ function compilePath(path, caseSensitive, end) {
|
|
|
812
812
|
let regexpSource = "^" + path.replace(/\/*\*?$/, "") // Ignore trailing / and /*, we'll handle it below
|
|
813
813
|
.replace(/^\/*/, "/") // Make sure it has a leading /
|
|
814
814
|
.replace(/[\\.*+^${}|()[\]]/g, "\\$&") // Escape special regex chars
|
|
815
|
-
.replace(/\/:(\w+)(\?)?/g, (_, paramName, isOptional) => {
|
|
815
|
+
.replace(/\/:([\w-]+)(\?)?/g, (_, paramName, isOptional) => {
|
|
816
816
|
params.push({
|
|
817
817
|
paramName,
|
|
818
818
|
isOptional: isOptional != null
|
|
@@ -1306,8 +1306,6 @@ function createRouter(init) {
|
|
|
1306
1306
|
const isBrowser = typeof routerWindow !== "undefined" && typeof routerWindow.document !== "undefined" && typeof routerWindow.document.createElement !== "undefined";
|
|
1307
1307
|
const isServer = !isBrowser;
|
|
1308
1308
|
invariant(init.routes.length > 0, "You must provide a non-empty routes array to createRouter");
|
|
1309
|
-
const dataStrategy = init.unstable_dataStrategy || defaultDataStrategy;
|
|
1310
|
-
const callLoaderOrAction = createCallLoaderOrAction(dataStrategy);
|
|
1311
1309
|
let mapRouteProperties;
|
|
1312
1310
|
if (init.mapRouteProperties) {
|
|
1313
1311
|
mapRouteProperties = init.mapRouteProperties;
|
|
@@ -1326,6 +1324,7 @@ function createRouter(init) {
|
|
|
1326
1324
|
let dataRoutes = convertRoutesToDataRoutes(init.routes, mapRouteProperties, undefined, manifest);
|
|
1327
1325
|
let inFlightDataRoutes;
|
|
1328
1326
|
let basename = init.basename || "/";
|
|
1327
|
+
let dataStrategyImpl = init.unstable_dataStrategy || defaultDataStrategy;
|
|
1329
1328
|
// Config driven behavior flags
|
|
1330
1329
|
let future = _extends({
|
|
1331
1330
|
v7_fetcherPersist: false,
|
|
@@ -1368,17 +1367,28 @@ function createRouter(init) {
|
|
|
1368
1367
|
[route.id]: error
|
|
1369
1368
|
};
|
|
1370
1369
|
}
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1370
|
+
let initialized;
|
|
1371
|
+
let hasLazyRoutes = initialMatches.some(m => m.route.lazy);
|
|
1372
|
+
let hasLoaders = initialMatches.some(m => m.route.loader);
|
|
1373
|
+
if (hasLazyRoutes) {
|
|
1374
|
+
// All initialMatches need to be loaded before we're ready. If we have lazy
|
|
1375
|
+
// functions around still then we'll need to run them in initialize()
|
|
1376
|
+
initialized = false;
|
|
1377
|
+
} else if (!hasLoaders) {
|
|
1378
|
+
// If we've got no loaders to run, then we're good to go
|
|
1379
|
+
initialized = true;
|
|
1380
|
+
} else if (future.v7_partialHydration) {
|
|
1381
|
+
// If partial hydration is enabled, we're initialized so long as we were
|
|
1382
|
+
// provided with hydrationData for every route with a loader, and no loaders
|
|
1383
|
+
// were marked for explicit hydration
|
|
1384
|
+
let loaderData = init.hydrationData ? init.hydrationData.loaderData : null;
|
|
1385
|
+
let errors = init.hydrationData ? init.hydrationData.errors : null;
|
|
1386
|
+
initialized = initialMatches.every(m => m.route.loader && m.route.loader.hydrate !== true && (loaderData && loaderData[m.route.id] !== undefined || errors && errors[m.route.id] !== undefined));
|
|
1387
|
+
} else {
|
|
1388
|
+
// Without partial hydration - we're initialized if we were provided any
|
|
1389
|
+
// hydrationData - which is expected to be complete
|
|
1390
|
+
initialized = init.hydrationData != null;
|
|
1391
|
+
}
|
|
1382
1392
|
let router;
|
|
1383
1393
|
let state = {
|
|
1384
1394
|
historyAction: init.history.action,
|
|
@@ -1521,7 +1531,7 @@ function createRouter(init) {
|
|
|
1521
1531
|
// in the normal navigation flow. For SSR it's expected that lazy modules are
|
|
1522
1532
|
// resolved prior to router creation since we can't go into a fallbackElement
|
|
1523
1533
|
// UI for SSR'd apps
|
|
1524
|
-
if (!state.initialized
|
|
1534
|
+
if (!state.initialized) {
|
|
1525
1535
|
startNavigation(Action.Pop, state.location, {
|
|
1526
1536
|
initialHydration: true
|
|
1527
1537
|
});
|
|
@@ -1932,7 +1942,8 @@ function createRouter(init) {
|
|
|
1932
1942
|
})
|
|
1933
1943
|
};
|
|
1934
1944
|
} else {
|
|
1935
|
-
|
|
1945
|
+
let results = await callDataStrategy("action", request, [actionMatch], matches);
|
|
1946
|
+
result = results[0];
|
|
1936
1947
|
if (request.signal.aborted) {
|
|
1937
1948
|
return {
|
|
1938
1949
|
shortCircuited: true
|
|
@@ -1947,9 +1958,9 @@ function createRouter(init) {
|
|
|
1947
1958
|
// If the user didn't explicity indicate replace behavior, replace if
|
|
1948
1959
|
// we redirected to the exact same location we're currently at to avoid
|
|
1949
1960
|
// double back-buttons
|
|
1950
|
-
replace = result.
|
|
1961
|
+
replace = result.response.headers.get("Location") === state.location.pathname + state.location.search;
|
|
1951
1962
|
}
|
|
1952
|
-
await startRedirectNavigation(
|
|
1963
|
+
await startRedirectNavigation(request, result, {
|
|
1953
1964
|
submission,
|
|
1954
1965
|
replace
|
|
1955
1966
|
});
|
|
@@ -2065,7 +2076,7 @@ function createRouter(init) {
|
|
|
2065
2076
|
let {
|
|
2066
2077
|
loaderResults,
|
|
2067
2078
|
fetcherResults
|
|
2068
|
-
} = await
|
|
2079
|
+
} = await callLoadersAndMaybeResolveData(state.matches, matches, matchesToLoad, revalidatingFetchers, request);
|
|
2069
2080
|
if (request.signal.aborted) {
|
|
2070
2081
|
return {
|
|
2071
2082
|
shortCircuited: true
|
|
@@ -2088,7 +2099,7 @@ function createRouter(init) {
|
|
|
2088
2099
|
let fetcherKey = revalidatingFetchers[redirect.idx - matchesToLoad.length].key;
|
|
2089
2100
|
fetchRedirectIds.add(fetcherKey);
|
|
2090
2101
|
}
|
|
2091
|
-
await startRedirectNavigation(
|
|
2102
|
+
await startRedirectNavigation(request, redirect.result, {
|
|
2092
2103
|
replace
|
|
2093
2104
|
});
|
|
2094
2105
|
return {
|
|
@@ -2190,7 +2201,8 @@ function createRouter(init) {
|
|
|
2190
2201
|
let fetchRequest = createClientSideRequest(init.history, path, abortController.signal, submission);
|
|
2191
2202
|
fetchControllers.set(key, abortController);
|
|
2192
2203
|
let originatingLoadId = incrementingLoadId;
|
|
2193
|
-
let
|
|
2204
|
+
let actionResults = await callDataStrategy("action", fetchRequest, [match], requestMatches);
|
|
2205
|
+
let actionResult = actionResults[0];
|
|
2194
2206
|
if (fetchRequest.signal.aborted) {
|
|
2195
2207
|
// We can delete this so long as we weren't aborted by our own fetcher
|
|
2196
2208
|
// re-submit which would have put _new_ controller is in fetchControllers
|
|
@@ -2221,7 +2233,7 @@ function createRouter(init) {
|
|
|
2221
2233
|
} else {
|
|
2222
2234
|
fetchRedirectIds.add(key);
|
|
2223
2235
|
updateFetcherState(key, getLoadingFetcher(submission));
|
|
2224
|
-
return startRedirectNavigation(
|
|
2236
|
+
return startRedirectNavigation(fetchRequest, actionResult, {
|
|
2225
2237
|
fetcherSubmission: submission
|
|
2226
2238
|
});
|
|
2227
2239
|
}
|
|
@@ -2275,7 +2287,7 @@ function createRouter(init) {
|
|
|
2275
2287
|
let {
|
|
2276
2288
|
loaderResults,
|
|
2277
2289
|
fetcherResults
|
|
2278
|
-
} = await
|
|
2290
|
+
} = await callLoadersAndMaybeResolveData(state.matches, matches, matchesToLoad, revalidatingFetchers, revalidationRequest);
|
|
2279
2291
|
if (abortController.signal.aborted) {
|
|
2280
2292
|
return;
|
|
2281
2293
|
}
|
|
@@ -2292,7 +2304,7 @@ function createRouter(init) {
|
|
|
2292
2304
|
let fetcherKey = revalidatingFetchers[redirect.idx - matchesToLoad.length].key;
|
|
2293
2305
|
fetchRedirectIds.add(fetcherKey);
|
|
2294
2306
|
}
|
|
2295
|
-
return startRedirectNavigation(
|
|
2307
|
+
return startRedirectNavigation(revalidationRequest, redirect.result);
|
|
2296
2308
|
}
|
|
2297
2309
|
// Process and commit output from loaders
|
|
2298
2310
|
let {
|
|
@@ -2341,7 +2353,8 @@ function createRouter(init) {
|
|
|
2341
2353
|
let fetchRequest = createClientSideRequest(init.history, path, abortController.signal);
|
|
2342
2354
|
fetchControllers.set(key, abortController);
|
|
2343
2355
|
let originatingLoadId = incrementingLoadId;
|
|
2344
|
-
let
|
|
2356
|
+
let results = await callDataStrategy("loader", fetchRequest, [match], matches);
|
|
2357
|
+
let result = results[0];
|
|
2345
2358
|
// Deferred isn't supported for fetcher loads, await everything and treat it
|
|
2346
2359
|
// as a normal load. resolveDeferredData will return undefined if this
|
|
2347
2360
|
// fetcher gets aborted, so we just leave result untouched and short circuit
|
|
@@ -2372,7 +2385,7 @@ function createRouter(init) {
|
|
|
2372
2385
|
return;
|
|
2373
2386
|
} else {
|
|
2374
2387
|
fetchRedirectIds.add(key);
|
|
2375
|
-
await startRedirectNavigation(
|
|
2388
|
+
await startRedirectNavigation(fetchRequest, result);
|
|
2376
2389
|
return;
|
|
2377
2390
|
}
|
|
2378
2391
|
}
|
|
@@ -2404,26 +2417,40 @@ function createRouter(init) {
|
|
|
2404
2417
|
* actually touch history until we've processed redirects, so we just use
|
|
2405
2418
|
* the history action from the original navigation (PUSH or REPLACE).
|
|
2406
2419
|
*/
|
|
2407
|
-
async function startRedirectNavigation(
|
|
2420
|
+
async function startRedirectNavigation(request, redirect, _temp2) {
|
|
2408
2421
|
let {
|
|
2409
2422
|
submission,
|
|
2410
2423
|
fetcherSubmission,
|
|
2411
2424
|
replace
|
|
2412
2425
|
} = _temp2 === void 0 ? {} : _temp2;
|
|
2413
|
-
if (redirect.
|
|
2426
|
+
if (redirect.response.headers.has("X-Remix-Revalidate")) {
|
|
2414
2427
|
isRevalidationRequired = true;
|
|
2415
2428
|
}
|
|
2416
|
-
let
|
|
2429
|
+
let location = redirect.response.headers.get("Location");
|
|
2430
|
+
invariant(location, "Expected a Location header on the redirect Response");
|
|
2431
|
+
let redirectLocation = createLocation(state.location, location, {
|
|
2417
2432
|
_isRedirect: true
|
|
2418
2433
|
});
|
|
2419
|
-
|
|
2434
|
+
if (ABSOLUTE_URL_REGEX.test(location)) {
|
|
2435
|
+
// Strip off the protocol+origin for same-origin + same-basename absolute redirects
|
|
2436
|
+
let normalizedLocation = location;
|
|
2437
|
+
let currentUrl = new URL(request.url);
|
|
2438
|
+
let url = normalizedLocation.startsWith("//") ? new URL(currentUrl.protocol + normalizedLocation) : new URL(normalizedLocation);
|
|
2439
|
+
let isSameBasename = stripBasename(url.pathname, basename) != null;
|
|
2440
|
+
if (url.origin === currentUrl.origin && isSameBasename) {
|
|
2441
|
+
normalizedLocation = url.pathname + url.search + url.hash;
|
|
2442
|
+
redirectLocation = createLocation(state.location, normalizedLocation, {
|
|
2443
|
+
_isRedirect: true
|
|
2444
|
+
});
|
|
2445
|
+
}
|
|
2446
|
+
}
|
|
2420
2447
|
if (isBrowser) {
|
|
2421
2448
|
let isDocumentReload = false;
|
|
2422
|
-
if (redirect.
|
|
2449
|
+
if (redirect.response.headers.has("X-Remix-Reload-Document")) {
|
|
2423
2450
|
// Hard reload if the response contained X-Remix-Reload-Document
|
|
2424
2451
|
isDocumentReload = true;
|
|
2425
|
-
} else if (ABSOLUTE_URL_REGEX.test(
|
|
2426
|
-
const url = init.history.createURL(
|
|
2452
|
+
} else if (ABSOLUTE_URL_REGEX.test(location)) {
|
|
2453
|
+
const url = init.history.createURL(location);
|
|
2427
2454
|
isDocumentReload =
|
|
2428
2455
|
// Hard reload if it's an absolute URL to a new origin
|
|
2429
2456
|
url.origin !== routerWindow.location.origin ||
|
|
@@ -2432,9 +2459,9 @@ function createRouter(init) {
|
|
|
2432
2459
|
}
|
|
2433
2460
|
if (isDocumentReload) {
|
|
2434
2461
|
if (replace) {
|
|
2435
|
-
routerWindow.location.replace(
|
|
2462
|
+
routerWindow.location.replace(location);
|
|
2436
2463
|
} else {
|
|
2437
|
-
routerWindow.location.assign(
|
|
2464
|
+
routerWindow.location.assign(location);
|
|
2438
2465
|
}
|
|
2439
2466
|
return;
|
|
2440
2467
|
}
|
|
@@ -2457,10 +2484,10 @@ function createRouter(init) {
|
|
|
2457
2484
|
// re-submit the GET/POST/PUT/PATCH/DELETE as a submission navigation to the
|
|
2458
2485
|
// redirected location
|
|
2459
2486
|
let activeSubmission = submission || fetcherSubmission;
|
|
2460
|
-
if (redirectPreserveMethodStatusCodes.has(redirect.status) && activeSubmission && isMutationMethod(activeSubmission.formMethod)) {
|
|
2487
|
+
if (redirectPreserveMethodStatusCodes.has(redirect.response.status) && activeSubmission && isMutationMethod(activeSubmission.formMethod)) {
|
|
2461
2488
|
await startNavigation(redirectHistoryAction, redirectLocation, {
|
|
2462
2489
|
submission: _extends({}, activeSubmission, {
|
|
2463
|
-
formAction:
|
|
2490
|
+
formAction: location
|
|
2464
2491
|
}),
|
|
2465
2492
|
// Preserve this flag across redirects
|
|
2466
2493
|
preventScrollReset: pendingPreventScrollReset
|
|
@@ -2478,16 +2505,35 @@ function createRouter(init) {
|
|
|
2478
2505
|
});
|
|
2479
2506
|
}
|
|
2480
2507
|
}
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
type
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2508
|
+
// Utility wrapper for calling dataStrategy client-side without having to
|
|
2509
|
+
// pass around the manifest, mapRouteProperties, etc.
|
|
2510
|
+
async function callDataStrategy(type, request, matchesToLoad, matches) {
|
|
2511
|
+
try {
|
|
2512
|
+
let results = await callDataStrategyImpl(dataStrategyImpl, type, request, matchesToLoad, matches, manifest, mapRouteProperties);
|
|
2513
|
+
return await Promise.all(results.map((result, i) => {
|
|
2514
|
+
if (isRedirectHandlerResult(result)) {
|
|
2515
|
+
return {
|
|
2516
|
+
type: ResultType.redirect,
|
|
2517
|
+
response: normalizeRelativeRoutingRedirectResponse(result.result, request, matchesToLoad[i].route.id, matches, basename, future.v7_relativeSplatPath)
|
|
2518
|
+
};
|
|
2519
|
+
}
|
|
2520
|
+
return convertHandlerResultToDataResult(result);
|
|
2521
|
+
}));
|
|
2522
|
+
} catch (e) {
|
|
2523
|
+
// If the outer dataStrategy method throws, just return the error for all
|
|
2524
|
+
// matches - and it'll naturally bubble to the root
|
|
2525
|
+
return matchesToLoad.map(() => ({
|
|
2526
|
+
type: ResultType.error,
|
|
2527
|
+
error: e
|
|
2528
|
+
}));
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
async function callLoadersAndMaybeResolveData(currentMatches, matches, matchesToLoad, fetchersToLoad, request) {
|
|
2532
|
+
let [loaderResults, ...fetcherResults] = await Promise.all([matchesToLoad.length ? callDataStrategy("loader", request, matchesToLoad, matches) : [], ...fetchersToLoad.map(f => {
|
|
2533
|
+
if (f.matches && f.match && f.controller) {
|
|
2534
|
+
let fetcherRequest = createClientSideRequest(init.history, f.path, f.controller.signal);
|
|
2535
|
+
return callDataStrategy("loader", fetcherRequest, [f.match], f.matches).then(r => r[0]);
|
|
2536
|
+
} else {
|
|
2491
2537
|
return Promise.resolve({
|
|
2492
2538
|
type: ResultType.error,
|
|
2493
2539
|
error: getInternalRouterError(404, {
|
|
@@ -2495,16 +2541,6 @@ function createRouter(init) {
|
|
|
2495
2541
|
})
|
|
2496
2542
|
});
|
|
2497
2543
|
}
|
|
2498
|
-
return dataStrategy({
|
|
2499
|
-
matches: [finesseToAgnosticDataStrategyMatch(f.match, mapRouteProperties, manifest)],
|
|
2500
|
-
request,
|
|
2501
|
-
type: "loader",
|
|
2502
|
-
defaultStrategy(match) {
|
|
2503
|
-
invariant(f.controller, "Expected controller for fetcher in defaultStrategy");
|
|
2504
|
-
invariant(f.matches, "Expected matches for fetcher in defaultStrategy");
|
|
2505
|
-
return callLoaderOrActionImplementation("loader", createClientSideRequest(init.history, f.path, f.controller.signal), match, f.matches, basename, future.v7_relativeSplatPath);
|
|
2506
|
-
}
|
|
2507
|
-
}).then(r => r[0]);
|
|
2508
2544
|
})]);
|
|
2509
2545
|
await Promise.all([resolveDeferredResults(currentMatches, matchesToLoad, loaderResults, loaderResults.map(() => request.signal), false, state.loaderData), resolveDeferredResults(currentMatches, fetchersToLoad.map(f => f.match), fetcherResults, fetchersToLoad.map(f => f.controller ? f.controller.signal : null), true)]);
|
|
2510
2546
|
return {
|
|
@@ -2803,10 +2839,9 @@ function createRouter(init) {
|
|
|
2803
2839
|
const UNSAFE_DEFERRED_SYMBOL = Symbol("deferred");
|
|
2804
2840
|
function createStaticHandler(routes, opts) {
|
|
2805
2841
|
invariant(routes.length > 0, "You must provide a non-empty routes array to createStaticHandler");
|
|
2806
|
-
const dataStrategy = (opts == null ? void 0 : opts.dataStrategy) || defaultDataStrategy;
|
|
2807
|
-
const callLoaderOrAction = createCallLoaderOrAction(dataStrategy);
|
|
2808
2842
|
let manifest = {};
|
|
2809
2843
|
let basename = (opts ? opts.basename : null) || "/";
|
|
2844
|
+
let dataStrategyImpl = (opts == null ? void 0 : opts.unstable_dataStrategy) || defaultDataStrategy;
|
|
2810
2845
|
let mapRouteProperties;
|
|
2811
2846
|
if (opts != null && opts.mapRouteProperties) {
|
|
2812
2847
|
mapRouteProperties = opts.mapRouteProperties;
|
|
@@ -2821,7 +2856,8 @@ function createStaticHandler(routes, opts) {
|
|
|
2821
2856
|
}
|
|
2822
2857
|
// Config driven behavior flags
|
|
2823
2858
|
let future = _extends({
|
|
2824
|
-
v7_relativeSplatPath: false
|
|
2859
|
+
v7_relativeSplatPath: false,
|
|
2860
|
+
v7_throwAbortReason: false
|
|
2825
2861
|
}, opts ? opts.future : null);
|
|
2826
2862
|
let dataRoutes = convertRoutesToDataRoutes(routes, mapRouteProperties, undefined, manifest);
|
|
2827
2863
|
/**
|
|
@@ -2999,14 +3035,14 @@ function createStaticHandler(routes, opts) {
|
|
|
2999
3035
|
actionHeaders: {}
|
|
3000
3036
|
});
|
|
3001
3037
|
} catch (e) {
|
|
3002
|
-
// If the user threw/returned a Response in callLoaderOrAction
|
|
3003
|
-
//
|
|
3004
|
-
//
|
|
3005
|
-
if (
|
|
3038
|
+
// If the user threw/returned a Response in callLoaderOrAction for a
|
|
3039
|
+
// `queryRoute` call, we throw the `HandlerResult` to bail out early
|
|
3040
|
+
// and then return or throw the raw Response here accordingly
|
|
3041
|
+
if (isHandlerResult(e) && isResponse(e.result)) {
|
|
3006
3042
|
if (e.type === ResultType.error) {
|
|
3007
|
-
throw e.
|
|
3043
|
+
throw e.result;
|
|
3008
3044
|
}
|
|
3009
|
-
return e.
|
|
3045
|
+
return e.result;
|
|
3010
3046
|
}
|
|
3011
3047
|
// Redirects are always returned since they don't propagate to catch
|
|
3012
3048
|
// boundaries
|
|
@@ -3032,14 +3068,10 @@ function createStaticHandler(routes, opts) {
|
|
|
3032
3068
|
error
|
|
3033
3069
|
};
|
|
3034
3070
|
} else {
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
isRouteRequest,
|
|
3038
|
-
requestContext
|
|
3039
|
-
});
|
|
3071
|
+
let results = await callDataStrategy("action", request, [actionMatch], matches, isRouteRequest, requestContext);
|
|
3072
|
+
result = results[0];
|
|
3040
3073
|
if (request.signal.aborted) {
|
|
3041
|
-
|
|
3042
|
-
throw new Error(method + "() call aborted: " + request.method + " " + request.url);
|
|
3074
|
+
throwStaticHandlerAbortedError(request, isRouteRequest, future);
|
|
3043
3075
|
}
|
|
3044
3076
|
}
|
|
3045
3077
|
if (isRedirectResult(result)) {
|
|
@@ -3048,9 +3080,9 @@ function createStaticHandler(routes, opts) {
|
|
|
3048
3080
|
// can get back on the "throw all redirect responses" train here should
|
|
3049
3081
|
// this ever happen :/
|
|
3050
3082
|
throw new Response(null, {
|
|
3051
|
-
status: result.status,
|
|
3083
|
+
status: result.response.status,
|
|
3052
3084
|
headers: {
|
|
3053
|
-
Location: result.
|
|
3085
|
+
Location: result.response.headers.get("Location")
|
|
3054
3086
|
}
|
|
3055
3087
|
});
|
|
3056
3088
|
}
|
|
@@ -3098,8 +3130,8 @@ function createStaticHandler(routes, opts) {
|
|
|
3098
3130
|
return _extends({}, context, {
|
|
3099
3131
|
statusCode: isRouteErrorResponse(result.error) ? result.error.status : 500,
|
|
3100
3132
|
actionData: null,
|
|
3101
|
-
actionHeaders: _extends({}, result.
|
|
3102
|
-
[actionMatch.route.id]: result.headers
|
|
3133
|
+
actionHeaders: _extends({}, result.response ? {
|
|
3134
|
+
[actionMatch.route.id]: result.response.headers
|
|
3103
3135
|
} : {})
|
|
3104
3136
|
});
|
|
3105
3137
|
}
|
|
@@ -3110,15 +3142,17 @@ function createStaticHandler(routes, opts) {
|
|
|
3110
3142
|
signal: request.signal
|
|
3111
3143
|
});
|
|
3112
3144
|
let context = await loadRouteData(loaderRequest, matches, requestContext);
|
|
3113
|
-
return _extends({}, context,
|
|
3114
|
-
statusCode: result.statusCode
|
|
3115
|
-
} : {}, {
|
|
3145
|
+
return _extends({}, context, {
|
|
3116
3146
|
actionData: {
|
|
3117
3147
|
[actionMatch.route.id]: result.data
|
|
3118
|
-
}
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3148
|
+
}
|
|
3149
|
+
}, result.response ? {
|
|
3150
|
+
statusCode: result.response.status,
|
|
3151
|
+
actionHeaders: {
|
|
3152
|
+
[actionMatch.route.id]: result.response.headers
|
|
3153
|
+
}
|
|
3154
|
+
} : {
|
|
3155
|
+
actionHeaders: {}
|
|
3122
3156
|
});
|
|
3123
3157
|
}
|
|
3124
3158
|
async function loadRouteData(request, matches, requestContext, routeMatch, pendingActionError) {
|
|
@@ -3147,21 +3181,9 @@ function createStaticHandler(routes, opts) {
|
|
|
3147
3181
|
activeDeferreds: null
|
|
3148
3182
|
};
|
|
3149
3183
|
}
|
|
3150
|
-
let results = await
|
|
3151
|
-
matches: matchesToLoad.map(m => finesseToAgnosticDataStrategyMatch(m, mapRouteProperties, manifest)),
|
|
3152
|
-
request,
|
|
3153
|
-
type: "loader",
|
|
3154
|
-
defaultStrategy(match) {
|
|
3155
|
-
return callLoaderOrActionImplementation("loader", request, match, matches, basename, future.v7_relativeSplatPath, {
|
|
3156
|
-
isStaticRequest: true,
|
|
3157
|
-
isRouteRequest,
|
|
3158
|
-
requestContext
|
|
3159
|
-
});
|
|
3160
|
-
}
|
|
3161
|
-
});
|
|
3184
|
+
let results = await callDataStrategy("loader", request, matchesToLoad, matches, isRouteRequest, requestContext);
|
|
3162
3185
|
if (request.signal.aborted) {
|
|
3163
|
-
|
|
3164
|
-
throw new Error(method + "() call aborted: " + request.method + " " + request.url);
|
|
3186
|
+
throwStaticHandlerAbortedError(request, isRouteRequest, future);
|
|
3165
3187
|
}
|
|
3166
3188
|
// Process and commit output from loaders
|
|
3167
3189
|
let activeDeferreds = new Map();
|
|
@@ -3178,6 +3200,24 @@ function createStaticHandler(routes, opts) {
|
|
|
3178
3200
|
activeDeferreds: activeDeferreds.size > 0 ? Object.fromEntries(activeDeferreds.entries()) : null
|
|
3179
3201
|
});
|
|
3180
3202
|
}
|
|
3203
|
+
// Utility wrapper for calling dataStrategy server-side without having to
|
|
3204
|
+
// pass around the manifest, mapRouteProperties, etc.
|
|
3205
|
+
async function callDataStrategy(type, request, matchesToLoad, matches, isRouteRequest, requestContext) {
|
|
3206
|
+
let results = await callDataStrategyImpl(dataStrategyImpl, type, request, matchesToLoad, matches, manifest, mapRouteProperties, requestContext);
|
|
3207
|
+
return await Promise.all(results.map((result, i) => {
|
|
3208
|
+
if (isRedirectHandlerResult(result)) {
|
|
3209
|
+
// Throw redirects and let the server handle them with an HTTP redirect
|
|
3210
|
+
throw normalizeRelativeRoutingRedirectResponse(result.result, request, matchesToLoad[i].route.id, matches, basename, future.v7_relativeSplatPath);
|
|
3211
|
+
}
|
|
3212
|
+
if (isResponse(result.result) && isRouteRequest) {
|
|
3213
|
+
// For SSR single-route requests, we want to hand Responses back directly
|
|
3214
|
+
// without unwrapping. We do this with the QueryRouteResponse wrapper
|
|
3215
|
+
// interface so we can know whether it was returned or thrown
|
|
3216
|
+
throw result;
|
|
3217
|
+
}
|
|
3218
|
+
return convertHandlerResultToDataResult(result);
|
|
3219
|
+
}));
|
|
3220
|
+
}
|
|
3181
3221
|
return {
|
|
3182
3222
|
dataRoutes,
|
|
3183
3223
|
query,
|
|
@@ -3188,26 +3228,26 @@ function createStaticHandler(routes, opts) {
|
|
|
3188
3228
|
////////////////////////////////////////////////////////////////////////////////
|
|
3189
3229
|
//#region Helpers
|
|
3190
3230
|
////////////////////////////////////////////////////////////////////////////////
|
|
3191
|
-
function defaultDataStrategy(_ref3) {
|
|
3192
|
-
let {
|
|
3193
|
-
defaultStrategy,
|
|
3194
|
-
matches
|
|
3195
|
-
} = _ref3;
|
|
3196
|
-
return Promise.all(matches.map(match => defaultStrategy(match)));
|
|
3197
|
-
}
|
|
3198
3231
|
/**
|
|
3199
3232
|
* Given an existing StaticHandlerContext and an error thrown at render time,
|
|
3200
3233
|
* provide an updated StaticHandlerContext suitable for a second SSR render
|
|
3201
3234
|
*/
|
|
3202
3235
|
function getStaticContextFromError(routes, context, error) {
|
|
3203
3236
|
let newContext = _extends({}, context, {
|
|
3204
|
-
statusCode: 500,
|
|
3237
|
+
statusCode: isRouteErrorResponse(error) ? error.status : 500,
|
|
3205
3238
|
errors: {
|
|
3206
3239
|
[context._deepestRenderedBoundaryId || routes[0].id]: error
|
|
3207
3240
|
}
|
|
3208
3241
|
});
|
|
3209
3242
|
return newContext;
|
|
3210
3243
|
}
|
|
3244
|
+
function throwStaticHandlerAbortedError(request, isRouteRequest, future) {
|
|
3245
|
+
if (future.v7_throwAbortReason && request.signal.reason !== undefined) {
|
|
3246
|
+
throw request.signal.reason;
|
|
3247
|
+
}
|
|
3248
|
+
let method = isRouteRequest ? "queryRoute" : "query";
|
|
3249
|
+
throw new Error(method + "() call aborted: " + request.method + " " + request.url);
|
|
3250
|
+
}
|
|
3211
3251
|
function isSubmissionNavigation(opts) {
|
|
3212
3252
|
return opts != null && ("formData" in opts && opts.formData != null || "body" in opts && opts.body !== undefined);
|
|
3213
3253
|
}
|
|
@@ -3286,8 +3326,8 @@ function normalizeNavigateOptions(normalizeFormMethod, isFetcher, path, opts) {
|
|
|
3286
3326
|
}
|
|
3287
3327
|
let text = typeof opts.body === "string" ? opts.body : opts.body instanceof FormData || opts.body instanceof URLSearchParams ?
|
|
3288
3328
|
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#plain-text-form-data
|
|
3289
|
-
Array.from(opts.body.entries()).reduce((acc,
|
|
3290
|
-
let [name, value] =
|
|
3329
|
+
Array.from(opts.body.entries()).reduce((acc, _ref3) => {
|
|
3330
|
+
let [name, value] = _ref3;
|
|
3291
3331
|
return "" + acc + name + "=" + value + "\n";
|
|
3292
3332
|
}, "") : String(opts.body);
|
|
3293
3333
|
return {
|
|
@@ -3395,18 +3435,24 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
|
|
|
3395
3435
|
let boundaryId = pendingError ? Object.keys(pendingError)[0] : undefined;
|
|
3396
3436
|
let boundaryMatches = getLoaderMatchesUntilBoundary(matches, boundaryId);
|
|
3397
3437
|
let navigationMatches = boundaryMatches.filter((match, index) => {
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
}
|
|
3403
|
-
if (match.route.lazy) {
|
|
3438
|
+
let {
|
|
3439
|
+
route
|
|
3440
|
+
} = match;
|
|
3441
|
+
if (route.lazy) {
|
|
3404
3442
|
// We haven't loaded this route yet so we don't know if it's got a loader!
|
|
3405
3443
|
return true;
|
|
3406
3444
|
}
|
|
3407
|
-
if (
|
|
3445
|
+
if (route.loader == null) {
|
|
3408
3446
|
return false;
|
|
3409
3447
|
}
|
|
3448
|
+
if (isInitialLoad) {
|
|
3449
|
+
if (route.loader.hydrate) {
|
|
3450
|
+
return true;
|
|
3451
|
+
}
|
|
3452
|
+
return state.loaderData[route.id] === undefined && (
|
|
3453
|
+
// Don't re-run if the loader ran and threw an error
|
|
3454
|
+
!state.errors || state.errors[route.id] === undefined);
|
|
3455
|
+
}
|
|
3410
3456
|
// Always call the loader on new route instances and pending defer cancellations
|
|
3411
3457
|
if (isNewLoader(state.loaderData, state.matches[index], match) || cancelledDeferredRoutes.some(id => id === match.route.id)) {
|
|
3412
3458
|
return true;
|
|
@@ -3503,19 +3549,6 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
|
|
|
3503
3549
|
});
|
|
3504
3550
|
return [navigationMatches, revalidatingFetchers];
|
|
3505
3551
|
}
|
|
3506
|
-
// Is this route unhydrated (when v7_partialHydration=true) such that we need
|
|
3507
|
-
// to call it's loader on the initial router creation
|
|
3508
|
-
function isUnhydratedRoute(state, route) {
|
|
3509
|
-
if (!route.loader) {
|
|
3510
|
-
return false;
|
|
3511
|
-
}
|
|
3512
|
-
if (route.loader.hydrate) {
|
|
3513
|
-
return true;
|
|
3514
|
-
}
|
|
3515
|
-
return state.loaderData[route.id] === undefined && (!state.errors ||
|
|
3516
|
-
// Loader ran but errored - don't re-run
|
|
3517
|
-
state.errors[route.id] === undefined);
|
|
3518
|
-
}
|
|
3519
3552
|
function isNewLoader(currentLoaderData, currentMatch, match) {
|
|
3520
3553
|
let isNew =
|
|
3521
3554
|
// [a] -> [a, b]
|
|
@@ -3596,44 +3629,52 @@ async function loadLazyRouteModule(route, mapRouteProperties, manifest) {
|
|
|
3596
3629
|
}));
|
|
3597
3630
|
return routeToUpdate;
|
|
3598
3631
|
}
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
opts = {};
|
|
3603
|
-
}
|
|
3604
|
-
let [result] = await dataStrategy({
|
|
3605
|
-
matches: [finesseToAgnosticDataStrategyMatch(match, mapRouteProperties, manifest)],
|
|
3606
|
-
request,
|
|
3607
|
-
type,
|
|
3608
|
-
defaultStrategy(match) {
|
|
3609
|
-
return callLoaderOrActionImplementation(type, request, match, matches, basename, v7_relativeSplatPath, opts);
|
|
3610
|
-
}
|
|
3611
|
-
});
|
|
3612
|
-
return result;
|
|
3613
|
-
};
|
|
3632
|
+
// Default implementation of `dataStrategy` which fetches all loaders in parallel
|
|
3633
|
+
function defaultDataStrategy(opts) {
|
|
3634
|
+
return Promise.all(opts.matches.map(m => m.bikeshed_loadRoute()));
|
|
3614
3635
|
}
|
|
3615
|
-
function
|
|
3616
|
-
let
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3636
|
+
async function callDataStrategyImpl(dataStrategyImpl, type, request, matchesToLoad, matches, manifest, mapRouteProperties, requestContext) {
|
|
3637
|
+
let routeIdsToLoad = matchesToLoad.reduce((acc, m) => acc.add(m.route.id), new Set());
|
|
3638
|
+
let loadedMatches = new Set();
|
|
3639
|
+
// Send all matches here to allow for a middleware-type implementation.
|
|
3640
|
+
// handler will be a no-op for unneeded routes and we filter those results
|
|
3641
|
+
// back out below.
|
|
3642
|
+
let results = await dataStrategyImpl({
|
|
3643
|
+
matches: matches.map(match => {
|
|
3644
|
+
// `bikeshed_loadRoute` encapsulates the route.lazy, executing the
|
|
3645
|
+
// loader/action, and mapping return values/thrown errors to a
|
|
3646
|
+
// HandlerResult. Users can pass a callback to take fine-grained control
|
|
3647
|
+
// over the execution of the loader/action
|
|
3648
|
+
let bikeshed_loadRoute = bikeshed_handlerOverride => {
|
|
3649
|
+
loadedMatches.add(match.route.id);
|
|
3650
|
+
return routeIdsToLoad.has(match.route.id) ? callLoaderOrAction(type, request, match, manifest, mapRouteProperties, bikeshed_handlerOverride, requestContext) :
|
|
3651
|
+
// TODO: What's the best thing to do here - return an empty "success" result?
|
|
3652
|
+
// Or return a success result with the current route loader/action data?
|
|
3653
|
+
// We strip these results out if the route didn't need to be revalidated in
|
|
3654
|
+
// `callDataStrategy` so it doesn't matter for us. It's more of a question
|
|
3655
|
+
// of whether exposing the current data to the user is useful?
|
|
3656
|
+
Promise.resolve({
|
|
3657
|
+
type: ResultType.data,
|
|
3658
|
+
result: undefined
|
|
3659
|
+
});
|
|
3660
|
+
};
|
|
3661
|
+
return _extends({}, match, {
|
|
3662
|
+
bikeshed_loadRoute
|
|
3663
|
+
});
|
|
3664
|
+
}),
|
|
3665
|
+
request,
|
|
3666
|
+
params: matches[0].params
|
|
3630
3667
|
});
|
|
3668
|
+
// Throw if any loadRoute implementations not called since they are what
|
|
3669
|
+
// ensures a route is fully loaded
|
|
3670
|
+
// Throw if any loadRoute implementations not called since they are what
|
|
3671
|
+
// ensure a route is fully loaded
|
|
3672
|
+
matches.forEach(m => invariant(loadedMatches.has(m.route.id), "`match.bikeshed_loadRoute()` was not called for route id \"" + m.route.id + "\". " + "You must call `match.bikeshed_loadRoute()` on every match passed to " + "`dataStrategy` to ensure all routes are properly loaded."));
|
|
3673
|
+
// Filter out any middleware-only matches for which we didn't need to run handlers
|
|
3674
|
+
return results.filter((_, i) => routeIdsToLoad.has(matches[i].route.id));
|
|
3631
3675
|
}
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
opts = {};
|
|
3635
|
-
}
|
|
3636
|
-
let resultType;
|
|
3676
|
+
// Default logic for calling a loader/action is the user has no specified a dataStrategy
|
|
3677
|
+
async function callLoaderOrAction(type, request, match, manifest, mapRouteProperties, bikeshed_handlerOverride, staticContext) {
|
|
3637
3678
|
let result;
|
|
3638
3679
|
let onReject;
|
|
3639
3680
|
let runHandler = handler => {
|
|
@@ -3642,11 +3683,13 @@ async function callLoaderOrActionImplementation(type, request, match, matches, b
|
|
|
3642
3683
|
let abortPromise = new Promise((_, r) => reject = r);
|
|
3643
3684
|
onReject = () => reject();
|
|
3644
3685
|
request.signal.addEventListener("abort", onReject);
|
|
3645
|
-
|
|
3686
|
+
let handlerArg = {
|
|
3646
3687
|
request,
|
|
3647
3688
|
params: match.params,
|
|
3648
|
-
context:
|
|
3649
|
-
}
|
|
3689
|
+
context: staticContext
|
|
3690
|
+
};
|
|
3691
|
+
let handlerPromise = bikeshed_handlerOverride ? bikeshed_handlerOverride(async ctx => ctx !== undefined ? handler(handlerArg, ctx) : handler(handlerArg)) : handler(handlerArg);
|
|
3692
|
+
return Promise.race([handlerPromise, abortPromise]);
|
|
3650
3693
|
};
|
|
3651
3694
|
try {
|
|
3652
3695
|
let handler = match.route[type];
|
|
@@ -3660,17 +3703,17 @@ async function callLoaderOrActionImplementation(type, request, match, matches, b
|
|
|
3660
3703
|
// route has a boundary that can handle the error
|
|
3661
3704
|
runHandler(handler).catch(e => {
|
|
3662
3705
|
handlerError = e;
|
|
3663
|
-
}), match.route]);
|
|
3706
|
+
}), loadLazyRouteModule(match.route, mapRouteProperties, manifest)]);
|
|
3664
3707
|
if (handlerError) {
|
|
3665
3708
|
throw handlerError;
|
|
3666
3709
|
}
|
|
3667
3710
|
result = values[0];
|
|
3668
3711
|
} else {
|
|
3669
3712
|
// Load lazy route module, then run any returned handler
|
|
3670
|
-
|
|
3671
|
-
handler = route[type];
|
|
3713
|
+
await loadLazyRouteModule(match.route, mapRouteProperties, manifest);
|
|
3714
|
+
handler = match.route[type];
|
|
3672
3715
|
if (handler) {
|
|
3673
|
-
// Handler still
|
|
3716
|
+
// Handler still runs even if we got interrupted to maintain consistency
|
|
3674
3717
|
// with un-abortable behavior of handler execution on non-lazy or
|
|
3675
3718
|
// previously-lazy-loaded routes
|
|
3676
3719
|
result = await runHandler(handler);
|
|
@@ -3687,7 +3730,7 @@ async function callLoaderOrActionImplementation(type, request, match, matches, b
|
|
|
3687
3730
|
// hit the invariant below that errors on returning undefined.
|
|
3688
3731
|
return {
|
|
3689
3732
|
type: ResultType.data,
|
|
3690
|
-
|
|
3733
|
+
result: undefined
|
|
3691
3734
|
};
|
|
3692
3735
|
}
|
|
3693
3736
|
}
|
|
@@ -3702,66 +3745,37 @@ async function callLoaderOrActionImplementation(type, request, match, matches, b
|
|
|
3702
3745
|
}
|
|
3703
3746
|
invariant(result !== undefined, "You defined " + (type === "action" ? "an action" : "a loader") + " for route " + ("\"" + match.route.id + "\" but didn't return anything from your `" + type + "` ") + "function. Please return a value or `null`.");
|
|
3704
3747
|
} catch (e) {
|
|
3705
|
-
|
|
3706
|
-
|
|
3748
|
+
return {
|
|
3749
|
+
type: ResultType.error,
|
|
3750
|
+
result: e
|
|
3751
|
+
};
|
|
3707
3752
|
} finally {
|
|
3708
3753
|
if (onReject) {
|
|
3709
3754
|
request.signal.removeEventListener("abort", onReject);
|
|
3710
3755
|
}
|
|
3711
3756
|
}
|
|
3757
|
+
return {
|
|
3758
|
+
type: ResultType.data,
|
|
3759
|
+
result
|
|
3760
|
+
};
|
|
3761
|
+
}
|
|
3762
|
+
async function convertHandlerResultToDataResult(handlerResult) {
|
|
3763
|
+
let {
|
|
3764
|
+
result,
|
|
3765
|
+
type
|
|
3766
|
+
} = handlerResult;
|
|
3712
3767
|
if (isResponse(result)) {
|
|
3713
|
-
let status = result.status;
|
|
3714
|
-
// Process redirects
|
|
3715
|
-
if (redirectStatusCodes.has(status)) {
|
|
3716
|
-
let location = result.headers.get("Location");
|
|
3717
|
-
invariant(location, "Redirects returned/thrown from loaders/actions must have a Location header");
|
|
3718
|
-
// Support relative routing in internal redirects
|
|
3719
|
-
if (!ABSOLUTE_URL_REGEX.test(location)) {
|
|
3720
|
-
location = normalizeTo(new URL(request.url), matches.slice(0, matches.findIndex(m => m.route.id === match.route.id) + 1), basename, true, location, v7_relativeSplatPath);
|
|
3721
|
-
} else if (!opts.isStaticRequest) {
|
|
3722
|
-
// Strip off the protocol+origin for same-origin + same-basename absolute
|
|
3723
|
-
// redirects. If this is a static request, we can let it go back to the
|
|
3724
|
-
// browser as-is
|
|
3725
|
-
let currentUrl = new URL(request.url);
|
|
3726
|
-
let url = location.startsWith("//") ? new URL(currentUrl.protocol + location) : new URL(location);
|
|
3727
|
-
let isSameBasename = stripBasename(url.pathname, basename) != null;
|
|
3728
|
-
if (url.origin === currentUrl.origin && isSameBasename) {
|
|
3729
|
-
location = url.pathname + url.search + url.hash;
|
|
3730
|
-
}
|
|
3731
|
-
}
|
|
3732
|
-
// Don't process redirects in the router during static requests requests.
|
|
3733
|
-
// Instead, throw the Response and let the server handle it with an HTTP
|
|
3734
|
-
// redirect. We also update the Location header in place in this flow so
|
|
3735
|
-
// basename and relative routing is taken into account
|
|
3736
|
-
if (opts.isStaticRequest) {
|
|
3737
|
-
result.headers.set("Location", location);
|
|
3738
|
-
throw result;
|
|
3739
|
-
}
|
|
3740
|
-
return {
|
|
3741
|
-
type: ResultType.redirect,
|
|
3742
|
-
status,
|
|
3743
|
-
location,
|
|
3744
|
-
revalidate: result.headers.get("X-Remix-Revalidate") !== null,
|
|
3745
|
-
reloadDocument: result.headers.get("X-Remix-Reload-Document") !== null
|
|
3746
|
-
};
|
|
3747
|
-
}
|
|
3748
|
-
// For SSR single-route requests, we want to hand Responses back directly
|
|
3749
|
-
// without unwrapping. We do this with the QueryRouteResponse wrapper
|
|
3750
|
-
// interface so we can know whether it was returned or thrown
|
|
3751
|
-
if (opts.isRouteRequest) {
|
|
3752
|
-
let queryRouteResponse = {
|
|
3753
|
-
type: resultType === ResultType.error ? ResultType.error : ResultType.data,
|
|
3754
|
-
response: result
|
|
3755
|
-
};
|
|
3756
|
-
throw queryRouteResponse;
|
|
3757
|
-
}
|
|
3758
3768
|
let data;
|
|
3759
3769
|
try {
|
|
3760
3770
|
let contentType = result.headers.get("Content-Type");
|
|
3761
3771
|
// Check between word boundaries instead of startsWith() due to the last
|
|
3762
3772
|
// paragraph of https://httpwg.org/specs/rfc9110.html#field.content-type
|
|
3763
3773
|
if (contentType && /\bapplication\/json\b/.test(contentType)) {
|
|
3764
|
-
|
|
3774
|
+
if (result.body == null) {
|
|
3775
|
+
data = null;
|
|
3776
|
+
} else {
|
|
3777
|
+
data = await result.json();
|
|
3778
|
+
}
|
|
3765
3779
|
} else {
|
|
3766
3780
|
data = await result.text();
|
|
3767
3781
|
}
|
|
@@ -3771,23 +3785,22 @@ async function callLoaderOrActionImplementation(type, request, match, matches, b
|
|
|
3771
3785
|
error: e
|
|
3772
3786
|
};
|
|
3773
3787
|
}
|
|
3774
|
-
if (
|
|
3788
|
+
if (type === ResultType.error) {
|
|
3775
3789
|
return {
|
|
3776
|
-
type
|
|
3777
|
-
error: new ErrorResponseImpl(status, result.statusText, data),
|
|
3778
|
-
|
|
3790
|
+
type,
|
|
3791
|
+
error: new ErrorResponseImpl(result.status, result.statusText, data),
|
|
3792
|
+
response: result
|
|
3779
3793
|
};
|
|
3780
3794
|
}
|
|
3781
3795
|
return {
|
|
3782
3796
|
type: ResultType.data,
|
|
3783
3797
|
data,
|
|
3784
|
-
|
|
3785
|
-
headers: result.headers
|
|
3798
|
+
response: result
|
|
3786
3799
|
};
|
|
3787
3800
|
}
|
|
3788
|
-
if (
|
|
3801
|
+
if (type === ResultType.error) {
|
|
3789
3802
|
return {
|
|
3790
|
-
type
|
|
3803
|
+
type,
|
|
3791
3804
|
error: result
|
|
3792
3805
|
};
|
|
3793
3806
|
}
|
|
@@ -3805,6 +3818,17 @@ async function callLoaderOrActionImplementation(type, request, match, matches, b
|
|
|
3805
3818
|
data: result
|
|
3806
3819
|
};
|
|
3807
3820
|
}
|
|
3821
|
+
// Support relative routing in internal redirects
|
|
3822
|
+
function normalizeRelativeRoutingRedirectResponse(response, request, routeId, matches, basename, v7_relativeSplatPath) {
|
|
3823
|
+
let location = response.headers.get("Location");
|
|
3824
|
+
invariant(location, "Redirects returned/thrown from loaders/actions must have a Location header");
|
|
3825
|
+
if (!ABSOLUTE_URL_REGEX.test(location)) {
|
|
3826
|
+
let trimmedMatches = matches.slice(0, matches.findIndex(m => m.route.id === routeId) + 1);
|
|
3827
|
+
location = normalizeTo(new URL(request.url), trimmedMatches, basename, true, location, v7_relativeSplatPath);
|
|
3828
|
+
response.headers.set("Location", location);
|
|
3829
|
+
}
|
|
3830
|
+
return response;
|
|
3831
|
+
}
|
|
3808
3832
|
// Utility method for creating the Request instances for loaders/actions during
|
|
3809
3833
|
// client-side navigations and fetches. During SSR we will always have a
|
|
3810
3834
|
// Request instance from the static handler (query/queryRoute)
|
|
@@ -3891,23 +3915,31 @@ function processRouteLoaderData(matches, matchesToLoad, results, pendingError, a
|
|
|
3891
3915
|
foundError = true;
|
|
3892
3916
|
statusCode = isRouteErrorResponse(result.error) ? result.error.status : 500;
|
|
3893
3917
|
}
|
|
3894
|
-
if (result.
|
|
3895
|
-
loaderHeaders[id] = result.headers;
|
|
3918
|
+
if (result.response) {
|
|
3919
|
+
loaderHeaders[id] = result.response.headers;
|
|
3896
3920
|
}
|
|
3897
3921
|
} else {
|
|
3898
3922
|
if (isDeferredResult(result)) {
|
|
3899
3923
|
activeDeferreds.set(id, result.deferredData);
|
|
3900
3924
|
loaderData[id] = result.deferredData.data;
|
|
3925
|
+
// Error status codes always override success status codes, but if all
|
|
3926
|
+
// loaders are successful we take the deepest status code.
|
|
3927
|
+
if (result.statusCode != null && result.statusCode !== 200 && !foundError) {
|
|
3928
|
+
statusCode = result.statusCode;
|
|
3929
|
+
}
|
|
3930
|
+
if (result.headers) {
|
|
3931
|
+
loaderHeaders[id] = result.headers;
|
|
3932
|
+
}
|
|
3901
3933
|
} else {
|
|
3902
3934
|
loaderData[id] = result.data;
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3935
|
+
// Error status codes always override success status codes, but if all
|
|
3936
|
+
// loaders are successful we take the deepest status code.
|
|
3937
|
+
if (result.response) {
|
|
3938
|
+
if (result.response.status !== 200 && !foundError) {
|
|
3939
|
+
statusCode = result.response.status;
|
|
3940
|
+
}
|
|
3941
|
+
loaderHeaders[id] = result.response.headers;
|
|
3942
|
+
}
|
|
3911
3943
|
}
|
|
3912
3944
|
}
|
|
3913
3945
|
});
|
|
@@ -4081,6 +4113,12 @@ function isHashChangeOnly(a, b) {
|
|
|
4081
4113
|
// /page#hash -> /page
|
|
4082
4114
|
return false;
|
|
4083
4115
|
}
|
|
4116
|
+
function isHandlerResult(result) {
|
|
4117
|
+
return result != null && typeof result === "object" && "type" in result && "result" in result && (result.type === ResultType.data || result.type === ResultType.error);
|
|
4118
|
+
}
|
|
4119
|
+
function isRedirectHandlerResult(result) {
|
|
4120
|
+
return isResponse(result.result) && redirectStatusCodes.has(result.result.status);
|
|
4121
|
+
}
|
|
4084
4122
|
function isDeferredResult(result) {
|
|
4085
4123
|
return result.type === ResultType.deferred;
|
|
4086
4124
|
}
|
|
@@ -4105,9 +4143,6 @@ function isRedirectResponse(result) {
|
|
|
4105
4143
|
let location = result.headers.get("Location");
|
|
4106
4144
|
return status >= 300 && status <= 399 && location != null;
|
|
4107
4145
|
}
|
|
4108
|
-
function isQueryRouteResponse(obj) {
|
|
4109
|
-
return obj && isResponse(obj.response) && (obj.type === ResultType.data || obj.type === ResultType.error);
|
|
4110
|
-
}
|
|
4111
4146
|
function isValidMethod(method) {
|
|
4112
4147
|
return validRequestMethods.has(method.toLowerCase());
|
|
4113
4148
|
}
|
|
@@ -4345,5 +4380,5 @@ function persistAppliedTransitions(_window, transitions) {
|
|
|
4345
4380
|
}
|
|
4346
4381
|
//#endregion
|
|
4347
4382
|
|
|
4348
|
-
export { AbortedDeferredError, Action, IDLE_BLOCKER, IDLE_FETCHER, IDLE_NAVIGATION,
|
|
4383
|
+
export { AbortedDeferredError, Action, IDLE_BLOCKER, IDLE_FETCHER, IDLE_NAVIGATION, UNSAFE_DEFERRED_SYMBOL, DeferredData as UNSAFE_DeferredData, ErrorResponseImpl as UNSAFE_ErrorResponseImpl, convertRouteMatchToUiMatch as UNSAFE_convertRouteMatchToUiMatch, convertRoutesToDataRoutes as UNSAFE_convertRoutesToDataRoutes, getResolveToMatches as UNSAFE_getResolveToMatches, invariant as UNSAFE_invariant, warning as UNSAFE_warning, createBrowserHistory, createHashHistory, createMemoryHistory, createPath, createRouter, createStaticHandler, defer, generatePath, getStaticContextFromError, getToPathname, isDeferredData, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, parsePath, redirect, redirectDocument, resolvePath, resolveTo, stripBasename };
|
|
4349
4384
|
//# sourceMappingURL=router.js.map
|