@remix-run/router 0.0.0-experimental-bcda00aaf → 0.0.0-experimental-3be88c6fb
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/dist/router.cjs.js +152 -121
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.js +145 -118
- package/dist/router.js.map +1 -1
- package/dist/router.umd.js +152 -121
- 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/package.json +1 -1
- package/router.ts +181 -154
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-3be88c6fb
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -1445,24 +1445,12 @@ function createRouter(init) {
|
|
|
1445
1445
|
// were marked for explicit hydration
|
|
1446
1446
|
let loaderData = init.hydrationData ? init.hydrationData.loaderData : null;
|
|
1447
1447
|
let errors = init.hydrationData ? init.hydrationData.errors : null;
|
|
1448
|
-
let isRouteInitialized = m => {
|
|
1449
|
-
// No loader, nothing to initialize
|
|
1450
|
-
if (!m.route.loader) {
|
|
1451
|
-
return true;
|
|
1452
|
-
}
|
|
1453
|
-
// Explicitly opting-in to running on hydration
|
|
1454
|
-
if (typeof m.route.loader === "function" && m.route.loader.hydrate === true) {
|
|
1455
|
-
return false;
|
|
1456
|
-
}
|
|
1457
|
-
// Otherwise, initialized if hydrated with data or an error
|
|
1458
|
-
return loaderData && loaderData[m.route.id] !== undefined || errors && errors[m.route.id] !== undefined;
|
|
1459
|
-
};
|
|
1460
1448
|
// If errors exist, don't consider routes below the boundary
|
|
1461
1449
|
if (errors) {
|
|
1462
1450
|
let idx = initialMatches.findIndex(m => errors[m.route.id] !== undefined);
|
|
1463
|
-
initialized = initialMatches.slice(0, idx + 1).every(
|
|
1451
|
+
initialized = initialMatches.slice(0, idx + 1).every(m => !shouldLoadRouteOnHydration(m.route, loaderData, errors));
|
|
1464
1452
|
} else {
|
|
1465
|
-
initialized = initialMatches.every(
|
|
1453
|
+
initialized = initialMatches.every(m => !shouldLoadRouteOnHydration(m.route, loaderData, errors));
|
|
1466
1454
|
}
|
|
1467
1455
|
} else {
|
|
1468
1456
|
// Without partial hydration - we're initialized if we were provided any
|
|
@@ -1541,9 +1529,6 @@ function createRouter(init) {
|
|
|
1541
1529
|
// Store blocker functions in a separate Map outside of router state since
|
|
1542
1530
|
// we don't need to update UI state if they change
|
|
1543
1531
|
let blockerFunctions = new Map();
|
|
1544
|
-
// Map of pending patchRoutesOnNavigation() promises (keyed by path/matches) so
|
|
1545
|
-
// that we only kick them off once for a given combo
|
|
1546
|
-
let pendingPatchRoutes = new Map();
|
|
1547
1532
|
// Flag to ignore the next history update, so we can revert the URL change on
|
|
1548
1533
|
// a POP navigation that was blocked by the user without touching router state
|
|
1549
1534
|
let unblockBlockerHistoryUpdate = undefined;
|
|
@@ -1914,7 +1899,7 @@ function createRouter(init) {
|
|
|
1914
1899
|
pendingViewTransitionEnabled = (opts && opts.enableViewTransition) === true;
|
|
1915
1900
|
let routesToUse = inFlightDataRoutes || dataRoutes;
|
|
1916
1901
|
let loadingNavigation = opts && opts.overrideNavigation;
|
|
1917
|
-
let matches =
|
|
1902
|
+
let matches = matchRoutes(routesToUse, location, basename);
|
|
1918
1903
|
let flushSync = (opts && opts.flushSync) === true;
|
|
1919
1904
|
let fogOfWar = checkFogOfWar(matches, routesToUse, location.pathname);
|
|
1920
1905
|
if (fogOfWar.active && fogOfWar.matches) {
|
|
@@ -1941,7 +1926,7 @@ function createRouter(init) {
|
|
|
1941
1926
|
// Short circuit if it's only a hash change and not a revalidation or
|
|
1942
1927
|
// mutation submission.
|
|
1943
1928
|
//
|
|
1944
|
-
// Ignore on initial page loads because since the initial
|
|
1929
|
+
// Ignore on initial page loads because since the initial hydration will always
|
|
1945
1930
|
// be "same hash". For example, on /page#hash and submit a <Form method="post">
|
|
1946
1931
|
// which will default to a navigation to /page
|
|
1947
1932
|
if (state.initialized && !isRevalidationRequired && isHashChangeOnly(state.location, location) && !(opts && opts.submission && isMutationMethod(opts.submission.formMethod))) {
|
|
@@ -2245,9 +2230,7 @@ function createRouter(init) {
|
|
|
2245
2230
|
});
|
|
2246
2231
|
}
|
|
2247
2232
|
revalidatingFetchers.forEach(rf => {
|
|
2248
|
-
|
|
2249
|
-
abortFetcher(rf.key);
|
|
2250
|
-
}
|
|
2233
|
+
abortFetcher(rf.key);
|
|
2251
2234
|
if (rf.controller) {
|
|
2252
2235
|
// Fetchers use an independent AbortController so that aborting a fetcher
|
|
2253
2236
|
// (via deleteFetcher) does not abort the triggering navigation that
|
|
@@ -2303,7 +2286,7 @@ function createRouter(init) {
|
|
|
2303
2286
|
let {
|
|
2304
2287
|
loaderData,
|
|
2305
2288
|
errors
|
|
2306
|
-
} = processLoaderData(state, matches,
|
|
2289
|
+
} = processLoaderData(state, matches, loaderResults, pendingActionResult, revalidatingFetchers, fetcherResults, activeDeferreds);
|
|
2307
2290
|
// Wire up subscribers to update loaderData as promises settle
|
|
2308
2291
|
activeDeferreds.forEach((deferredData, routeId) => {
|
|
2309
2292
|
deferredData.subscribe(aborted => {
|
|
@@ -2315,17 +2298,9 @@ function createRouter(init) {
|
|
|
2315
2298
|
}
|
|
2316
2299
|
});
|
|
2317
2300
|
});
|
|
2318
|
-
//
|
|
2301
|
+
// Preserve SSR errors during partial hydration
|
|
2319
2302
|
if (future.v7_partialHydration && initialHydration && state.errors) {
|
|
2320
|
-
|
|
2321
|
-
let [id] = _ref2;
|
|
2322
|
-
return !matchesToLoad.some(m => m.route.id === id);
|
|
2323
|
-
}).forEach(_ref3 => {
|
|
2324
|
-
let [routeId, error] = _ref3;
|
|
2325
|
-
errors = Object.assign(errors || {}, {
|
|
2326
|
-
[routeId]: error
|
|
2327
|
-
});
|
|
2328
|
-
});
|
|
2303
|
+
errors = _extends({}, state.errors, errors);
|
|
2329
2304
|
}
|
|
2330
2305
|
let updatedFetchers = markFetchRedirectsDone();
|
|
2331
2306
|
let didAbortFetchLoads = abortStaleFetchLoads(pendingNavigationLoadId);
|
|
@@ -2367,7 +2342,7 @@ function createRouter(init) {
|
|
|
2367
2342
|
if (isServer) {
|
|
2368
2343
|
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.");
|
|
2369
2344
|
}
|
|
2370
|
-
|
|
2345
|
+
abortFetcher(key);
|
|
2371
2346
|
let flushSync = (opts && opts.flushSync) === true;
|
|
2372
2347
|
let routesToUse = inFlightDataRoutes || dataRoutes;
|
|
2373
2348
|
let normalizedPath = normalizeTo(state.location, state.matches, basename, future.v7_prependBasename, href, future.v7_relativeSplatPath, routeId, opts == null ? void 0 : opts.relative);
|
|
@@ -2396,9 +2371,9 @@ function createRouter(init) {
|
|
|
2396
2371
|
return;
|
|
2397
2372
|
}
|
|
2398
2373
|
let match = getTargetMatch(matches, path);
|
|
2399
|
-
|
|
2374
|
+
let preventScrollReset = (opts && opts.preventScrollReset) === true;
|
|
2400
2375
|
if (submission && isMutationMethod(submission.formMethod)) {
|
|
2401
|
-
handleFetcherAction(key, routeId, path, match, matches, fogOfWar.active, flushSync, submission);
|
|
2376
|
+
handleFetcherAction(key, routeId, path, match, matches, fogOfWar.active, flushSync, preventScrollReset, submission);
|
|
2402
2377
|
return;
|
|
2403
2378
|
}
|
|
2404
2379
|
// Store off the match so we can call it's shouldRevalidate on subsequent
|
|
@@ -2407,11 +2382,11 @@ function createRouter(init) {
|
|
|
2407
2382
|
routeId,
|
|
2408
2383
|
path
|
|
2409
2384
|
});
|
|
2410
|
-
handleFetcherLoader(key, routeId, path, match, matches, fogOfWar.active, flushSync, submission);
|
|
2385
|
+
handleFetcherLoader(key, routeId, path, match, matches, fogOfWar.active, flushSync, preventScrollReset, submission);
|
|
2411
2386
|
}
|
|
2412
2387
|
// Call the action for the matched fetcher.submit(), and then handle redirects,
|
|
2413
2388
|
// errors, and revalidation
|
|
2414
|
-
async function handleFetcherAction(key, routeId, path, match, requestMatches, isFogOfWar, flushSync, submission) {
|
|
2389
|
+
async function handleFetcherAction(key, routeId, path, match, requestMatches, isFogOfWar, flushSync, preventScrollReset, submission) {
|
|
2415
2390
|
interruptActiveLoads();
|
|
2416
2391
|
fetchLoadMatches.delete(key);
|
|
2417
2392
|
function detectAndHandle405Error(m) {
|
|
@@ -2501,7 +2476,8 @@ function createRouter(init) {
|
|
|
2501
2476
|
fetchRedirectIds.add(key);
|
|
2502
2477
|
updateFetcherState(key, getLoadingFetcher(submission));
|
|
2503
2478
|
return startRedirectNavigation(fetchRequest, actionResult, false, {
|
|
2504
|
-
fetcherSubmission: submission
|
|
2479
|
+
fetcherSubmission: submission,
|
|
2480
|
+
preventScrollReset
|
|
2505
2481
|
});
|
|
2506
2482
|
}
|
|
2507
2483
|
}
|
|
@@ -2536,9 +2512,7 @@ function createRouter(init) {
|
|
|
2536
2512
|
let existingFetcher = state.fetchers.get(staleKey);
|
|
2537
2513
|
let revalidatingFetcher = getLoadingFetcher(undefined, existingFetcher ? existingFetcher.data : undefined);
|
|
2538
2514
|
state.fetchers.set(staleKey, revalidatingFetcher);
|
|
2539
|
-
|
|
2540
|
-
abortFetcher(staleKey);
|
|
2541
|
-
}
|
|
2515
|
+
abortFetcher(staleKey);
|
|
2542
2516
|
if (rf.controller) {
|
|
2543
2517
|
fetchControllers.set(staleKey, rf.controller);
|
|
2544
2518
|
}
|
|
@@ -2561,7 +2535,9 @@ function createRouter(init) {
|
|
|
2561
2535
|
revalidatingFetchers.forEach(r => fetchControllers.delete(r.key));
|
|
2562
2536
|
let redirect = findRedirect(loaderResults);
|
|
2563
2537
|
if (redirect) {
|
|
2564
|
-
return startRedirectNavigation(revalidationRequest, redirect.result, false
|
|
2538
|
+
return startRedirectNavigation(revalidationRequest, redirect.result, false, {
|
|
2539
|
+
preventScrollReset
|
|
2540
|
+
});
|
|
2565
2541
|
}
|
|
2566
2542
|
redirect = findRedirect(fetcherResults);
|
|
2567
2543
|
if (redirect) {
|
|
@@ -2569,13 +2545,15 @@ function createRouter(init) {
|
|
|
2569
2545
|
// fetchRedirectIds so it doesn't get revalidated on the next set of
|
|
2570
2546
|
// loader executions
|
|
2571
2547
|
fetchRedirectIds.add(redirect.key);
|
|
2572
|
-
return startRedirectNavigation(revalidationRequest, redirect.result, false
|
|
2548
|
+
return startRedirectNavigation(revalidationRequest, redirect.result, false, {
|
|
2549
|
+
preventScrollReset
|
|
2550
|
+
});
|
|
2573
2551
|
}
|
|
2574
2552
|
// Process and commit output from loaders
|
|
2575
2553
|
let {
|
|
2576
2554
|
loaderData,
|
|
2577
2555
|
errors
|
|
2578
|
-
} = processLoaderData(state, matches,
|
|
2556
|
+
} = processLoaderData(state, matches, loaderResults, undefined, revalidatingFetchers, fetcherResults, activeDeferreds);
|
|
2579
2557
|
// Since we let revalidations complete even if the submitting fetcher was
|
|
2580
2558
|
// deleted, only put it back to idle if it hasn't been deleted
|
|
2581
2559
|
if (state.fetchers.has(key)) {
|
|
@@ -2608,7 +2586,7 @@ function createRouter(init) {
|
|
|
2608
2586
|
}
|
|
2609
2587
|
}
|
|
2610
2588
|
// Call the matched loader for fetcher.load(), handling redirects, errors, etc.
|
|
2611
|
-
async function handleFetcherLoader(key, routeId, path, match, matches, isFogOfWar, flushSync, submission) {
|
|
2589
|
+
async function handleFetcherLoader(key, routeId, path, match, matches, isFogOfWar, flushSync, preventScrollReset, submission) {
|
|
2612
2590
|
let existingFetcher = state.fetchers.get(key);
|
|
2613
2591
|
updateFetcherState(key, getLoadingFetcher(submission, existingFetcher ? existingFetcher.data : undefined), {
|
|
2614
2592
|
flushSync
|
|
@@ -2674,7 +2652,9 @@ function createRouter(init) {
|
|
|
2674
2652
|
return;
|
|
2675
2653
|
} else {
|
|
2676
2654
|
fetchRedirectIds.add(key);
|
|
2677
|
-
await startRedirectNavigation(fetchRequest, result, false
|
|
2655
|
+
await startRedirectNavigation(fetchRequest, result, false, {
|
|
2656
|
+
preventScrollReset
|
|
2657
|
+
});
|
|
2678
2658
|
return;
|
|
2679
2659
|
}
|
|
2680
2660
|
}
|
|
@@ -2710,6 +2690,7 @@ function createRouter(init) {
|
|
|
2710
2690
|
let {
|
|
2711
2691
|
submission,
|
|
2712
2692
|
fetcherSubmission,
|
|
2693
|
+
preventScrollReset,
|
|
2713
2694
|
replace
|
|
2714
2695
|
} = _temp2 === void 0 ? {} : _temp2;
|
|
2715
2696
|
if (redirect.response.headers.has("X-Remix-Revalidate")) {
|
|
@@ -2767,7 +2748,7 @@ function createRouter(init) {
|
|
|
2767
2748
|
formAction: location
|
|
2768
2749
|
}),
|
|
2769
2750
|
// Preserve these flags across redirects
|
|
2770
|
-
preventScrollReset: pendingPreventScrollReset,
|
|
2751
|
+
preventScrollReset: preventScrollReset || pendingPreventScrollReset,
|
|
2771
2752
|
enableViewTransition: isNavigation ? pendingViewTransitionEnabled : undefined
|
|
2772
2753
|
});
|
|
2773
2754
|
} else {
|
|
@@ -2779,7 +2760,7 @@ function createRouter(init) {
|
|
|
2779
2760
|
// Send fetcher submissions through for shouldRevalidate
|
|
2780
2761
|
fetcherSubmission,
|
|
2781
2762
|
// Preserve these flags across redirects
|
|
2782
|
-
preventScrollReset: pendingPreventScrollReset,
|
|
2763
|
+
preventScrollReset: preventScrollReset || pendingPreventScrollReset,
|
|
2783
2764
|
enableViewTransition: isNavigation ? pendingViewTransitionEnabled : undefined
|
|
2784
2765
|
});
|
|
2785
2766
|
}
|
|
@@ -2856,8 +2837,8 @@ function createRouter(init) {
|
|
|
2856
2837
|
fetchLoadMatches.forEach((_, key) => {
|
|
2857
2838
|
if (fetchControllers.has(key)) {
|
|
2858
2839
|
cancelledFetcherLoads.add(key);
|
|
2859
|
-
abortFetcher(key);
|
|
2860
2840
|
}
|
|
2841
|
+
abortFetcher(key);
|
|
2861
2842
|
});
|
|
2862
2843
|
}
|
|
2863
2844
|
function updateFetcherState(key, fetcher, opts) {
|
|
@@ -2930,9 +2911,10 @@ function createRouter(init) {
|
|
|
2930
2911
|
}
|
|
2931
2912
|
function abortFetcher(key) {
|
|
2932
2913
|
let controller = fetchControllers.get(key);
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2914
|
+
if (controller) {
|
|
2915
|
+
controller.abort();
|
|
2916
|
+
fetchControllers.delete(key);
|
|
2917
|
+
}
|
|
2936
2918
|
}
|
|
2937
2919
|
function markFetchersDone(keys) {
|
|
2938
2920
|
for (let key of keys) {
|
|
@@ -2995,12 +2977,12 @@ function createRouter(init) {
|
|
|
2995
2977
|
blockers
|
|
2996
2978
|
});
|
|
2997
2979
|
}
|
|
2998
|
-
function shouldBlockNavigation(
|
|
2980
|
+
function shouldBlockNavigation(_ref2) {
|
|
2999
2981
|
let {
|
|
3000
2982
|
currentLocation,
|
|
3001
2983
|
nextLocation,
|
|
3002
2984
|
historyAction
|
|
3003
|
-
} =
|
|
2985
|
+
} = _ref2;
|
|
3004
2986
|
if (blockerFunctions.size === 0) {
|
|
3005
2987
|
return;
|
|
3006
2988
|
}
|
|
@@ -3142,12 +3124,26 @@ function createRouter(init) {
|
|
|
3142
3124
|
};
|
|
3143
3125
|
}
|
|
3144
3126
|
async function discoverRoutes(matches, pathname, signal) {
|
|
3127
|
+
if (!patchRoutesOnNavigationImpl) {
|
|
3128
|
+
return {
|
|
3129
|
+
type: "success",
|
|
3130
|
+
matches
|
|
3131
|
+
};
|
|
3132
|
+
}
|
|
3145
3133
|
let partialMatches = matches;
|
|
3146
3134
|
while (true) {
|
|
3147
3135
|
let isNonHMR = inFlightDataRoutes == null;
|
|
3148
3136
|
let routesToUse = inFlightDataRoutes || dataRoutes;
|
|
3137
|
+
let localManifest = manifest;
|
|
3149
3138
|
try {
|
|
3150
|
-
await
|
|
3139
|
+
await patchRoutesOnNavigationImpl({
|
|
3140
|
+
path: pathname,
|
|
3141
|
+
matches: partialMatches,
|
|
3142
|
+
patch: (routeId, children) => {
|
|
3143
|
+
if (signal.aborted) return;
|
|
3144
|
+
patchRoutesImpl(routeId, children, routesToUse, localManifest, mapRouteProperties);
|
|
3145
|
+
}
|
|
3146
|
+
});
|
|
3151
3147
|
} catch (e) {
|
|
3152
3148
|
return {
|
|
3153
3149
|
type: "error",
|
|
@@ -3161,7 +3157,7 @@ function createRouter(init) {
|
|
|
3161
3157
|
// trigger a re-run of memoized `router.routes` dependencies.
|
|
3162
3158
|
// HMR will already update the identity and reflow when it lands
|
|
3163
3159
|
// `inFlightDataRoutes` in `completeNavigation`
|
|
3164
|
-
if (isNonHMR) {
|
|
3160
|
+
if (isNonHMR && !signal.aborted) {
|
|
3165
3161
|
dataRoutes = [...dataRoutes];
|
|
3166
3162
|
}
|
|
3167
3163
|
}
|
|
@@ -3712,9 +3708,21 @@ function normalizeTo(location, matches, basename, prependBasename, to, v7_relati
|
|
|
3712
3708
|
path.search = location.search;
|
|
3713
3709
|
path.hash = location.hash;
|
|
3714
3710
|
}
|
|
3715
|
-
//
|
|
3716
|
-
if ((to == null || to === "" || to === ".") && activeRouteMatch
|
|
3717
|
-
|
|
3711
|
+
// Account for `?index` params when routing to the current location
|
|
3712
|
+
if ((to == null || to === "" || to === ".") && activeRouteMatch) {
|
|
3713
|
+
let nakedIndex = hasNakedIndexQuery(path.search);
|
|
3714
|
+
if (activeRouteMatch.route.index && !nakedIndex) {
|
|
3715
|
+
// Add one when we're targeting an index route
|
|
3716
|
+
path.search = path.search ? path.search.replace(/^\?/, "?index&") : "?index";
|
|
3717
|
+
} else if (!activeRouteMatch.route.index && nakedIndex) {
|
|
3718
|
+
// Remove existing ones when we're not
|
|
3719
|
+
let params = new URLSearchParams(path.search);
|
|
3720
|
+
let indexValues = params.getAll("index");
|
|
3721
|
+
params.delete("index");
|
|
3722
|
+
indexValues.filter(v => v).forEach(v => params.append("index", v));
|
|
3723
|
+
let qs = params.toString();
|
|
3724
|
+
path.search = qs ? "?" + qs : "";
|
|
3725
|
+
}
|
|
3718
3726
|
}
|
|
3719
3727
|
// If we're operating within a basename, prepend it to the pathname. If
|
|
3720
3728
|
// this is a root navigation, then just use the raw basename which allows
|
|
@@ -3760,8 +3768,8 @@ function normalizeNavigateOptions(normalizeFormMethod, isFetcher, path, opts) {
|
|
|
3760
3768
|
}
|
|
3761
3769
|
let text = typeof opts.body === "string" ? opts.body : opts.body instanceof FormData || opts.body instanceof URLSearchParams ?
|
|
3762
3770
|
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#plain-text-form-data
|
|
3763
|
-
Array.from(opts.body.entries()).reduce((acc,
|
|
3764
|
-
let [name, value] =
|
|
3771
|
+
Array.from(opts.body.entries()).reduce((acc, _ref3) => {
|
|
3772
|
+
let [name, value] = _ref3;
|
|
3765
3773
|
return "" + acc + name + "=" + value + "\n";
|
|
3766
3774
|
}, "") : String(opts.body);
|
|
3767
3775
|
return {
|
|
@@ -3849,25 +3857,36 @@ function normalizeNavigateOptions(normalizeFormMethod, isFetcher, path, opts) {
|
|
|
3849
3857
|
submission
|
|
3850
3858
|
};
|
|
3851
3859
|
}
|
|
3852
|
-
// Filter out all routes below any caught error as they aren't going to
|
|
3860
|
+
// Filter out all routes at/below any caught error as they aren't going to
|
|
3853
3861
|
// render so we don't need to load them
|
|
3854
|
-
function getLoaderMatchesUntilBoundary(matches, boundaryId) {
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
let index = matches.findIndex(m => m.route.id === boundaryId);
|
|
3858
|
-
if (index >= 0) {
|
|
3859
|
-
boundaryMatches = matches.slice(0, index);
|
|
3860
|
-
}
|
|
3862
|
+
function getLoaderMatchesUntilBoundary(matches, boundaryId, includeBoundary) {
|
|
3863
|
+
if (includeBoundary === void 0) {
|
|
3864
|
+
includeBoundary = false;
|
|
3861
3865
|
}
|
|
3862
|
-
|
|
3866
|
+
let index = matches.findIndex(m => m.route.id === boundaryId);
|
|
3867
|
+
if (index >= 0) {
|
|
3868
|
+
return matches.slice(0, includeBoundary ? index + 1 : index);
|
|
3869
|
+
}
|
|
3870
|
+
return matches;
|
|
3863
3871
|
}
|
|
3864
|
-
function getMatchesToLoad(history, state, matches, submission, location,
|
|
3872
|
+
function getMatchesToLoad(history, state, matches, submission, location, initialHydration, skipActionErrorRevalidation, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, deletedFetchers, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, pendingActionResult) {
|
|
3865
3873
|
let actionResult = pendingActionResult ? isErrorResult(pendingActionResult[1]) ? pendingActionResult[1].error : pendingActionResult[1].data : undefined;
|
|
3866
3874
|
let currentUrl = history.createURL(state.location);
|
|
3867
3875
|
let nextUrl = history.createURL(location);
|
|
3868
3876
|
// Pick navigation matches that are net-new or qualify for revalidation
|
|
3869
|
-
let
|
|
3870
|
-
|
|
3877
|
+
let boundaryMatches = matches;
|
|
3878
|
+
if (initialHydration && state.errors) {
|
|
3879
|
+
// On initial hydration, only consider matches up to _and including_ the boundary.
|
|
3880
|
+
// This is inclusive to handle cases where a server loader ran successfully,
|
|
3881
|
+
// a child server loader bubbled up to this route, but this route has
|
|
3882
|
+
// `clientLoader.hydrate` so we want to still run the `clientLoader` so that
|
|
3883
|
+
// we have a complete version of `loaderData`
|
|
3884
|
+
boundaryMatches = getLoaderMatchesUntilBoundary(matches, Object.keys(state.errors)[0], true);
|
|
3885
|
+
} else if (pendingActionResult && isErrorResult(pendingActionResult[1])) {
|
|
3886
|
+
// If an action threw an error, we call loaders up to, but not including the
|
|
3887
|
+
// boundary
|
|
3888
|
+
boundaryMatches = getLoaderMatchesUntilBoundary(matches, pendingActionResult[0]);
|
|
3889
|
+
}
|
|
3871
3890
|
// Don't revalidate loaders by default after action 4xx/5xx responses
|
|
3872
3891
|
// when the flag is enabled. They can still opt-into revalidation via
|
|
3873
3892
|
// `shouldRevalidate` via `actionResult`
|
|
@@ -3884,13 +3903,8 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
|
|
|
3884
3903
|
if (route.loader == null) {
|
|
3885
3904
|
return false;
|
|
3886
3905
|
}
|
|
3887
|
-
if (
|
|
3888
|
-
|
|
3889
|
-
return true;
|
|
3890
|
-
}
|
|
3891
|
-
return state.loaderData[route.id] === undefined && (
|
|
3892
|
-
// Don't re-run if the loader ran and threw an error
|
|
3893
|
-
!state.errors || state.errors[route.id] === undefined);
|
|
3906
|
+
if (initialHydration) {
|
|
3907
|
+
return shouldLoadRouteOnHydration(route, state.loaderData, state.errors);
|
|
3894
3908
|
}
|
|
3895
3909
|
// Always call the loader on new route instances and pending defer cancellations
|
|
3896
3910
|
if (isNewLoader(state.loaderData, state.matches[index], match) || cancelledDeferredRoutes.some(id => id === match.route.id)) {
|
|
@@ -3921,11 +3935,11 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
|
|
|
3921
3935
|
let revalidatingFetchers = [];
|
|
3922
3936
|
fetchLoadMatches.forEach((f, key) => {
|
|
3923
3937
|
// Don't revalidate:
|
|
3924
|
-
// - on initial
|
|
3938
|
+
// - on initial hydration (shouldn't be any fetchers then anyway)
|
|
3925
3939
|
// - if fetcher won't be present in the subsequent render
|
|
3926
3940
|
// - no longer matches the URL (v7_fetcherPersist=false)
|
|
3927
3941
|
// - was unmounted but persisted due to v7_fetcherPersist=true
|
|
3928
|
-
if (
|
|
3942
|
+
if (initialHydration || !matches.some(m => m.route.id === f.routeId) || deletedFetchers.has(key)) {
|
|
3929
3943
|
return;
|
|
3930
3944
|
}
|
|
3931
3945
|
let fetcherMatches = matchRoutes(routesToUse, f.path, basename);
|
|
@@ -3989,6 +4003,28 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
|
|
|
3989
4003
|
});
|
|
3990
4004
|
return [navigationMatches, revalidatingFetchers];
|
|
3991
4005
|
}
|
|
4006
|
+
function shouldLoadRouteOnHydration(route, loaderData, errors) {
|
|
4007
|
+
// We dunno if we have a loader - gotta find out!
|
|
4008
|
+
if (route.lazy) {
|
|
4009
|
+
return true;
|
|
4010
|
+
}
|
|
4011
|
+
// No loader, nothing to initialize
|
|
4012
|
+
if (!route.loader) {
|
|
4013
|
+
return false;
|
|
4014
|
+
}
|
|
4015
|
+
let hasData = loaderData != null && loaderData[route.id] !== undefined;
|
|
4016
|
+
let hasError = errors != null && errors[route.id] !== undefined;
|
|
4017
|
+
// Don't run if we error'd during SSR
|
|
4018
|
+
if (!hasData && hasError) {
|
|
4019
|
+
return false;
|
|
4020
|
+
}
|
|
4021
|
+
// Explicitly opting-in to running on hydration
|
|
4022
|
+
if (typeof route.loader === "function" && route.loader.hydrate === true) {
|
|
4023
|
+
return true;
|
|
4024
|
+
}
|
|
4025
|
+
// Otherwise, run if we're not yet initialized with anything
|
|
4026
|
+
return !hasData && !hasError;
|
|
4027
|
+
}
|
|
3992
4028
|
function isNewLoader(currentLoaderData, currentMatch, match) {
|
|
3993
4029
|
let isNew =
|
|
3994
4030
|
// [a] -> [a, b]
|
|
@@ -4020,33 +4056,6 @@ function shouldRevalidateLoader(loaderMatch, arg) {
|
|
|
4020
4056
|
}
|
|
4021
4057
|
return arg.defaultShouldRevalidate;
|
|
4022
4058
|
}
|
|
4023
|
-
/**
|
|
4024
|
-
* Idempotent utility to execute patchRoutesOnNavigation() to lazily load route
|
|
4025
|
-
* definitions and update the routes/routeManifest
|
|
4026
|
-
*/
|
|
4027
|
-
async function loadLazyRouteChildren(patchRoutesOnNavigationImpl, path, matches, routes, manifest, mapRouteProperties, pendingRouteChildren, signal) {
|
|
4028
|
-
let key = [path, ...matches.map(m => m.route.id)].join("-");
|
|
4029
|
-
try {
|
|
4030
|
-
let pending = pendingRouteChildren.get(key);
|
|
4031
|
-
if (!pending) {
|
|
4032
|
-
pending = patchRoutesOnNavigationImpl({
|
|
4033
|
-
path,
|
|
4034
|
-
matches,
|
|
4035
|
-
patch: (routeId, children) => {
|
|
4036
|
-
if (!signal.aborted) {
|
|
4037
|
-
patchRoutesImpl(routeId, children, routes, manifest, mapRouteProperties);
|
|
4038
|
-
}
|
|
4039
|
-
}
|
|
4040
|
-
});
|
|
4041
|
-
pendingRouteChildren.set(key, pending);
|
|
4042
|
-
}
|
|
4043
|
-
if (pending && isPromise(pending)) {
|
|
4044
|
-
await pending;
|
|
4045
|
-
}
|
|
4046
|
-
} finally {
|
|
4047
|
-
pendingRouteChildren.delete(key);
|
|
4048
|
-
}
|
|
4049
|
-
}
|
|
4050
4059
|
function patchRoutesImpl(routeId, children, routesToUse, manifest, mapRouteProperties) {
|
|
4051
4060
|
var _childrenToPatch;
|
|
4052
4061
|
let childrenToPatch;
|
|
@@ -4063,10 +4072,31 @@ function patchRoutesImpl(routeId, children, routesToUse, manifest, mapRoutePrope
|
|
|
4063
4072
|
// Don't patch in routes we already know about so that `patch` is idempotent
|
|
4064
4073
|
// to simplify user-land code. This is useful because we re-call the
|
|
4065
4074
|
// `patchRoutesOnNavigation` function for matched routes with params.
|
|
4066
|
-
let uniqueChildren = children.filter(
|
|
4075
|
+
let uniqueChildren = children.filter(newRoute => !childrenToPatch.some(existingRoute => isSameRoute(newRoute, existingRoute)));
|
|
4067
4076
|
let newRoutes = convertRoutesToDataRoutes(uniqueChildren, mapRouteProperties, [routeId || "_", "patch", String(((_childrenToPatch = childrenToPatch) == null ? void 0 : _childrenToPatch.length) || "0")], manifest);
|
|
4068
4077
|
childrenToPatch.push(...newRoutes);
|
|
4069
4078
|
}
|
|
4079
|
+
function isSameRoute(newRoute, existingRoute) {
|
|
4080
|
+
// Most optimal check is by id
|
|
4081
|
+
if ("id" in newRoute && "id" in existingRoute && newRoute.id === existingRoute.id) {
|
|
4082
|
+
return true;
|
|
4083
|
+
}
|
|
4084
|
+
// Second is by pathing differences
|
|
4085
|
+
if (!(newRoute.index === existingRoute.index && newRoute.path === existingRoute.path && newRoute.caseSensitive === existingRoute.caseSensitive)) {
|
|
4086
|
+
return false;
|
|
4087
|
+
}
|
|
4088
|
+
// Pathless layout routes are trickier since we need to check children.
|
|
4089
|
+
// If they have no children then they're the same as far as we can tell
|
|
4090
|
+
if ((!newRoute.children || newRoute.children.length === 0) && (!existingRoute.children || existingRoute.children.length === 0)) {
|
|
4091
|
+
return true;
|
|
4092
|
+
}
|
|
4093
|
+
// Otherwise, we look to see if every child in the new route is already
|
|
4094
|
+
// represented in the existing route's children
|
|
4095
|
+
return newRoute.children.every((aChild, i) => {
|
|
4096
|
+
var _existingRoute$childr;
|
|
4097
|
+
return (_existingRoute$childr = existingRoute.children) == null ? void 0 : _existingRoute$childr.some(bChild => isSameRoute(aChild, bChild));
|
|
4098
|
+
});
|
|
4099
|
+
}
|
|
4070
4100
|
/**
|
|
4071
4101
|
* Execute route.lazy() methods to lazily load route modules (loader, action,
|
|
4072
4102
|
* shouldRevalidate) and update the routeManifest in place which shares objects
|
|
@@ -4116,10 +4146,10 @@ async function loadLazyRouteModule(route, mapRouteProperties, manifest) {
|
|
|
4116
4146
|
}));
|
|
4117
4147
|
}
|
|
4118
4148
|
// Default implementation of `dataStrategy` which fetches all loaders in parallel
|
|
4119
|
-
async function defaultDataStrategy(
|
|
4149
|
+
async function defaultDataStrategy(_ref4) {
|
|
4120
4150
|
let {
|
|
4121
4151
|
matches
|
|
4122
|
-
} =
|
|
4152
|
+
} = _ref4;
|
|
4123
4153
|
let matchesToLoad = matches.filter(m => m.shouldLoad);
|
|
4124
4154
|
let results = await Promise.all(matchesToLoad.map(m => m.resolve()));
|
|
4125
4155
|
return results.reduce((acc, result, i) => Object.assign(acc, {
|
|
@@ -4522,7 +4552,7 @@ function processRouteLoaderData(matches, results, pendingActionResult, activeDef
|
|
|
4522
4552
|
loaderHeaders
|
|
4523
4553
|
};
|
|
4524
4554
|
}
|
|
4525
|
-
function processLoaderData(state, matches,
|
|
4555
|
+
function processLoaderData(state, matches, results, pendingActionResult, revalidatingFetchers, fetcherResults, activeDeferreds) {
|
|
4526
4556
|
let {
|
|
4527
4557
|
loaderData,
|
|
4528
4558
|
errors
|
|
@@ -4696,9 +4726,6 @@ function isHashChangeOnly(a, b) {
|
|
|
4696
4726
|
// /page#hash -> /page
|
|
4697
4727
|
return false;
|
|
4698
4728
|
}
|
|
4699
|
-
function isPromise(val) {
|
|
4700
|
-
return typeof val === "object" && val != null && "then" in val;
|
|
4701
|
-
}
|
|
4702
4729
|
function isDataStrategyResult(result) {
|
|
4703
4730
|
return result != null && typeof result === "object" && "type" in result && "result" in result && (result.type === ResultType.data || result.type === ResultType.error);
|
|
4704
4731
|
}
|