@remix-run/router 0.0.0-experimental-bcda00aaf → 0.0.0-experimental-1116190f7
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 +26 -0
- package/dist/router.cjs.js +163 -152
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.js +156 -149
- package/dist/router.js.map +1 -1
- package/dist/router.umd.js +163 -152
- 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 +198 -189
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-1116190f7
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -1391,6 +1391,7 @@ function createRouter(init) {
|
|
|
1391
1391
|
// SSR did the initial scroll restoration.
|
|
1392
1392
|
let initialScrollRestored = init.hydrationData != null;
|
|
1393
1393
|
let initialMatches = matchRoutes(dataRoutes, init.history.location, basename);
|
|
1394
|
+
let initialMatchesIsFOW = false;
|
|
1394
1395
|
let initialErrors = null;
|
|
1395
1396
|
if (initialMatches == null && !patchRoutesOnNavigationImpl) {
|
|
1396
1397
|
// If we do not match a user-provided-route, fall back to the root
|
|
@@ -1429,6 +1430,7 @@ function createRouter(init) {
|
|
|
1429
1430
|
if (future.v7_partialHydration) {
|
|
1430
1431
|
let fogOfWar = checkFogOfWar(null, dataRoutes, init.history.location.pathname);
|
|
1431
1432
|
if (fogOfWar.active && fogOfWar.matches) {
|
|
1433
|
+
initialMatchesIsFOW = true;
|
|
1432
1434
|
initialMatches = fogOfWar.matches;
|
|
1433
1435
|
}
|
|
1434
1436
|
}
|
|
@@ -1445,24 +1447,12 @@ function createRouter(init) {
|
|
|
1445
1447
|
// were marked for explicit hydration
|
|
1446
1448
|
let loaderData = init.hydrationData ? init.hydrationData.loaderData : null;
|
|
1447
1449
|
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
1450
|
// If errors exist, don't consider routes below the boundary
|
|
1461
1451
|
if (errors) {
|
|
1462
1452
|
let idx = initialMatches.findIndex(m => errors[m.route.id] !== undefined);
|
|
1463
|
-
initialized = initialMatches.slice(0, idx + 1).every(
|
|
1453
|
+
initialized = initialMatches.slice(0, idx + 1).every(m => !shouldLoadRouteOnHydration(m.route, loaderData, errors));
|
|
1464
1454
|
} else {
|
|
1465
|
-
initialized = initialMatches.every(
|
|
1455
|
+
initialized = initialMatches.every(m => !shouldLoadRouteOnHydration(m.route, loaderData, errors));
|
|
1466
1456
|
}
|
|
1467
1457
|
} else {
|
|
1468
1458
|
// Without partial hydration - we're initialized if we were provided any
|
|
@@ -1541,9 +1531,6 @@ function createRouter(init) {
|
|
|
1541
1531
|
// Store blocker functions in a separate Map outside of router state since
|
|
1542
1532
|
// we don't need to update UI state if they change
|
|
1543
1533
|
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
1534
|
// Flag to ignore the next history update, so we can revert the URL change on
|
|
1548
1535
|
// a POP navigation that was blocked by the user without touching router state
|
|
1549
1536
|
let unblockBlockerHistoryUpdate = undefined;
|
|
@@ -1914,7 +1901,9 @@ function createRouter(init) {
|
|
|
1914
1901
|
pendingViewTransitionEnabled = (opts && opts.enableViewTransition) === true;
|
|
1915
1902
|
let routesToUse = inFlightDataRoutes || dataRoutes;
|
|
1916
1903
|
let loadingNavigation = opts && opts.overrideNavigation;
|
|
1917
|
-
let matches =
|
|
1904
|
+
let matches = opts != null && opts.initialHydration && state.matches && state.matches.length > 0 && !initialMatchesIsFOW ?
|
|
1905
|
+
// `matchRoutes()` has already been called if we're in here via `router.initialize()`
|
|
1906
|
+
state.matches : matchRoutes(routesToUse, location, basename);
|
|
1918
1907
|
let flushSync = (opts && opts.flushSync) === true;
|
|
1919
1908
|
let fogOfWar = checkFogOfWar(matches, routesToUse, location.pathname);
|
|
1920
1909
|
if (fogOfWar.active && fogOfWar.matches) {
|
|
@@ -1941,7 +1930,7 @@ function createRouter(init) {
|
|
|
1941
1930
|
// Short circuit if it's only a hash change and not a revalidation or
|
|
1942
1931
|
// mutation submission.
|
|
1943
1932
|
//
|
|
1944
|
-
// Ignore on initial page loads because since the initial
|
|
1933
|
+
// Ignore on initial page loads because since the initial hydration will always
|
|
1945
1934
|
// be "same hash". For example, on /page#hash and submit a <Form method="post">
|
|
1946
1935
|
// which will default to a navigation to /page
|
|
1947
1936
|
if (state.initialized && !isRevalidationRequired && isHashChangeOnly(state.location, location) && !(opts && opts.submission && isMutationMethod(opts.submission.formMethod))) {
|
|
@@ -2041,15 +2030,12 @@ function createRouter(init) {
|
|
|
2041
2030
|
shortCircuited: true
|
|
2042
2031
|
};
|
|
2043
2032
|
} else if (discoverResult.type === "error") {
|
|
2044
|
-
let
|
|
2045
|
-
boundaryId,
|
|
2046
|
-
error
|
|
2047
|
-
} = handleDiscoverRouteError(location.pathname, discoverResult);
|
|
2033
|
+
let boundaryId = findNearestBoundary(discoverResult.partialMatches).route.id;
|
|
2048
2034
|
return {
|
|
2049
2035
|
matches: discoverResult.partialMatches,
|
|
2050
2036
|
pendingActionResult: [boundaryId, {
|
|
2051
2037
|
type: ResultType.error,
|
|
2052
|
-
error
|
|
2038
|
+
error: discoverResult.error
|
|
2053
2039
|
}]
|
|
2054
2040
|
};
|
|
2055
2041
|
} else if (!discoverResult.matches) {
|
|
@@ -2173,15 +2159,12 @@ function createRouter(init) {
|
|
|
2173
2159
|
shortCircuited: true
|
|
2174
2160
|
};
|
|
2175
2161
|
} else if (discoverResult.type === "error") {
|
|
2176
|
-
let
|
|
2177
|
-
boundaryId,
|
|
2178
|
-
error
|
|
2179
|
-
} = handleDiscoverRouteError(location.pathname, discoverResult);
|
|
2162
|
+
let boundaryId = findNearestBoundary(discoverResult.partialMatches).route.id;
|
|
2180
2163
|
return {
|
|
2181
2164
|
matches: discoverResult.partialMatches,
|
|
2182
2165
|
loaderData: {},
|
|
2183
2166
|
errors: {
|
|
2184
|
-
[boundaryId]: error
|
|
2167
|
+
[boundaryId]: discoverResult.error
|
|
2185
2168
|
}
|
|
2186
2169
|
};
|
|
2187
2170
|
} else if (!discoverResult.matches) {
|
|
@@ -2245,9 +2228,7 @@ function createRouter(init) {
|
|
|
2245
2228
|
});
|
|
2246
2229
|
}
|
|
2247
2230
|
revalidatingFetchers.forEach(rf => {
|
|
2248
|
-
|
|
2249
|
-
abortFetcher(rf.key);
|
|
2250
|
-
}
|
|
2231
|
+
abortFetcher(rf.key);
|
|
2251
2232
|
if (rf.controller) {
|
|
2252
2233
|
// Fetchers use an independent AbortController so that aborting a fetcher
|
|
2253
2234
|
// (via deleteFetcher) does not abort the triggering navigation that
|
|
@@ -2303,7 +2284,7 @@ function createRouter(init) {
|
|
|
2303
2284
|
let {
|
|
2304
2285
|
loaderData,
|
|
2305
2286
|
errors
|
|
2306
|
-
} = processLoaderData(state, matches,
|
|
2287
|
+
} = processLoaderData(state, matches, loaderResults, pendingActionResult, revalidatingFetchers, fetcherResults, activeDeferreds);
|
|
2307
2288
|
// Wire up subscribers to update loaderData as promises settle
|
|
2308
2289
|
activeDeferreds.forEach((deferredData, routeId) => {
|
|
2309
2290
|
deferredData.subscribe(aborted => {
|
|
@@ -2315,17 +2296,9 @@ function createRouter(init) {
|
|
|
2315
2296
|
}
|
|
2316
2297
|
});
|
|
2317
2298
|
});
|
|
2318
|
-
//
|
|
2299
|
+
// Preserve SSR errors during partial hydration
|
|
2319
2300
|
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
|
-
});
|
|
2301
|
+
errors = _extends({}, state.errors, errors);
|
|
2329
2302
|
}
|
|
2330
2303
|
let updatedFetchers = markFetchRedirectsDone();
|
|
2331
2304
|
let didAbortFetchLoads = abortStaleFetchLoads(pendingNavigationLoadId);
|
|
@@ -2367,7 +2340,7 @@ function createRouter(init) {
|
|
|
2367
2340
|
if (isServer) {
|
|
2368
2341
|
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
2342
|
}
|
|
2370
|
-
|
|
2343
|
+
abortFetcher(key);
|
|
2371
2344
|
let flushSync = (opts && opts.flushSync) === true;
|
|
2372
2345
|
let routesToUse = inFlightDataRoutes || dataRoutes;
|
|
2373
2346
|
let normalizedPath = normalizeTo(state.location, state.matches, basename, future.v7_prependBasename, href, future.v7_relativeSplatPath, routeId, opts == null ? void 0 : opts.relative);
|
|
@@ -2396,9 +2369,9 @@ function createRouter(init) {
|
|
|
2396
2369
|
return;
|
|
2397
2370
|
}
|
|
2398
2371
|
let match = getTargetMatch(matches, path);
|
|
2399
|
-
|
|
2372
|
+
let preventScrollReset = (opts && opts.preventScrollReset) === true;
|
|
2400
2373
|
if (submission && isMutationMethod(submission.formMethod)) {
|
|
2401
|
-
handleFetcherAction(key, routeId, path, match, matches, fogOfWar.active, flushSync, submission);
|
|
2374
|
+
handleFetcherAction(key, routeId, path, match, matches, fogOfWar.active, flushSync, preventScrollReset, submission);
|
|
2402
2375
|
return;
|
|
2403
2376
|
}
|
|
2404
2377
|
// Store off the match so we can call it's shouldRevalidate on subsequent
|
|
@@ -2407,11 +2380,11 @@ function createRouter(init) {
|
|
|
2407
2380
|
routeId,
|
|
2408
2381
|
path
|
|
2409
2382
|
});
|
|
2410
|
-
handleFetcherLoader(key, routeId, path, match, matches, fogOfWar.active, flushSync, submission);
|
|
2383
|
+
handleFetcherLoader(key, routeId, path, match, matches, fogOfWar.active, flushSync, preventScrollReset, submission);
|
|
2411
2384
|
}
|
|
2412
2385
|
// Call the action for the matched fetcher.submit(), and then handle redirects,
|
|
2413
2386
|
// errors, and revalidation
|
|
2414
|
-
async function handleFetcherAction(key, routeId, path, match, requestMatches, isFogOfWar, flushSync, submission) {
|
|
2387
|
+
async function handleFetcherAction(key, routeId, path, match, requestMatches, isFogOfWar, flushSync, preventScrollReset, submission) {
|
|
2415
2388
|
interruptActiveLoads();
|
|
2416
2389
|
fetchLoadMatches.delete(key);
|
|
2417
2390
|
function detectAndHandle405Error(m) {
|
|
@@ -2443,10 +2416,7 @@ function createRouter(init) {
|
|
|
2443
2416
|
if (discoverResult.type === "aborted") {
|
|
2444
2417
|
return;
|
|
2445
2418
|
} else if (discoverResult.type === "error") {
|
|
2446
|
-
|
|
2447
|
-
error
|
|
2448
|
-
} = handleDiscoverRouteError(path, discoverResult);
|
|
2449
|
-
setFetcherError(key, routeId, error, {
|
|
2419
|
+
setFetcherError(key, routeId, discoverResult.error, {
|
|
2450
2420
|
flushSync
|
|
2451
2421
|
});
|
|
2452
2422
|
return;
|
|
@@ -2501,7 +2471,8 @@ function createRouter(init) {
|
|
|
2501
2471
|
fetchRedirectIds.add(key);
|
|
2502
2472
|
updateFetcherState(key, getLoadingFetcher(submission));
|
|
2503
2473
|
return startRedirectNavigation(fetchRequest, actionResult, false, {
|
|
2504
|
-
fetcherSubmission: submission
|
|
2474
|
+
fetcherSubmission: submission,
|
|
2475
|
+
preventScrollReset
|
|
2505
2476
|
});
|
|
2506
2477
|
}
|
|
2507
2478
|
}
|
|
@@ -2536,9 +2507,7 @@ function createRouter(init) {
|
|
|
2536
2507
|
let existingFetcher = state.fetchers.get(staleKey);
|
|
2537
2508
|
let revalidatingFetcher = getLoadingFetcher(undefined, existingFetcher ? existingFetcher.data : undefined);
|
|
2538
2509
|
state.fetchers.set(staleKey, revalidatingFetcher);
|
|
2539
|
-
|
|
2540
|
-
abortFetcher(staleKey);
|
|
2541
|
-
}
|
|
2510
|
+
abortFetcher(staleKey);
|
|
2542
2511
|
if (rf.controller) {
|
|
2543
2512
|
fetchControllers.set(staleKey, rf.controller);
|
|
2544
2513
|
}
|
|
@@ -2561,7 +2530,9 @@ function createRouter(init) {
|
|
|
2561
2530
|
revalidatingFetchers.forEach(r => fetchControllers.delete(r.key));
|
|
2562
2531
|
let redirect = findRedirect(loaderResults);
|
|
2563
2532
|
if (redirect) {
|
|
2564
|
-
return startRedirectNavigation(revalidationRequest, redirect.result, false
|
|
2533
|
+
return startRedirectNavigation(revalidationRequest, redirect.result, false, {
|
|
2534
|
+
preventScrollReset
|
|
2535
|
+
});
|
|
2565
2536
|
}
|
|
2566
2537
|
redirect = findRedirect(fetcherResults);
|
|
2567
2538
|
if (redirect) {
|
|
@@ -2569,13 +2540,15 @@ function createRouter(init) {
|
|
|
2569
2540
|
// fetchRedirectIds so it doesn't get revalidated on the next set of
|
|
2570
2541
|
// loader executions
|
|
2571
2542
|
fetchRedirectIds.add(redirect.key);
|
|
2572
|
-
return startRedirectNavigation(revalidationRequest, redirect.result, false
|
|
2543
|
+
return startRedirectNavigation(revalidationRequest, redirect.result, false, {
|
|
2544
|
+
preventScrollReset
|
|
2545
|
+
});
|
|
2573
2546
|
}
|
|
2574
2547
|
// Process and commit output from loaders
|
|
2575
2548
|
let {
|
|
2576
2549
|
loaderData,
|
|
2577
2550
|
errors
|
|
2578
|
-
} = processLoaderData(state, matches,
|
|
2551
|
+
} = processLoaderData(state, matches, loaderResults, undefined, revalidatingFetchers, fetcherResults, activeDeferreds);
|
|
2579
2552
|
// Since we let revalidations complete even if the submitting fetcher was
|
|
2580
2553
|
// deleted, only put it back to idle if it hasn't been deleted
|
|
2581
2554
|
if (state.fetchers.has(key)) {
|
|
@@ -2608,7 +2581,7 @@ function createRouter(init) {
|
|
|
2608
2581
|
}
|
|
2609
2582
|
}
|
|
2610
2583
|
// Call the matched loader for fetcher.load(), handling redirects, errors, etc.
|
|
2611
|
-
async function handleFetcherLoader(key, routeId, path, match, matches, isFogOfWar, flushSync, submission) {
|
|
2584
|
+
async function handleFetcherLoader(key, routeId, path, match, matches, isFogOfWar, flushSync, preventScrollReset, submission) {
|
|
2612
2585
|
let existingFetcher = state.fetchers.get(key);
|
|
2613
2586
|
updateFetcherState(key, getLoadingFetcher(submission, existingFetcher ? existingFetcher.data : undefined), {
|
|
2614
2587
|
flushSync
|
|
@@ -2620,10 +2593,7 @@ function createRouter(init) {
|
|
|
2620
2593
|
if (discoverResult.type === "aborted") {
|
|
2621
2594
|
return;
|
|
2622
2595
|
} else if (discoverResult.type === "error") {
|
|
2623
|
-
|
|
2624
|
-
error
|
|
2625
|
-
} = handleDiscoverRouteError(path, discoverResult);
|
|
2626
|
-
setFetcherError(key, routeId, error, {
|
|
2596
|
+
setFetcherError(key, routeId, discoverResult.error, {
|
|
2627
2597
|
flushSync
|
|
2628
2598
|
});
|
|
2629
2599
|
return;
|
|
@@ -2674,7 +2644,9 @@ function createRouter(init) {
|
|
|
2674
2644
|
return;
|
|
2675
2645
|
} else {
|
|
2676
2646
|
fetchRedirectIds.add(key);
|
|
2677
|
-
await startRedirectNavigation(fetchRequest, result, false
|
|
2647
|
+
await startRedirectNavigation(fetchRequest, result, false, {
|
|
2648
|
+
preventScrollReset
|
|
2649
|
+
});
|
|
2678
2650
|
return;
|
|
2679
2651
|
}
|
|
2680
2652
|
}
|
|
@@ -2710,6 +2682,7 @@ function createRouter(init) {
|
|
|
2710
2682
|
let {
|
|
2711
2683
|
submission,
|
|
2712
2684
|
fetcherSubmission,
|
|
2685
|
+
preventScrollReset,
|
|
2713
2686
|
replace
|
|
2714
2687
|
} = _temp2 === void 0 ? {} : _temp2;
|
|
2715
2688
|
if (redirect.response.headers.has("X-Remix-Revalidate")) {
|
|
@@ -2767,7 +2740,7 @@ function createRouter(init) {
|
|
|
2767
2740
|
formAction: location
|
|
2768
2741
|
}),
|
|
2769
2742
|
// Preserve these flags across redirects
|
|
2770
|
-
preventScrollReset: pendingPreventScrollReset,
|
|
2743
|
+
preventScrollReset: preventScrollReset || pendingPreventScrollReset,
|
|
2771
2744
|
enableViewTransition: isNavigation ? pendingViewTransitionEnabled : undefined
|
|
2772
2745
|
});
|
|
2773
2746
|
} else {
|
|
@@ -2779,7 +2752,7 @@ function createRouter(init) {
|
|
|
2779
2752
|
// Send fetcher submissions through for shouldRevalidate
|
|
2780
2753
|
fetcherSubmission,
|
|
2781
2754
|
// Preserve these flags across redirects
|
|
2782
|
-
preventScrollReset: pendingPreventScrollReset,
|
|
2755
|
+
preventScrollReset: preventScrollReset || pendingPreventScrollReset,
|
|
2783
2756
|
enableViewTransition: isNavigation ? pendingViewTransitionEnabled : undefined
|
|
2784
2757
|
});
|
|
2785
2758
|
}
|
|
@@ -2856,8 +2829,8 @@ function createRouter(init) {
|
|
|
2856
2829
|
fetchLoadMatches.forEach((_, key) => {
|
|
2857
2830
|
if (fetchControllers.has(key)) {
|
|
2858
2831
|
cancelledFetcherLoads.add(key);
|
|
2859
|
-
abortFetcher(key);
|
|
2860
2832
|
}
|
|
2833
|
+
abortFetcher(key);
|
|
2861
2834
|
});
|
|
2862
2835
|
}
|
|
2863
2836
|
function updateFetcherState(key, fetcher, opts) {
|
|
@@ -2930,9 +2903,10 @@ function createRouter(init) {
|
|
|
2930
2903
|
}
|
|
2931
2904
|
function abortFetcher(key) {
|
|
2932
2905
|
let controller = fetchControllers.get(key);
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2906
|
+
if (controller) {
|
|
2907
|
+
controller.abort();
|
|
2908
|
+
fetchControllers.delete(key);
|
|
2909
|
+
}
|
|
2936
2910
|
}
|
|
2937
2911
|
function markFetchersDone(keys) {
|
|
2938
2912
|
for (let key of keys) {
|
|
@@ -2995,12 +2969,12 @@ function createRouter(init) {
|
|
|
2995
2969
|
blockers
|
|
2996
2970
|
});
|
|
2997
2971
|
}
|
|
2998
|
-
function shouldBlockNavigation(
|
|
2972
|
+
function shouldBlockNavigation(_ref2) {
|
|
2999
2973
|
let {
|
|
3000
2974
|
currentLocation,
|
|
3001
2975
|
nextLocation,
|
|
3002
2976
|
historyAction
|
|
3003
|
-
} =
|
|
2977
|
+
} = _ref2;
|
|
3004
2978
|
if (blockerFunctions.size === 0) {
|
|
3005
2979
|
return;
|
|
3006
2980
|
}
|
|
@@ -3044,16 +3018,6 @@ function createRouter(init) {
|
|
|
3044
3018
|
error
|
|
3045
3019
|
};
|
|
3046
3020
|
}
|
|
3047
|
-
function handleDiscoverRouteError(pathname, discoverResult) {
|
|
3048
|
-
return {
|
|
3049
|
-
boundaryId: findNearestBoundary(discoverResult.partialMatches).route.id,
|
|
3050
|
-
error: getInternalRouterError(400, {
|
|
3051
|
-
type: "route-discovery",
|
|
3052
|
-
pathname,
|
|
3053
|
-
message: discoverResult.error != null && "message" in discoverResult.error ? discoverResult.error : String(discoverResult.error)
|
|
3054
|
-
})
|
|
3055
|
-
};
|
|
3056
|
-
}
|
|
3057
3021
|
function cancelActiveDeferreds(predicate) {
|
|
3058
3022
|
let cancelledRouteIds = [];
|
|
3059
3023
|
activeDeferreds.forEach((dfd, routeId) => {
|
|
@@ -3142,12 +3106,26 @@ function createRouter(init) {
|
|
|
3142
3106
|
};
|
|
3143
3107
|
}
|
|
3144
3108
|
async function discoverRoutes(matches, pathname, signal) {
|
|
3109
|
+
if (!patchRoutesOnNavigationImpl) {
|
|
3110
|
+
return {
|
|
3111
|
+
type: "success",
|
|
3112
|
+
matches
|
|
3113
|
+
};
|
|
3114
|
+
}
|
|
3145
3115
|
let partialMatches = matches;
|
|
3146
3116
|
while (true) {
|
|
3147
3117
|
let isNonHMR = inFlightDataRoutes == null;
|
|
3148
3118
|
let routesToUse = inFlightDataRoutes || dataRoutes;
|
|
3119
|
+
let localManifest = manifest;
|
|
3149
3120
|
try {
|
|
3150
|
-
await
|
|
3121
|
+
await patchRoutesOnNavigationImpl({
|
|
3122
|
+
path: pathname,
|
|
3123
|
+
matches: partialMatches,
|
|
3124
|
+
patch: (routeId, children) => {
|
|
3125
|
+
if (signal.aborted) return;
|
|
3126
|
+
patchRoutesImpl(routeId, children, routesToUse, localManifest, mapRouteProperties);
|
|
3127
|
+
}
|
|
3128
|
+
});
|
|
3151
3129
|
} catch (e) {
|
|
3152
3130
|
return {
|
|
3153
3131
|
type: "error",
|
|
@@ -3161,7 +3139,7 @@ function createRouter(init) {
|
|
|
3161
3139
|
// trigger a re-run of memoized `router.routes` dependencies.
|
|
3162
3140
|
// HMR will already update the identity and reflow when it lands
|
|
3163
3141
|
// `inFlightDataRoutes` in `completeNavigation`
|
|
3164
|
-
if (isNonHMR) {
|
|
3142
|
+
if (isNonHMR && !signal.aborted) {
|
|
3165
3143
|
dataRoutes = [...dataRoutes];
|
|
3166
3144
|
}
|
|
3167
3145
|
}
|
|
@@ -3712,9 +3690,21 @@ function normalizeTo(location, matches, basename, prependBasename, to, v7_relati
|
|
|
3712
3690
|
path.search = location.search;
|
|
3713
3691
|
path.hash = location.hash;
|
|
3714
3692
|
}
|
|
3715
|
-
//
|
|
3716
|
-
if ((to == null || to === "" || to === ".") && activeRouteMatch
|
|
3717
|
-
|
|
3693
|
+
// Account for `?index` params when routing to the current location
|
|
3694
|
+
if ((to == null || to === "" || to === ".") && activeRouteMatch) {
|
|
3695
|
+
let nakedIndex = hasNakedIndexQuery(path.search);
|
|
3696
|
+
if (activeRouteMatch.route.index && !nakedIndex) {
|
|
3697
|
+
// Add one when we're targeting an index route
|
|
3698
|
+
path.search = path.search ? path.search.replace(/^\?/, "?index&") : "?index";
|
|
3699
|
+
} else if (!activeRouteMatch.route.index && nakedIndex) {
|
|
3700
|
+
// Remove existing ones when we're not
|
|
3701
|
+
let params = new URLSearchParams(path.search);
|
|
3702
|
+
let indexValues = params.getAll("index");
|
|
3703
|
+
params.delete("index");
|
|
3704
|
+
indexValues.filter(v => v).forEach(v => params.append("index", v));
|
|
3705
|
+
let qs = params.toString();
|
|
3706
|
+
path.search = qs ? "?" + qs : "";
|
|
3707
|
+
}
|
|
3718
3708
|
}
|
|
3719
3709
|
// If we're operating within a basename, prepend it to the pathname. If
|
|
3720
3710
|
// this is a root navigation, then just use the raw basename which allows
|
|
@@ -3760,8 +3750,8 @@ function normalizeNavigateOptions(normalizeFormMethod, isFetcher, path, opts) {
|
|
|
3760
3750
|
}
|
|
3761
3751
|
let text = typeof opts.body === "string" ? opts.body : opts.body instanceof FormData || opts.body instanceof URLSearchParams ?
|
|
3762
3752
|
// 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] =
|
|
3753
|
+
Array.from(opts.body.entries()).reduce((acc, _ref3) => {
|
|
3754
|
+
let [name, value] = _ref3;
|
|
3765
3755
|
return "" + acc + name + "=" + value + "\n";
|
|
3766
3756
|
}, "") : String(opts.body);
|
|
3767
3757
|
return {
|
|
@@ -3849,25 +3839,36 @@ function normalizeNavigateOptions(normalizeFormMethod, isFetcher, path, opts) {
|
|
|
3849
3839
|
submission
|
|
3850
3840
|
};
|
|
3851
3841
|
}
|
|
3852
|
-
// Filter out all routes below any caught error as they aren't going to
|
|
3842
|
+
// Filter out all routes at/below any caught error as they aren't going to
|
|
3853
3843
|
// render so we don't need to load them
|
|
3854
|
-
function getLoaderMatchesUntilBoundary(matches, boundaryId) {
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3844
|
+
function getLoaderMatchesUntilBoundary(matches, boundaryId, includeBoundary) {
|
|
3845
|
+
if (includeBoundary === void 0) {
|
|
3846
|
+
includeBoundary = false;
|
|
3847
|
+
}
|
|
3848
|
+
let index = matches.findIndex(m => m.route.id === boundaryId);
|
|
3849
|
+
if (index >= 0) {
|
|
3850
|
+
return matches.slice(0, includeBoundary ? index + 1 : index);
|
|
3861
3851
|
}
|
|
3862
|
-
return
|
|
3852
|
+
return matches;
|
|
3863
3853
|
}
|
|
3864
|
-
function getMatchesToLoad(history, state, matches, submission, location,
|
|
3854
|
+
function getMatchesToLoad(history, state, matches, submission, location, initialHydration, skipActionErrorRevalidation, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, deletedFetchers, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, pendingActionResult) {
|
|
3865
3855
|
let actionResult = pendingActionResult ? isErrorResult(pendingActionResult[1]) ? pendingActionResult[1].error : pendingActionResult[1].data : undefined;
|
|
3866
3856
|
let currentUrl = history.createURL(state.location);
|
|
3867
3857
|
let nextUrl = history.createURL(location);
|
|
3868
3858
|
// Pick navigation matches that are net-new or qualify for revalidation
|
|
3869
|
-
let
|
|
3870
|
-
|
|
3859
|
+
let boundaryMatches = matches;
|
|
3860
|
+
if (initialHydration && state.errors) {
|
|
3861
|
+
// On initial hydration, only consider matches up to _and including_ the boundary.
|
|
3862
|
+
// This is inclusive to handle cases where a server loader ran successfully,
|
|
3863
|
+
// a child server loader bubbled up to this route, but this route has
|
|
3864
|
+
// `clientLoader.hydrate` so we want to still run the `clientLoader` so that
|
|
3865
|
+
// we have a complete version of `loaderData`
|
|
3866
|
+
boundaryMatches = getLoaderMatchesUntilBoundary(matches, Object.keys(state.errors)[0], true);
|
|
3867
|
+
} else if (pendingActionResult && isErrorResult(pendingActionResult[1])) {
|
|
3868
|
+
// If an action threw an error, we call loaders up to, but not including the
|
|
3869
|
+
// boundary
|
|
3870
|
+
boundaryMatches = getLoaderMatchesUntilBoundary(matches, pendingActionResult[0]);
|
|
3871
|
+
}
|
|
3871
3872
|
// Don't revalidate loaders by default after action 4xx/5xx responses
|
|
3872
3873
|
// when the flag is enabled. They can still opt-into revalidation via
|
|
3873
3874
|
// `shouldRevalidate` via `actionResult`
|
|
@@ -3884,13 +3885,8 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
|
|
|
3884
3885
|
if (route.loader == null) {
|
|
3885
3886
|
return false;
|
|
3886
3887
|
}
|
|
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);
|
|
3888
|
+
if (initialHydration) {
|
|
3889
|
+
return shouldLoadRouteOnHydration(route, state.loaderData, state.errors);
|
|
3894
3890
|
}
|
|
3895
3891
|
// Always call the loader on new route instances and pending defer cancellations
|
|
3896
3892
|
if (isNewLoader(state.loaderData, state.matches[index], match) || cancelledDeferredRoutes.some(id => id === match.route.id)) {
|
|
@@ -3921,11 +3917,11 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
|
|
|
3921
3917
|
let revalidatingFetchers = [];
|
|
3922
3918
|
fetchLoadMatches.forEach((f, key) => {
|
|
3923
3919
|
// Don't revalidate:
|
|
3924
|
-
// - on initial
|
|
3920
|
+
// - on initial hydration (shouldn't be any fetchers then anyway)
|
|
3925
3921
|
// - if fetcher won't be present in the subsequent render
|
|
3926
3922
|
// - no longer matches the URL (v7_fetcherPersist=false)
|
|
3927
3923
|
// - was unmounted but persisted due to v7_fetcherPersist=true
|
|
3928
|
-
if (
|
|
3924
|
+
if (initialHydration || !matches.some(m => m.route.id === f.routeId) || deletedFetchers.has(key)) {
|
|
3929
3925
|
return;
|
|
3930
3926
|
}
|
|
3931
3927
|
let fetcherMatches = matchRoutes(routesToUse, f.path, basename);
|
|
@@ -3989,6 +3985,28 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
|
|
|
3989
3985
|
});
|
|
3990
3986
|
return [navigationMatches, revalidatingFetchers];
|
|
3991
3987
|
}
|
|
3988
|
+
function shouldLoadRouteOnHydration(route, loaderData, errors) {
|
|
3989
|
+
// We dunno if we have a loader - gotta find out!
|
|
3990
|
+
if (route.lazy) {
|
|
3991
|
+
return true;
|
|
3992
|
+
}
|
|
3993
|
+
// No loader, nothing to initialize
|
|
3994
|
+
if (!route.loader) {
|
|
3995
|
+
return false;
|
|
3996
|
+
}
|
|
3997
|
+
let hasData = loaderData != null && loaderData[route.id] !== undefined;
|
|
3998
|
+
let hasError = errors != null && errors[route.id] !== undefined;
|
|
3999
|
+
// Don't run if we error'd during SSR
|
|
4000
|
+
if (!hasData && hasError) {
|
|
4001
|
+
return false;
|
|
4002
|
+
}
|
|
4003
|
+
// Explicitly opting-in to running on hydration
|
|
4004
|
+
if (typeof route.loader === "function" && route.loader.hydrate === true) {
|
|
4005
|
+
return true;
|
|
4006
|
+
}
|
|
4007
|
+
// Otherwise, run if we're not yet initialized with anything
|
|
4008
|
+
return !hasData && !hasError;
|
|
4009
|
+
}
|
|
3992
4010
|
function isNewLoader(currentLoaderData, currentMatch, match) {
|
|
3993
4011
|
let isNew =
|
|
3994
4012
|
// [a] -> [a, b]
|
|
@@ -4020,33 +4038,6 @@ function shouldRevalidateLoader(loaderMatch, arg) {
|
|
|
4020
4038
|
}
|
|
4021
4039
|
return arg.defaultShouldRevalidate;
|
|
4022
4040
|
}
|
|
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
4041
|
function patchRoutesImpl(routeId, children, routesToUse, manifest, mapRouteProperties) {
|
|
4051
4042
|
var _childrenToPatch;
|
|
4052
4043
|
let childrenToPatch;
|
|
@@ -4063,10 +4054,31 @@ function patchRoutesImpl(routeId, children, routesToUse, manifest, mapRoutePrope
|
|
|
4063
4054
|
// Don't patch in routes we already know about so that `patch` is idempotent
|
|
4064
4055
|
// to simplify user-land code. This is useful because we re-call the
|
|
4065
4056
|
// `patchRoutesOnNavigation` function for matched routes with params.
|
|
4066
|
-
let uniqueChildren = children.filter(
|
|
4057
|
+
let uniqueChildren = children.filter(newRoute => !childrenToPatch.some(existingRoute => isSameRoute(newRoute, existingRoute)));
|
|
4067
4058
|
let newRoutes = convertRoutesToDataRoutes(uniqueChildren, mapRouteProperties, [routeId || "_", "patch", String(((_childrenToPatch = childrenToPatch) == null ? void 0 : _childrenToPatch.length) || "0")], manifest);
|
|
4068
4059
|
childrenToPatch.push(...newRoutes);
|
|
4069
4060
|
}
|
|
4061
|
+
function isSameRoute(newRoute, existingRoute) {
|
|
4062
|
+
// Most optimal check is by id
|
|
4063
|
+
if ("id" in newRoute && "id" in existingRoute && newRoute.id === existingRoute.id) {
|
|
4064
|
+
return true;
|
|
4065
|
+
}
|
|
4066
|
+
// Second is by pathing differences
|
|
4067
|
+
if (!(newRoute.index === existingRoute.index && newRoute.path === existingRoute.path && newRoute.caseSensitive === existingRoute.caseSensitive)) {
|
|
4068
|
+
return false;
|
|
4069
|
+
}
|
|
4070
|
+
// Pathless layout routes are trickier since we need to check children.
|
|
4071
|
+
// If they have no children then they're the same as far as we can tell
|
|
4072
|
+
if ((!newRoute.children || newRoute.children.length === 0) && (!existingRoute.children || existingRoute.children.length === 0)) {
|
|
4073
|
+
return true;
|
|
4074
|
+
}
|
|
4075
|
+
// Otherwise, we look to see if every child in the new route is already
|
|
4076
|
+
// represented in the existing route's children
|
|
4077
|
+
return newRoute.children.every((aChild, i) => {
|
|
4078
|
+
var _existingRoute$childr;
|
|
4079
|
+
return (_existingRoute$childr = existingRoute.children) == null ? void 0 : _existingRoute$childr.some(bChild => isSameRoute(aChild, bChild));
|
|
4080
|
+
});
|
|
4081
|
+
}
|
|
4070
4082
|
/**
|
|
4071
4083
|
* Execute route.lazy() methods to lazily load route modules (loader, action,
|
|
4072
4084
|
* shouldRevalidate) and update the routeManifest in place which shares objects
|
|
@@ -4116,10 +4128,10 @@ async function loadLazyRouteModule(route, mapRouteProperties, manifest) {
|
|
|
4116
4128
|
}));
|
|
4117
4129
|
}
|
|
4118
4130
|
// Default implementation of `dataStrategy` which fetches all loaders in parallel
|
|
4119
|
-
async function defaultDataStrategy(
|
|
4131
|
+
async function defaultDataStrategy(_ref4) {
|
|
4120
4132
|
let {
|
|
4121
4133
|
matches
|
|
4122
|
-
} =
|
|
4134
|
+
} = _ref4;
|
|
4123
4135
|
let matchesToLoad = matches.filter(m => m.shouldLoad);
|
|
4124
4136
|
let results = await Promise.all(matchesToLoad.map(m => m.resolve()));
|
|
4125
4137
|
return results.reduce((acc, result, i) => Object.assign(acc, {
|
|
@@ -4522,7 +4534,7 @@ function processRouteLoaderData(matches, results, pendingActionResult, activeDef
|
|
|
4522
4534
|
loaderHeaders
|
|
4523
4535
|
};
|
|
4524
4536
|
}
|
|
4525
|
-
function processLoaderData(state, matches,
|
|
4537
|
+
function processLoaderData(state, matches, results, pendingActionResult, revalidatingFetchers, fetcherResults, activeDeferreds) {
|
|
4526
4538
|
let {
|
|
4527
4539
|
loaderData,
|
|
4528
4540
|
errors
|
|
@@ -4634,9 +4646,7 @@ function getInternalRouterError(status, _temp5) {
|
|
|
4634
4646
|
let errorMessage = "Unknown @remix-run/router error";
|
|
4635
4647
|
if (status === 400) {
|
|
4636
4648
|
statusText = "Bad Request";
|
|
4637
|
-
if (
|
|
4638
|
-
errorMessage = "Unable to match URL \"" + pathname + "\" - the `patchRoutesOnNavigation()` " + ("function threw the following error:\n" + message);
|
|
4639
|
-
} else if (method && pathname && routeId) {
|
|
4649
|
+
if (method && pathname && routeId) {
|
|
4640
4650
|
errorMessage = "You made a " + method + " request to \"" + pathname + "\" but " + ("did not provide a `loader` for route \"" + routeId + "\", ") + "so there is no way to handle the request.";
|
|
4641
4651
|
} else if (type === "defer-action") {
|
|
4642
4652
|
errorMessage = "defer() is not supported in actions";
|
|
@@ -4696,9 +4706,6 @@ function isHashChangeOnly(a, b) {
|
|
|
4696
4706
|
// /page#hash -> /page
|
|
4697
4707
|
return false;
|
|
4698
4708
|
}
|
|
4699
|
-
function isPromise(val) {
|
|
4700
|
-
return typeof val === "object" && val != null && "then" in val;
|
|
4701
|
-
}
|
|
4702
4709
|
function isDataStrategyResult(result) {
|
|
4703
4710
|
return result != null && typeof result === "object" && "type" in result && "result" in result && (result.type === ResultType.data || result.type === ResultType.error);
|
|
4704
4711
|
}
|