@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.cjs.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
|
*
|
|
@@ -1797,25 +1797,12 @@ function createRouter(init) {
|
|
|
1797
1797
|
// were marked for explicit hydration
|
|
1798
1798
|
let loaderData = init.hydrationData ? init.hydrationData.loaderData : null;
|
|
1799
1799
|
let errors = init.hydrationData ? init.hydrationData.errors : null;
|
|
1800
|
-
let isRouteInitialized = m => {
|
|
1801
|
-
// No loader, nothing to initialize
|
|
1802
|
-
if (!m.route.loader) {
|
|
1803
|
-
return true;
|
|
1804
|
-
}
|
|
1805
|
-
// Explicitly opting-in to running on hydration
|
|
1806
|
-
if (typeof m.route.loader === "function" && m.route.loader.hydrate === true) {
|
|
1807
|
-
return false;
|
|
1808
|
-
}
|
|
1809
|
-
// Otherwise, initialized if hydrated with data or an error
|
|
1810
|
-
return loaderData && loaderData[m.route.id] !== undefined || errors && errors[m.route.id] !== undefined;
|
|
1811
|
-
};
|
|
1812
|
-
|
|
1813
1800
|
// If errors exist, don't consider routes below the boundary
|
|
1814
1801
|
if (errors) {
|
|
1815
1802
|
let idx = initialMatches.findIndex(m => errors[m.route.id] !== undefined);
|
|
1816
|
-
initialized = initialMatches.slice(0, idx + 1).every(
|
|
1803
|
+
initialized = initialMatches.slice(0, idx + 1).every(m => !shouldLoadRouteOnHydration(m.route, loaderData, errors));
|
|
1817
1804
|
} else {
|
|
1818
|
-
initialized = initialMatches.every(
|
|
1805
|
+
initialized = initialMatches.every(m => !shouldLoadRouteOnHydration(m.route, loaderData, errors));
|
|
1819
1806
|
}
|
|
1820
1807
|
} else {
|
|
1821
1808
|
// Without partial hydration - we're initialized if we were provided any
|
|
@@ -1915,10 +1902,6 @@ function createRouter(init) {
|
|
|
1915
1902
|
// we don't need to update UI state if they change
|
|
1916
1903
|
let blockerFunctions = new Map();
|
|
1917
1904
|
|
|
1918
|
-
// Map of pending patchRoutesOnNavigation() promises (keyed by path/matches) so
|
|
1919
|
-
// that we only kick them off once for a given combo
|
|
1920
|
-
let pendingPatchRoutes = new Map();
|
|
1921
|
-
|
|
1922
1905
|
// Flag to ignore the next history update, so we can revert the URL change on
|
|
1923
1906
|
// a POP navigation that was blocked by the user without touching router state
|
|
1924
1907
|
let unblockBlockerHistoryUpdate = undefined;
|
|
@@ -2314,7 +2297,7 @@ function createRouter(init) {
|
|
|
2314
2297
|
pendingViewTransitionEnabled = (opts && opts.enableViewTransition) === true;
|
|
2315
2298
|
let routesToUse = inFlightDataRoutes || dataRoutes;
|
|
2316
2299
|
let loadingNavigation = opts && opts.overrideNavigation;
|
|
2317
|
-
let matches =
|
|
2300
|
+
let matches = matchRoutes(routesToUse, location, basename);
|
|
2318
2301
|
let flushSync = (opts && opts.flushSync) === true;
|
|
2319
2302
|
let fogOfWar = checkFogOfWar(matches, routesToUse, location.pathname);
|
|
2320
2303
|
if (fogOfWar.active && fogOfWar.matches) {
|
|
@@ -2343,7 +2326,7 @@ function createRouter(init) {
|
|
|
2343
2326
|
// Short circuit if it's only a hash change and not a revalidation or
|
|
2344
2327
|
// mutation submission.
|
|
2345
2328
|
//
|
|
2346
|
-
// Ignore on initial page loads because since the initial
|
|
2329
|
+
// Ignore on initial page loads because since the initial hydration will always
|
|
2347
2330
|
// be "same hash". For example, on /page#hash and submit a <Form method="post">
|
|
2348
2331
|
// which will default to a navigation to /page
|
|
2349
2332
|
if (state.initialized && !isRevalidationRequired && isHashChangeOnly(state.location, location) && !(opts && opts.submission && isMutationMethod(opts.submission.formMethod))) {
|
|
@@ -2662,9 +2645,7 @@ function createRouter(init) {
|
|
|
2662
2645
|
});
|
|
2663
2646
|
}
|
|
2664
2647
|
revalidatingFetchers.forEach(rf => {
|
|
2665
|
-
|
|
2666
|
-
abortFetcher(rf.key);
|
|
2667
|
-
}
|
|
2648
|
+
abortFetcher(rf.key);
|
|
2668
2649
|
if (rf.controller) {
|
|
2669
2650
|
// Fetchers use an independent AbortController so that aborting a fetcher
|
|
2670
2651
|
// (via deleteFetcher) does not abort the triggering navigation that
|
|
@@ -2724,7 +2705,7 @@ function createRouter(init) {
|
|
|
2724
2705
|
let {
|
|
2725
2706
|
loaderData,
|
|
2726
2707
|
errors
|
|
2727
|
-
} = processLoaderData(state, matches,
|
|
2708
|
+
} = processLoaderData(state, matches, loaderResults, pendingActionResult, revalidatingFetchers, fetcherResults, activeDeferreds);
|
|
2728
2709
|
|
|
2729
2710
|
// Wire up subscribers to update loaderData as promises settle
|
|
2730
2711
|
activeDeferreds.forEach((deferredData, routeId) => {
|
|
@@ -2738,17 +2719,9 @@ function createRouter(init) {
|
|
|
2738
2719
|
});
|
|
2739
2720
|
});
|
|
2740
2721
|
|
|
2741
|
-
//
|
|
2722
|
+
// Preserve SSR errors during partial hydration
|
|
2742
2723
|
if (future.v7_partialHydration && initialHydration && state.errors) {
|
|
2743
|
-
|
|
2744
|
-
let [id] = _ref2;
|
|
2745
|
-
return !matchesToLoad.some(m => m.route.id === id);
|
|
2746
|
-
}).forEach(_ref3 => {
|
|
2747
|
-
let [routeId, error] = _ref3;
|
|
2748
|
-
errors = Object.assign(errors || {}, {
|
|
2749
|
-
[routeId]: error
|
|
2750
|
-
});
|
|
2751
|
-
});
|
|
2724
|
+
errors = _extends({}, state.errors, errors);
|
|
2752
2725
|
}
|
|
2753
2726
|
let updatedFetchers = markFetchRedirectsDone();
|
|
2754
2727
|
let didAbortFetchLoads = abortStaleFetchLoads(pendingNavigationLoadId);
|
|
@@ -2791,7 +2764,7 @@ function createRouter(init) {
|
|
|
2791
2764
|
if (isServer) {
|
|
2792
2765
|
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.");
|
|
2793
2766
|
}
|
|
2794
|
-
|
|
2767
|
+
abortFetcher(key);
|
|
2795
2768
|
let flushSync = (opts && opts.flushSync) === true;
|
|
2796
2769
|
let routesToUse = inFlightDataRoutes || dataRoutes;
|
|
2797
2770
|
let normalizedPath = normalizeTo(state.location, state.matches, basename, future.v7_prependBasename, href, future.v7_relativeSplatPath, routeId, opts == null ? void 0 : opts.relative);
|
|
@@ -2820,9 +2793,9 @@ function createRouter(init) {
|
|
|
2820
2793
|
return;
|
|
2821
2794
|
}
|
|
2822
2795
|
let match = getTargetMatch(matches, path);
|
|
2823
|
-
|
|
2796
|
+
let preventScrollReset = (opts && opts.preventScrollReset) === true;
|
|
2824
2797
|
if (submission && isMutationMethod(submission.formMethod)) {
|
|
2825
|
-
handleFetcherAction(key, routeId, path, match, matches, fogOfWar.active, flushSync, submission);
|
|
2798
|
+
handleFetcherAction(key, routeId, path, match, matches, fogOfWar.active, flushSync, preventScrollReset, submission);
|
|
2826
2799
|
return;
|
|
2827
2800
|
}
|
|
2828
2801
|
|
|
@@ -2832,12 +2805,12 @@ function createRouter(init) {
|
|
|
2832
2805
|
routeId,
|
|
2833
2806
|
path
|
|
2834
2807
|
});
|
|
2835
|
-
handleFetcherLoader(key, routeId, path, match, matches, fogOfWar.active, flushSync, submission);
|
|
2808
|
+
handleFetcherLoader(key, routeId, path, match, matches, fogOfWar.active, flushSync, preventScrollReset, submission);
|
|
2836
2809
|
}
|
|
2837
2810
|
|
|
2838
2811
|
// Call the action for the matched fetcher.submit(), and then handle redirects,
|
|
2839
2812
|
// errors, and revalidation
|
|
2840
|
-
async function handleFetcherAction(key, routeId, path, match, requestMatches, isFogOfWar, flushSync, submission) {
|
|
2813
|
+
async function handleFetcherAction(key, routeId, path, match, requestMatches, isFogOfWar, flushSync, preventScrollReset, submission) {
|
|
2841
2814
|
interruptActiveLoads();
|
|
2842
2815
|
fetchLoadMatches.delete(key);
|
|
2843
2816
|
function detectAndHandle405Error(m) {
|
|
@@ -2930,7 +2903,8 @@ function createRouter(init) {
|
|
|
2930
2903
|
fetchRedirectIds.add(key);
|
|
2931
2904
|
updateFetcherState(key, getLoadingFetcher(submission));
|
|
2932
2905
|
return startRedirectNavigation(fetchRequest, actionResult, false, {
|
|
2933
|
-
fetcherSubmission: submission
|
|
2906
|
+
fetcherSubmission: submission,
|
|
2907
|
+
preventScrollReset
|
|
2934
2908
|
});
|
|
2935
2909
|
}
|
|
2936
2910
|
}
|
|
@@ -2968,9 +2942,7 @@ function createRouter(init) {
|
|
|
2968
2942
|
let existingFetcher = state.fetchers.get(staleKey);
|
|
2969
2943
|
let revalidatingFetcher = getLoadingFetcher(undefined, existingFetcher ? existingFetcher.data : undefined);
|
|
2970
2944
|
state.fetchers.set(staleKey, revalidatingFetcher);
|
|
2971
|
-
|
|
2972
|
-
abortFetcher(staleKey);
|
|
2973
|
-
}
|
|
2945
|
+
abortFetcher(staleKey);
|
|
2974
2946
|
if (rf.controller) {
|
|
2975
2947
|
fetchControllers.set(staleKey, rf.controller);
|
|
2976
2948
|
}
|
|
@@ -2993,7 +2965,9 @@ function createRouter(init) {
|
|
|
2993
2965
|
revalidatingFetchers.forEach(r => fetchControllers.delete(r.key));
|
|
2994
2966
|
let redirect = findRedirect(loaderResults);
|
|
2995
2967
|
if (redirect) {
|
|
2996
|
-
return startRedirectNavigation(revalidationRequest, redirect.result, false
|
|
2968
|
+
return startRedirectNavigation(revalidationRequest, redirect.result, false, {
|
|
2969
|
+
preventScrollReset
|
|
2970
|
+
});
|
|
2997
2971
|
}
|
|
2998
2972
|
redirect = findRedirect(fetcherResults);
|
|
2999
2973
|
if (redirect) {
|
|
@@ -3001,14 +2975,16 @@ function createRouter(init) {
|
|
|
3001
2975
|
// fetchRedirectIds so it doesn't get revalidated on the next set of
|
|
3002
2976
|
// loader executions
|
|
3003
2977
|
fetchRedirectIds.add(redirect.key);
|
|
3004
|
-
return startRedirectNavigation(revalidationRequest, redirect.result, false
|
|
2978
|
+
return startRedirectNavigation(revalidationRequest, redirect.result, false, {
|
|
2979
|
+
preventScrollReset
|
|
2980
|
+
});
|
|
3005
2981
|
}
|
|
3006
2982
|
|
|
3007
2983
|
// Process and commit output from loaders
|
|
3008
2984
|
let {
|
|
3009
2985
|
loaderData,
|
|
3010
2986
|
errors
|
|
3011
|
-
} = processLoaderData(state, matches,
|
|
2987
|
+
} = processLoaderData(state, matches, loaderResults, undefined, revalidatingFetchers, fetcherResults, activeDeferreds);
|
|
3012
2988
|
|
|
3013
2989
|
// Since we let revalidations complete even if the submitting fetcher was
|
|
3014
2990
|
// deleted, only put it back to idle if it hasn't been deleted
|
|
@@ -3044,7 +3020,7 @@ function createRouter(init) {
|
|
|
3044
3020
|
}
|
|
3045
3021
|
|
|
3046
3022
|
// Call the matched loader for fetcher.load(), handling redirects, errors, etc.
|
|
3047
|
-
async function handleFetcherLoader(key, routeId, path, match, matches, isFogOfWar, flushSync, submission) {
|
|
3023
|
+
async function handleFetcherLoader(key, routeId, path, match, matches, isFogOfWar, flushSync, preventScrollReset, submission) {
|
|
3048
3024
|
let existingFetcher = state.fetchers.get(key);
|
|
3049
3025
|
updateFetcherState(key, getLoadingFetcher(submission, existingFetcher ? existingFetcher.data : undefined), {
|
|
3050
3026
|
flushSync
|
|
@@ -3115,7 +3091,9 @@ function createRouter(init) {
|
|
|
3115
3091
|
return;
|
|
3116
3092
|
} else {
|
|
3117
3093
|
fetchRedirectIds.add(key);
|
|
3118
|
-
await startRedirectNavigation(fetchRequest, result, false
|
|
3094
|
+
await startRedirectNavigation(fetchRequest, result, false, {
|
|
3095
|
+
preventScrollReset
|
|
3096
|
+
});
|
|
3119
3097
|
return;
|
|
3120
3098
|
}
|
|
3121
3099
|
}
|
|
@@ -3154,6 +3132,7 @@ function createRouter(init) {
|
|
|
3154
3132
|
let {
|
|
3155
3133
|
submission,
|
|
3156
3134
|
fetcherSubmission,
|
|
3135
|
+
preventScrollReset,
|
|
3157
3136
|
replace
|
|
3158
3137
|
} = _temp2 === void 0 ? {} : _temp2;
|
|
3159
3138
|
if (redirect.response.headers.has("X-Remix-Revalidate")) {
|
|
@@ -3214,7 +3193,7 @@ function createRouter(init) {
|
|
|
3214
3193
|
formAction: location
|
|
3215
3194
|
}),
|
|
3216
3195
|
// Preserve these flags across redirects
|
|
3217
|
-
preventScrollReset: pendingPreventScrollReset,
|
|
3196
|
+
preventScrollReset: preventScrollReset || pendingPreventScrollReset,
|
|
3218
3197
|
enableViewTransition: isNavigation ? pendingViewTransitionEnabled : undefined
|
|
3219
3198
|
});
|
|
3220
3199
|
} else {
|
|
@@ -3226,7 +3205,7 @@ function createRouter(init) {
|
|
|
3226
3205
|
// Send fetcher submissions through for shouldRevalidate
|
|
3227
3206
|
fetcherSubmission,
|
|
3228
3207
|
// Preserve these flags across redirects
|
|
3229
|
-
preventScrollReset: pendingPreventScrollReset,
|
|
3208
|
+
preventScrollReset: preventScrollReset || pendingPreventScrollReset,
|
|
3230
3209
|
enableViewTransition: isNavigation ? pendingViewTransitionEnabled : undefined
|
|
3231
3210
|
});
|
|
3232
3211
|
}
|
|
@@ -3307,8 +3286,8 @@ function createRouter(init) {
|
|
|
3307
3286
|
fetchLoadMatches.forEach((_, key) => {
|
|
3308
3287
|
if (fetchControllers.has(key)) {
|
|
3309
3288
|
cancelledFetcherLoads.add(key);
|
|
3310
|
-
abortFetcher(key);
|
|
3311
3289
|
}
|
|
3290
|
+
abortFetcher(key);
|
|
3312
3291
|
});
|
|
3313
3292
|
}
|
|
3314
3293
|
function updateFetcherState(key, fetcher, opts) {
|
|
@@ -3381,9 +3360,10 @@ function createRouter(init) {
|
|
|
3381
3360
|
}
|
|
3382
3361
|
function abortFetcher(key) {
|
|
3383
3362
|
let controller = fetchControllers.get(key);
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3363
|
+
if (controller) {
|
|
3364
|
+
controller.abort();
|
|
3365
|
+
fetchControllers.delete(key);
|
|
3366
|
+
}
|
|
3387
3367
|
}
|
|
3388
3368
|
function markFetchersDone(keys) {
|
|
3389
3369
|
for (let key of keys) {
|
|
@@ -3448,12 +3428,12 @@ function createRouter(init) {
|
|
|
3448
3428
|
blockers
|
|
3449
3429
|
});
|
|
3450
3430
|
}
|
|
3451
|
-
function shouldBlockNavigation(
|
|
3431
|
+
function shouldBlockNavigation(_ref2) {
|
|
3452
3432
|
let {
|
|
3453
3433
|
currentLocation,
|
|
3454
3434
|
nextLocation,
|
|
3455
3435
|
historyAction
|
|
3456
|
-
} =
|
|
3436
|
+
} = _ref2;
|
|
3457
3437
|
if (blockerFunctions.size === 0) {
|
|
3458
3438
|
return;
|
|
3459
3439
|
}
|
|
@@ -3600,12 +3580,26 @@ function createRouter(init) {
|
|
|
3600
3580
|
};
|
|
3601
3581
|
}
|
|
3602
3582
|
async function discoverRoutes(matches, pathname, signal) {
|
|
3583
|
+
if (!patchRoutesOnNavigationImpl) {
|
|
3584
|
+
return {
|
|
3585
|
+
type: "success",
|
|
3586
|
+
matches
|
|
3587
|
+
};
|
|
3588
|
+
}
|
|
3603
3589
|
let partialMatches = matches;
|
|
3604
3590
|
while (true) {
|
|
3605
3591
|
let isNonHMR = inFlightDataRoutes == null;
|
|
3606
3592
|
let routesToUse = inFlightDataRoutes || dataRoutes;
|
|
3593
|
+
let localManifest = manifest;
|
|
3607
3594
|
try {
|
|
3608
|
-
await
|
|
3595
|
+
await patchRoutesOnNavigationImpl({
|
|
3596
|
+
path: pathname,
|
|
3597
|
+
matches: partialMatches,
|
|
3598
|
+
patch: (routeId, children) => {
|
|
3599
|
+
if (signal.aborted) return;
|
|
3600
|
+
patchRoutesImpl(routeId, children, routesToUse, localManifest, mapRouteProperties);
|
|
3601
|
+
}
|
|
3602
|
+
});
|
|
3609
3603
|
} catch (e) {
|
|
3610
3604
|
return {
|
|
3611
3605
|
type: "error",
|
|
@@ -3619,7 +3613,7 @@ function createRouter(init) {
|
|
|
3619
3613
|
// trigger a re-run of memoized `router.routes` dependencies.
|
|
3620
3614
|
// HMR will already update the identity and reflow when it lands
|
|
3621
3615
|
// `inFlightDataRoutes` in `completeNavigation`
|
|
3622
|
-
if (isNonHMR) {
|
|
3616
|
+
if (isNonHMR && !signal.aborted) {
|
|
3623
3617
|
dataRoutes = [...dataRoutes];
|
|
3624
3618
|
}
|
|
3625
3619
|
}
|
|
@@ -4198,9 +4192,21 @@ function normalizeTo(location, matches, basename, prependBasename, to, v7_relati
|
|
|
4198
4192
|
path.hash = location.hash;
|
|
4199
4193
|
}
|
|
4200
4194
|
|
|
4201
|
-
//
|
|
4202
|
-
if ((to == null || to === "" || to === ".") && activeRouteMatch
|
|
4203
|
-
|
|
4195
|
+
// Account for `?index` params when routing to the current location
|
|
4196
|
+
if ((to == null || to === "" || to === ".") && activeRouteMatch) {
|
|
4197
|
+
let nakedIndex = hasNakedIndexQuery(path.search);
|
|
4198
|
+
if (activeRouteMatch.route.index && !nakedIndex) {
|
|
4199
|
+
// Add one when we're targeting an index route
|
|
4200
|
+
path.search = path.search ? path.search.replace(/^\?/, "?index&") : "?index";
|
|
4201
|
+
} else if (!activeRouteMatch.route.index && nakedIndex) {
|
|
4202
|
+
// Remove existing ones when we're not
|
|
4203
|
+
let params = new URLSearchParams(path.search);
|
|
4204
|
+
let indexValues = params.getAll("index");
|
|
4205
|
+
params.delete("index");
|
|
4206
|
+
indexValues.filter(v => v).forEach(v => params.append("index", v));
|
|
4207
|
+
let qs = params.toString();
|
|
4208
|
+
path.search = qs ? "?" + qs : "";
|
|
4209
|
+
}
|
|
4204
4210
|
}
|
|
4205
4211
|
|
|
4206
4212
|
// If we're operating within a basename, prepend it to the pathname. If
|
|
@@ -4249,8 +4255,8 @@ function normalizeNavigateOptions(normalizeFormMethod, isFetcher, path, opts) {
|
|
|
4249
4255
|
}
|
|
4250
4256
|
let text = typeof opts.body === "string" ? opts.body : opts.body instanceof FormData || opts.body instanceof URLSearchParams ?
|
|
4251
4257
|
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#plain-text-form-data
|
|
4252
|
-
Array.from(opts.body.entries()).reduce((acc,
|
|
4253
|
-
let [name, value] =
|
|
4258
|
+
Array.from(opts.body.entries()).reduce((acc, _ref3) => {
|
|
4259
|
+
let [name, value] = _ref3;
|
|
4254
4260
|
return "" + acc + name + "=" + value + "\n";
|
|
4255
4261
|
}, "") : String(opts.body);
|
|
4256
4262
|
return {
|
|
@@ -4340,26 +4346,37 @@ function normalizeNavigateOptions(normalizeFormMethod, isFetcher, path, opts) {
|
|
|
4340
4346
|
};
|
|
4341
4347
|
}
|
|
4342
4348
|
|
|
4343
|
-
// Filter out all routes below any caught error as they aren't going to
|
|
4349
|
+
// Filter out all routes at/below any caught error as they aren't going to
|
|
4344
4350
|
// render so we don't need to load them
|
|
4345
|
-
function getLoaderMatchesUntilBoundary(matches, boundaryId) {
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
let index = matches.findIndex(m => m.route.id === boundaryId);
|
|
4349
|
-
if (index >= 0) {
|
|
4350
|
-
boundaryMatches = matches.slice(0, index);
|
|
4351
|
-
}
|
|
4351
|
+
function getLoaderMatchesUntilBoundary(matches, boundaryId, includeBoundary) {
|
|
4352
|
+
if (includeBoundary === void 0) {
|
|
4353
|
+
includeBoundary = false;
|
|
4352
4354
|
}
|
|
4353
|
-
|
|
4355
|
+
let index = matches.findIndex(m => m.route.id === boundaryId);
|
|
4356
|
+
if (index >= 0) {
|
|
4357
|
+
return matches.slice(0, includeBoundary ? index + 1 : index);
|
|
4358
|
+
}
|
|
4359
|
+
return matches;
|
|
4354
4360
|
}
|
|
4355
|
-
function getMatchesToLoad(history, state, matches, submission, location,
|
|
4361
|
+
function getMatchesToLoad(history, state, matches, submission, location, initialHydration, skipActionErrorRevalidation, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, deletedFetchers, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, pendingActionResult) {
|
|
4356
4362
|
let actionResult = pendingActionResult ? isErrorResult(pendingActionResult[1]) ? pendingActionResult[1].error : pendingActionResult[1].data : undefined;
|
|
4357
4363
|
let currentUrl = history.createURL(state.location);
|
|
4358
4364
|
let nextUrl = history.createURL(location);
|
|
4359
4365
|
|
|
4360
4366
|
// Pick navigation matches that are net-new or qualify for revalidation
|
|
4361
|
-
let
|
|
4362
|
-
|
|
4367
|
+
let boundaryMatches = matches;
|
|
4368
|
+
if (initialHydration && state.errors) {
|
|
4369
|
+
// On initial hydration, only consider matches up to _and including_ the boundary.
|
|
4370
|
+
// This is inclusive to handle cases where a server loader ran successfully,
|
|
4371
|
+
// a child server loader bubbled up to this route, but this route has
|
|
4372
|
+
// `clientLoader.hydrate` so we want to still run the `clientLoader` so that
|
|
4373
|
+
// we have a complete version of `loaderData`
|
|
4374
|
+
boundaryMatches = getLoaderMatchesUntilBoundary(matches, Object.keys(state.errors)[0], true);
|
|
4375
|
+
} else if (pendingActionResult && isErrorResult(pendingActionResult[1])) {
|
|
4376
|
+
// If an action threw an error, we call loaders up to, but not including the
|
|
4377
|
+
// boundary
|
|
4378
|
+
boundaryMatches = getLoaderMatchesUntilBoundary(matches, pendingActionResult[0]);
|
|
4379
|
+
}
|
|
4363
4380
|
|
|
4364
4381
|
// Don't revalidate loaders by default after action 4xx/5xx responses
|
|
4365
4382
|
// when the flag is enabled. They can still opt-into revalidation via
|
|
@@ -4377,13 +4394,8 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
|
|
|
4377
4394
|
if (route.loader == null) {
|
|
4378
4395
|
return false;
|
|
4379
4396
|
}
|
|
4380
|
-
if (
|
|
4381
|
-
|
|
4382
|
-
return true;
|
|
4383
|
-
}
|
|
4384
|
-
return state.loaderData[route.id] === undefined && (
|
|
4385
|
-
// Don't re-run if the loader ran and threw an error
|
|
4386
|
-
!state.errors || state.errors[route.id] === undefined);
|
|
4397
|
+
if (initialHydration) {
|
|
4398
|
+
return shouldLoadRouteOnHydration(route, state.loaderData, state.errors);
|
|
4387
4399
|
}
|
|
4388
4400
|
|
|
4389
4401
|
// Always call the loader on new route instances and pending defer cancellations
|
|
@@ -4417,11 +4429,11 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
|
|
|
4417
4429
|
let revalidatingFetchers = [];
|
|
4418
4430
|
fetchLoadMatches.forEach((f, key) => {
|
|
4419
4431
|
// Don't revalidate:
|
|
4420
|
-
// - on initial
|
|
4432
|
+
// - on initial hydration (shouldn't be any fetchers then anyway)
|
|
4421
4433
|
// - if fetcher won't be present in the subsequent render
|
|
4422
4434
|
// - no longer matches the URL (v7_fetcherPersist=false)
|
|
4423
4435
|
// - was unmounted but persisted due to v7_fetcherPersist=true
|
|
4424
|
-
if (
|
|
4436
|
+
if (initialHydration || !matches.some(m => m.route.id === f.routeId) || deletedFetchers.has(key)) {
|
|
4425
4437
|
return;
|
|
4426
4438
|
}
|
|
4427
4439
|
let fetcherMatches = matchRoutes(routesToUse, f.path, basename);
|
|
@@ -4487,6 +4499,32 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
|
|
|
4487
4499
|
});
|
|
4488
4500
|
return [navigationMatches, revalidatingFetchers];
|
|
4489
4501
|
}
|
|
4502
|
+
function shouldLoadRouteOnHydration(route, loaderData, errors) {
|
|
4503
|
+
// We dunno if we have a loader - gotta find out!
|
|
4504
|
+
if (route.lazy) {
|
|
4505
|
+
return true;
|
|
4506
|
+
}
|
|
4507
|
+
|
|
4508
|
+
// No loader, nothing to initialize
|
|
4509
|
+
if (!route.loader) {
|
|
4510
|
+
return false;
|
|
4511
|
+
}
|
|
4512
|
+
let hasData = loaderData != null && loaderData[route.id] !== undefined;
|
|
4513
|
+
let hasError = errors != null && errors[route.id] !== undefined;
|
|
4514
|
+
|
|
4515
|
+
// Don't run if we error'd during SSR
|
|
4516
|
+
if (!hasData && hasError) {
|
|
4517
|
+
return false;
|
|
4518
|
+
}
|
|
4519
|
+
|
|
4520
|
+
// Explicitly opting-in to running on hydration
|
|
4521
|
+
if (typeof route.loader === "function" && route.loader.hydrate === true) {
|
|
4522
|
+
return true;
|
|
4523
|
+
}
|
|
4524
|
+
|
|
4525
|
+
// Otherwise, run if we're not yet initialized with anything
|
|
4526
|
+
return !hasData && !hasError;
|
|
4527
|
+
}
|
|
4490
4528
|
function isNewLoader(currentLoaderData, currentMatch, match) {
|
|
4491
4529
|
let isNew =
|
|
4492
4530
|
// [a] -> [a, b]
|
|
@@ -4520,34 +4558,6 @@ function shouldRevalidateLoader(loaderMatch, arg) {
|
|
|
4520
4558
|
}
|
|
4521
4559
|
return arg.defaultShouldRevalidate;
|
|
4522
4560
|
}
|
|
4523
|
-
|
|
4524
|
-
/**
|
|
4525
|
-
* Idempotent utility to execute patchRoutesOnNavigation() to lazily load route
|
|
4526
|
-
* definitions and update the routes/routeManifest
|
|
4527
|
-
*/
|
|
4528
|
-
async function loadLazyRouteChildren(patchRoutesOnNavigationImpl, path, matches, routes, manifest, mapRouteProperties, pendingRouteChildren, signal) {
|
|
4529
|
-
let key = [path, ...matches.map(m => m.route.id)].join("-");
|
|
4530
|
-
try {
|
|
4531
|
-
let pending = pendingRouteChildren.get(key);
|
|
4532
|
-
if (!pending) {
|
|
4533
|
-
pending = patchRoutesOnNavigationImpl({
|
|
4534
|
-
path,
|
|
4535
|
-
matches,
|
|
4536
|
-
patch: (routeId, children) => {
|
|
4537
|
-
if (!signal.aborted) {
|
|
4538
|
-
patchRoutesImpl(routeId, children, routes, manifest, mapRouteProperties);
|
|
4539
|
-
}
|
|
4540
|
-
}
|
|
4541
|
-
});
|
|
4542
|
-
pendingRouteChildren.set(key, pending);
|
|
4543
|
-
}
|
|
4544
|
-
if (pending && isPromise(pending)) {
|
|
4545
|
-
await pending;
|
|
4546
|
-
}
|
|
4547
|
-
} finally {
|
|
4548
|
-
pendingRouteChildren.delete(key);
|
|
4549
|
-
}
|
|
4550
|
-
}
|
|
4551
4561
|
function patchRoutesImpl(routeId, children, routesToUse, manifest, mapRouteProperties) {
|
|
4552
4562
|
var _childrenToPatch;
|
|
4553
4563
|
let childrenToPatch;
|
|
@@ -4565,10 +4575,34 @@ function patchRoutesImpl(routeId, children, routesToUse, manifest, mapRoutePrope
|
|
|
4565
4575
|
// Don't patch in routes we already know about so that `patch` is idempotent
|
|
4566
4576
|
// to simplify user-land code. This is useful because we re-call the
|
|
4567
4577
|
// `patchRoutesOnNavigation` function for matched routes with params.
|
|
4568
|
-
let uniqueChildren = children.filter(
|
|
4578
|
+
let uniqueChildren = children.filter(newRoute => !childrenToPatch.some(existingRoute => isSameRoute(newRoute, existingRoute)));
|
|
4569
4579
|
let newRoutes = convertRoutesToDataRoutes(uniqueChildren, mapRouteProperties, [routeId || "_", "patch", String(((_childrenToPatch = childrenToPatch) == null ? void 0 : _childrenToPatch.length) || "0")], manifest);
|
|
4570
4580
|
childrenToPatch.push(...newRoutes);
|
|
4571
4581
|
}
|
|
4582
|
+
function isSameRoute(newRoute, existingRoute) {
|
|
4583
|
+
// Most optimal check is by id
|
|
4584
|
+
if ("id" in newRoute && "id" in existingRoute && newRoute.id === existingRoute.id) {
|
|
4585
|
+
return true;
|
|
4586
|
+
}
|
|
4587
|
+
|
|
4588
|
+
// Second is by pathing differences
|
|
4589
|
+
if (!(newRoute.index === existingRoute.index && newRoute.path === existingRoute.path && newRoute.caseSensitive === existingRoute.caseSensitive)) {
|
|
4590
|
+
return false;
|
|
4591
|
+
}
|
|
4592
|
+
|
|
4593
|
+
// Pathless layout routes are trickier since we need to check children.
|
|
4594
|
+
// If they have no children then they're the same as far as we can tell
|
|
4595
|
+
if ((!newRoute.children || newRoute.children.length === 0) && (!existingRoute.children || existingRoute.children.length === 0)) {
|
|
4596
|
+
return true;
|
|
4597
|
+
}
|
|
4598
|
+
|
|
4599
|
+
// Otherwise, we look to see if every child in the new route is already
|
|
4600
|
+
// represented in the existing route's children
|
|
4601
|
+
return newRoute.children.every((aChild, i) => {
|
|
4602
|
+
var _existingRoute$childr;
|
|
4603
|
+
return (_existingRoute$childr = existingRoute.children) == null ? void 0 : _existingRoute$childr.some(bChild => isSameRoute(aChild, bChild));
|
|
4604
|
+
});
|
|
4605
|
+
}
|
|
4572
4606
|
|
|
4573
4607
|
/**
|
|
4574
4608
|
* Execute route.lazy() methods to lazily load route modules (loader, action,
|
|
@@ -4624,10 +4658,10 @@ async function loadLazyRouteModule(route, mapRouteProperties, manifest) {
|
|
|
4624
4658
|
}
|
|
4625
4659
|
|
|
4626
4660
|
// Default implementation of `dataStrategy` which fetches all loaders in parallel
|
|
4627
|
-
async function defaultDataStrategy(
|
|
4661
|
+
async function defaultDataStrategy(_ref4) {
|
|
4628
4662
|
let {
|
|
4629
4663
|
matches
|
|
4630
|
-
} =
|
|
4664
|
+
} = _ref4;
|
|
4631
4665
|
let matchesToLoad = matches.filter(m => m.shouldLoad);
|
|
4632
4666
|
let results = await Promise.all(matchesToLoad.map(m => m.resolve()));
|
|
4633
4667
|
return results.reduce((acc, result, i) => Object.assign(acc, {
|
|
@@ -5041,7 +5075,7 @@ function processRouteLoaderData(matches, results, pendingActionResult, activeDef
|
|
|
5041
5075
|
loaderHeaders
|
|
5042
5076
|
};
|
|
5043
5077
|
}
|
|
5044
|
-
function processLoaderData(state, matches,
|
|
5078
|
+
function processLoaderData(state, matches, results, pendingActionResult, revalidatingFetchers, fetcherResults, activeDeferreds) {
|
|
5045
5079
|
let {
|
|
5046
5080
|
loaderData,
|
|
5047
5081
|
errors
|
|
@@ -5220,9 +5254,6 @@ function isHashChangeOnly(a, b) {
|
|
|
5220
5254
|
// /page#hash -> /page
|
|
5221
5255
|
return false;
|
|
5222
5256
|
}
|
|
5223
|
-
function isPromise(val) {
|
|
5224
|
-
return typeof val === "object" && val != null && "then" in val;
|
|
5225
|
-
}
|
|
5226
5257
|
function isDataStrategyResult(result) {
|
|
5227
5258
|
return result != null && typeof result === "object" && "type" in result && "result" in result && (result.type === ResultType.data || result.type === ResultType.error);
|
|
5228
5259
|
}
|