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