@remix-run/router 1.19.2 → 1.20.0-pre.1
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 +33 -0
- package/dist/index.d.ts +2 -2
- package/dist/router.cjs.js +194 -204
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.d.ts +8 -8
- package/dist/router.js +186 -201
- package/dist/router.js.map +1 -1
- package/dist/router.umd.js +194 -204
- package/dist/router.umd.js.map +1 -1
- package/dist/router.umd.min.js +2 -2
- package/dist/router.umd.min.js.map +1 -1
- package/dist/utils.d.ts +6 -7
- package/index.ts +7 -6
- package/package.json +1 -1
- package/router.ts +244 -252
- package/utils.ts +14 -8
package/router.ts
CHANGED
|
@@ -391,8 +391,8 @@ export interface RouterInit {
|
|
|
391
391
|
future?: Partial<FutureConfig>;
|
|
392
392
|
hydrationData?: HydrationState;
|
|
393
393
|
window?: Window;
|
|
394
|
-
|
|
395
|
-
|
|
394
|
+
dataStrategy?: DataStrategyFunction;
|
|
395
|
+
patchRoutesOnNavigation?: AgnosticPatchRoutesOnNavigationFunction;
|
|
396
396
|
}
|
|
397
397
|
|
|
398
398
|
/**
|
|
@@ -422,7 +422,7 @@ export interface StaticHandler {
|
|
|
422
422
|
opts?: {
|
|
423
423
|
requestContext?: unknown;
|
|
424
424
|
skipLoaderErrorBubbling?: boolean;
|
|
425
|
-
|
|
425
|
+
dataStrategy?: DataStrategyFunction;
|
|
426
426
|
}
|
|
427
427
|
): Promise<StaticHandlerContext | Response>;
|
|
428
428
|
queryRoute(
|
|
@@ -430,7 +430,7 @@ export interface StaticHandler {
|
|
|
430
430
|
opts?: {
|
|
431
431
|
routeId?: string;
|
|
432
432
|
requestContext?: unknown;
|
|
433
|
-
|
|
433
|
+
dataStrategy?: DataStrategyFunction;
|
|
434
434
|
}
|
|
435
435
|
): Promise<any>;
|
|
436
436
|
}
|
|
@@ -448,8 +448,8 @@ export interface RouterSubscriber {
|
|
|
448
448
|
state: RouterState,
|
|
449
449
|
opts: {
|
|
450
450
|
deletedFetchers: string[];
|
|
451
|
-
|
|
452
|
-
|
|
451
|
+
viewTransitionOpts?: ViewTransitionOpts;
|
|
452
|
+
flushSync: boolean;
|
|
453
453
|
}
|
|
454
454
|
): void;
|
|
455
455
|
}
|
|
@@ -475,7 +475,7 @@ export type RelativeRoutingType = "route" | "path";
|
|
|
475
475
|
type BaseNavigateOrFetchOptions = {
|
|
476
476
|
preventScrollReset?: boolean;
|
|
477
477
|
relative?: RelativeRoutingType;
|
|
478
|
-
|
|
478
|
+
flushSync?: boolean;
|
|
479
479
|
};
|
|
480
480
|
|
|
481
481
|
// Only allowed for navigations
|
|
@@ -483,7 +483,7 @@ type BaseNavigateOptions = BaseNavigateOrFetchOptions & {
|
|
|
483
483
|
replace?: boolean;
|
|
484
484
|
state?: any;
|
|
485
485
|
fromRouteId?: string;
|
|
486
|
-
|
|
486
|
+
viewTransition?: boolean;
|
|
487
487
|
};
|
|
488
488
|
|
|
489
489
|
// Only allowed for submission navigations
|
|
@@ -797,8 +797,8 @@ export function createRouter(init: RouterInit): Router {
|
|
|
797
797
|
);
|
|
798
798
|
let inFlightDataRoutes: AgnosticDataRouteObject[] | undefined;
|
|
799
799
|
let basename = init.basename || "/";
|
|
800
|
-
let dataStrategyImpl = init.
|
|
801
|
-
let patchRoutesOnNavigationImpl = init.
|
|
800
|
+
let dataStrategyImpl = init.dataStrategy || defaultDataStrategy;
|
|
801
|
+
let patchRoutesOnNavigationImpl = init.patchRoutesOnNavigation;
|
|
802
802
|
|
|
803
803
|
// Config driven behavior flags
|
|
804
804
|
let future: FutureConfig = {
|
|
@@ -814,10 +814,6 @@ export function createRouter(init: RouterInit): Router {
|
|
|
814
814
|
let unlistenHistory: (() => void) | null = null;
|
|
815
815
|
// Externally-provided functions to call on all state changes
|
|
816
816
|
let subscribers = new Set<RouterSubscriber>();
|
|
817
|
-
// FIFO queue of previously discovered routes to prevent re-calling on
|
|
818
|
-
// subsequent navigations to the same path
|
|
819
|
-
let discoveredRoutesMaxSize = 1000;
|
|
820
|
-
let discoveredRoutes = new Set<string>();
|
|
821
817
|
// Externally-provided object to hold scroll restoration locations during routing
|
|
822
818
|
let savedScrollPositions: Record<string, number> | null = null;
|
|
823
819
|
// Externally-provided function to get scroll restoration keys
|
|
@@ -894,33 +890,18 @@ export function createRouter(init: RouterInit): Router {
|
|
|
894
890
|
// were marked for explicit hydration
|
|
895
891
|
let loaderData = init.hydrationData ? init.hydrationData.loaderData : null;
|
|
896
892
|
let errors = init.hydrationData ? init.hydrationData.errors : null;
|
|
897
|
-
let isRouteInitialized = (m: AgnosticDataRouteMatch) => {
|
|
898
|
-
// No loader, nothing to initialize
|
|
899
|
-
if (!m.route.loader) {
|
|
900
|
-
return true;
|
|
901
|
-
}
|
|
902
|
-
// Explicitly opting-in to running on hydration
|
|
903
|
-
if (
|
|
904
|
-
typeof m.route.loader === "function" &&
|
|
905
|
-
m.route.loader.hydrate === true
|
|
906
|
-
) {
|
|
907
|
-
return false;
|
|
908
|
-
}
|
|
909
|
-
// Otherwise, initialized if hydrated with data or an error
|
|
910
|
-
return (
|
|
911
|
-
(loaderData && loaderData[m.route.id] !== undefined) ||
|
|
912
|
-
(errors && errors[m.route.id] !== undefined)
|
|
913
|
-
);
|
|
914
|
-
};
|
|
915
|
-
|
|
916
893
|
// If errors exist, don't consider routes below the boundary
|
|
917
894
|
if (errors) {
|
|
918
895
|
let idx = initialMatches.findIndex(
|
|
919
896
|
(m) => errors![m.route.id] !== undefined
|
|
920
897
|
);
|
|
921
|
-
initialized = initialMatches
|
|
898
|
+
initialized = initialMatches
|
|
899
|
+
.slice(0, idx + 1)
|
|
900
|
+
.every((m) => !shouldLoadRouteOnHydration(m.route, loaderData, errors));
|
|
922
901
|
} else {
|
|
923
|
-
initialized = initialMatches.every(
|
|
902
|
+
initialized = initialMatches.every(
|
|
903
|
+
(m) => !shouldLoadRouteOnHydration(m.route, loaderData, errors)
|
|
904
|
+
);
|
|
924
905
|
}
|
|
925
906
|
} else {
|
|
926
907
|
// Without partial hydration - we're initialized if we were provided any
|
|
@@ -1187,8 +1168,8 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1187
1168
|
[...subscribers].forEach((subscriber) =>
|
|
1188
1169
|
subscriber(state, {
|
|
1189
1170
|
deletedFetchers: deletedFetchersKeys,
|
|
1190
|
-
|
|
1191
|
-
|
|
1171
|
+
viewTransitionOpts: opts.viewTransitionOpts,
|
|
1172
|
+
flushSync: opts.flushSync === true,
|
|
1192
1173
|
})
|
|
1193
1174
|
);
|
|
1194
1175
|
|
|
@@ -1411,7 +1392,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1411
1392
|
? opts.preventScrollReset === true
|
|
1412
1393
|
: undefined;
|
|
1413
1394
|
|
|
1414
|
-
let flushSync = (opts && opts.
|
|
1395
|
+
let flushSync = (opts && opts.flushSync) === true;
|
|
1415
1396
|
|
|
1416
1397
|
let blockerKey = shouldBlockNavigation({
|
|
1417
1398
|
currentLocation,
|
|
@@ -1450,7 +1431,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1450
1431
|
pendingError: error,
|
|
1451
1432
|
preventScrollReset,
|
|
1452
1433
|
replace: opts && opts.replace,
|
|
1453
|
-
enableViewTransition: opts && opts.
|
|
1434
|
+
enableViewTransition: opts && opts.viewTransition,
|
|
1454
1435
|
flushSync,
|
|
1455
1436
|
});
|
|
1456
1437
|
}
|
|
@@ -1559,7 +1540,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1559
1540
|
// Short circuit if it's only a hash change and not a revalidation or
|
|
1560
1541
|
// mutation submission.
|
|
1561
1542
|
//
|
|
1562
|
-
// Ignore on initial page loads because since the initial
|
|
1543
|
+
// Ignore on initial page loads because since the initial hydration will always
|
|
1563
1544
|
// be "same hash". For example, on /page#hash and submit a <Form method="post">
|
|
1564
1545
|
// which will default to a navigation to /page
|
|
1565
1546
|
if (
|
|
@@ -1709,17 +1690,15 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1709
1690
|
if (discoverResult.type === "aborted") {
|
|
1710
1691
|
return { shortCircuited: true };
|
|
1711
1692
|
} else if (discoverResult.type === "error") {
|
|
1712
|
-
let
|
|
1713
|
-
|
|
1714
|
-
discoverResult
|
|
1715
|
-
);
|
|
1693
|
+
let boundaryId = findNearestBoundary(discoverResult.partialMatches)
|
|
1694
|
+
.route.id;
|
|
1716
1695
|
return {
|
|
1717
1696
|
matches: discoverResult.partialMatches,
|
|
1718
1697
|
pendingActionResult: [
|
|
1719
1698
|
boundaryId,
|
|
1720
1699
|
{
|
|
1721
1700
|
type: ResultType.error,
|
|
1722
|
-
error,
|
|
1701
|
+
error: discoverResult.error,
|
|
1723
1702
|
},
|
|
1724
1703
|
],
|
|
1725
1704
|
};
|
|
@@ -1887,15 +1866,13 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1887
1866
|
if (discoverResult.type === "aborted") {
|
|
1888
1867
|
return { shortCircuited: true };
|
|
1889
1868
|
} else if (discoverResult.type === "error") {
|
|
1890
|
-
let
|
|
1891
|
-
|
|
1892
|
-
discoverResult
|
|
1893
|
-
);
|
|
1869
|
+
let boundaryId = findNearestBoundary(discoverResult.partialMatches)
|
|
1870
|
+
.route.id;
|
|
1894
1871
|
return {
|
|
1895
1872
|
matches: discoverResult.partialMatches,
|
|
1896
1873
|
loaderData: {},
|
|
1897
1874
|
errors: {
|
|
1898
|
-
[boundaryId]: error,
|
|
1875
|
+
[boundaryId]: discoverResult.error,
|
|
1899
1876
|
},
|
|
1900
1877
|
};
|
|
1901
1878
|
} else if (!discoverResult.matches) {
|
|
@@ -1983,9 +1960,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1983
1960
|
}
|
|
1984
1961
|
|
|
1985
1962
|
revalidatingFetchers.forEach((rf) => {
|
|
1986
|
-
|
|
1987
|
-
abortFetcher(rf.key);
|
|
1988
|
-
}
|
|
1963
|
+
abortFetcher(rf.key);
|
|
1989
1964
|
if (rf.controller) {
|
|
1990
1965
|
// Fetchers use an independent AbortController so that aborting a fetcher
|
|
1991
1966
|
// (via deleteFetcher) does not abort the triggering navigation that
|
|
@@ -2026,6 +2001,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2026
2001
|
abortPendingFetchRevalidations
|
|
2027
2002
|
);
|
|
2028
2003
|
}
|
|
2004
|
+
|
|
2029
2005
|
revalidatingFetchers.forEach((rf) => fetchControllers.delete(rf.key));
|
|
2030
2006
|
|
|
2031
2007
|
// If any loaders returned a redirect Response, start a new REPLACE navigation
|
|
@@ -2053,7 +2029,6 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2053
2029
|
let { loaderData, errors } = processLoaderData(
|
|
2054
2030
|
state,
|
|
2055
2031
|
matches,
|
|
2056
|
-
matchesToLoad,
|
|
2057
2032
|
loaderResults,
|
|
2058
2033
|
pendingActionResult,
|
|
2059
2034
|
revalidatingFetchers,
|
|
@@ -2073,13 +2048,9 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2073
2048
|
});
|
|
2074
2049
|
});
|
|
2075
2050
|
|
|
2076
|
-
//
|
|
2051
|
+
// Preserve SSR errors during partial hydration
|
|
2077
2052
|
if (future.v7_partialHydration && initialHydration && state.errors) {
|
|
2078
|
-
|
|
2079
|
-
.filter(([id]) => !matchesToLoad.some((m) => m.route.id === id))
|
|
2080
|
-
.forEach(([routeId, error]) => {
|
|
2081
|
-
errors = Object.assign(errors || {}, { [routeId]: error });
|
|
2082
|
-
});
|
|
2053
|
+
errors = { ...state.errors, ...errors };
|
|
2083
2054
|
}
|
|
2084
2055
|
|
|
2085
2056
|
let updatedFetchers = markFetchRedirectsDone();
|
|
@@ -2143,8 +2114,9 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2143
2114
|
);
|
|
2144
2115
|
}
|
|
2145
2116
|
|
|
2146
|
-
|
|
2147
|
-
|
|
2117
|
+
abortFetcher(key);
|
|
2118
|
+
|
|
2119
|
+
let flushSync = (opts && opts.flushSync) === true;
|
|
2148
2120
|
|
|
2149
2121
|
let routesToUse = inFlightDataRoutes || dataRoutes;
|
|
2150
2122
|
let normalizedPath = normalizeTo(
|
|
@@ -2188,7 +2160,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2188
2160
|
|
|
2189
2161
|
let match = getTargetMatch(matches, path);
|
|
2190
2162
|
|
|
2191
|
-
|
|
2163
|
+
let preventScrollReset = (opts && opts.preventScrollReset) === true;
|
|
2192
2164
|
|
|
2193
2165
|
if (submission && isMutationMethod(submission.formMethod)) {
|
|
2194
2166
|
handleFetcherAction(
|
|
@@ -2199,6 +2171,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2199
2171
|
matches,
|
|
2200
2172
|
fogOfWar.active,
|
|
2201
2173
|
flushSync,
|
|
2174
|
+
preventScrollReset,
|
|
2202
2175
|
submission
|
|
2203
2176
|
);
|
|
2204
2177
|
return;
|
|
@@ -2215,6 +2188,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2215
2188
|
matches,
|
|
2216
2189
|
fogOfWar.active,
|
|
2217
2190
|
flushSync,
|
|
2191
|
+
preventScrollReset,
|
|
2218
2192
|
submission
|
|
2219
2193
|
);
|
|
2220
2194
|
}
|
|
@@ -2229,6 +2203,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2229
2203
|
requestMatches: AgnosticDataRouteMatch[],
|
|
2230
2204
|
isFogOfWar: boolean,
|
|
2231
2205
|
flushSync: boolean,
|
|
2206
|
+
preventScrollReset: boolean,
|
|
2232
2207
|
submission: Submission
|
|
2233
2208
|
) {
|
|
2234
2209
|
interruptActiveLoads();
|
|
@@ -2275,8 +2250,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2275
2250
|
if (discoverResult.type === "aborted") {
|
|
2276
2251
|
return;
|
|
2277
2252
|
} else if (discoverResult.type === "error") {
|
|
2278
|
-
|
|
2279
|
-
setFetcherError(key, routeId, error, { flushSync });
|
|
2253
|
+
setFetcherError(key, routeId, discoverResult.error, { flushSync });
|
|
2280
2254
|
return;
|
|
2281
2255
|
} else if (!discoverResult.matches) {
|
|
2282
2256
|
setFetcherError(
|
|
@@ -2343,6 +2317,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2343
2317
|
updateFetcherState(key, getLoadingFetcher(submission));
|
|
2344
2318
|
return startRedirectNavigation(fetchRequest, actionResult, false, {
|
|
2345
2319
|
fetcherSubmission: submission,
|
|
2320
|
+
preventScrollReset,
|
|
2346
2321
|
});
|
|
2347
2322
|
}
|
|
2348
2323
|
}
|
|
@@ -2412,9 +2387,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2412
2387
|
existingFetcher ? existingFetcher.data : undefined
|
|
2413
2388
|
);
|
|
2414
2389
|
state.fetchers.set(staleKey, revalidatingFetcher);
|
|
2415
|
-
|
|
2416
|
-
abortFetcher(staleKey);
|
|
2417
|
-
}
|
|
2390
|
+
abortFetcher(staleKey);
|
|
2418
2391
|
if (rf.controller) {
|
|
2419
2392
|
fetchControllers.set(staleKey, rf.controller);
|
|
2420
2393
|
}
|
|
@@ -2457,7 +2430,8 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2457
2430
|
return startRedirectNavigation(
|
|
2458
2431
|
revalidationRequest,
|
|
2459
2432
|
redirect.result,
|
|
2460
|
-
false
|
|
2433
|
+
false,
|
|
2434
|
+
{ preventScrollReset }
|
|
2461
2435
|
);
|
|
2462
2436
|
}
|
|
2463
2437
|
|
|
@@ -2470,7 +2444,8 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2470
2444
|
return startRedirectNavigation(
|
|
2471
2445
|
revalidationRequest,
|
|
2472
2446
|
redirect.result,
|
|
2473
|
-
false
|
|
2447
|
+
false,
|
|
2448
|
+
{ preventScrollReset }
|
|
2474
2449
|
);
|
|
2475
2450
|
}
|
|
2476
2451
|
|
|
@@ -2478,7 +2453,6 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2478
2453
|
let { loaderData, errors } = processLoaderData(
|
|
2479
2454
|
state,
|
|
2480
2455
|
matches,
|
|
2481
|
-
matchesToLoad,
|
|
2482
2456
|
loaderResults,
|
|
2483
2457
|
undefined,
|
|
2484
2458
|
revalidatingFetchers,
|
|
@@ -2538,6 +2512,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2538
2512
|
matches: AgnosticDataRouteMatch[],
|
|
2539
2513
|
isFogOfWar: boolean,
|
|
2540
2514
|
flushSync: boolean,
|
|
2515
|
+
preventScrollReset: boolean,
|
|
2541
2516
|
submission?: Submission
|
|
2542
2517
|
) {
|
|
2543
2518
|
let existingFetcher = state.fetchers.get(key);
|
|
@@ -2567,8 +2542,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2567
2542
|
if (discoverResult.type === "aborted") {
|
|
2568
2543
|
return;
|
|
2569
2544
|
} else if (discoverResult.type === "error") {
|
|
2570
|
-
|
|
2571
|
-
setFetcherError(key, routeId, error, { flushSync });
|
|
2545
|
+
setFetcherError(key, routeId, discoverResult.error, { flushSync });
|
|
2572
2546
|
return;
|
|
2573
2547
|
} else if (!discoverResult.matches) {
|
|
2574
2548
|
setFetcherError(
|
|
@@ -2634,7 +2608,9 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2634
2608
|
return;
|
|
2635
2609
|
} else {
|
|
2636
2610
|
fetchRedirectIds.add(key);
|
|
2637
|
-
await startRedirectNavigation(fetchRequest, result, false
|
|
2611
|
+
await startRedirectNavigation(fetchRequest, result, false, {
|
|
2612
|
+
preventScrollReset,
|
|
2613
|
+
});
|
|
2638
2614
|
return;
|
|
2639
2615
|
}
|
|
2640
2616
|
}
|
|
@@ -2677,10 +2653,12 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2677
2653
|
{
|
|
2678
2654
|
submission,
|
|
2679
2655
|
fetcherSubmission,
|
|
2656
|
+
preventScrollReset,
|
|
2680
2657
|
replace,
|
|
2681
2658
|
}: {
|
|
2682
2659
|
submission?: Submission;
|
|
2683
2660
|
fetcherSubmission?: Submission;
|
|
2661
|
+
preventScrollReset?: boolean;
|
|
2684
2662
|
replace?: boolean;
|
|
2685
2663
|
} = {}
|
|
2686
2664
|
) {
|
|
@@ -2761,7 +2739,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2761
2739
|
formAction: location,
|
|
2762
2740
|
},
|
|
2763
2741
|
// Preserve these flags across redirects
|
|
2764
|
-
preventScrollReset: pendingPreventScrollReset,
|
|
2742
|
+
preventScrollReset: preventScrollReset || pendingPreventScrollReset,
|
|
2765
2743
|
enableViewTransition: isNavigation
|
|
2766
2744
|
? pendingViewTransitionEnabled
|
|
2767
2745
|
: undefined,
|
|
@@ -2778,7 +2756,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2778
2756
|
// Send fetcher submissions through for shouldRevalidate
|
|
2779
2757
|
fetcherSubmission,
|
|
2780
2758
|
// Preserve these flags across redirects
|
|
2781
|
-
preventScrollReset: pendingPreventScrollReset,
|
|
2759
|
+
preventScrollReset: preventScrollReset || pendingPreventScrollReset,
|
|
2782
2760
|
enableViewTransition: isNavigation
|
|
2783
2761
|
? pendingViewTransitionEnabled
|
|
2784
2762
|
: undefined,
|
|
@@ -2927,8 +2905,8 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2927
2905
|
fetchLoadMatches.forEach((_, key) => {
|
|
2928
2906
|
if (fetchControllers.has(key)) {
|
|
2929
2907
|
cancelledFetcherLoads.add(key);
|
|
2930
|
-
abortFetcher(key);
|
|
2931
2908
|
}
|
|
2909
|
+
abortFetcher(key);
|
|
2932
2910
|
});
|
|
2933
2911
|
}
|
|
2934
2912
|
|
|
@@ -3011,9 +2989,10 @@ export function createRouter(init: RouterInit): Router {
|
|
|
3011
2989
|
|
|
3012
2990
|
function abortFetcher(key: string) {
|
|
3013
2991
|
let controller = fetchControllers.get(key);
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
2992
|
+
if (controller) {
|
|
2993
|
+
controller.abort();
|
|
2994
|
+
fetchControllers.delete(key);
|
|
2995
|
+
}
|
|
3017
2996
|
}
|
|
3018
2997
|
|
|
3019
2998
|
function markFetchersDone(keys: string[]) {
|
|
@@ -3139,23 +3118,6 @@ export function createRouter(init: RouterInit): Router {
|
|
|
3139
3118
|
return { notFoundMatches: matches, route, error };
|
|
3140
3119
|
}
|
|
3141
3120
|
|
|
3142
|
-
function handleDiscoverRouteError(
|
|
3143
|
-
pathname: string,
|
|
3144
|
-
discoverResult: DiscoverRoutesErrorResult
|
|
3145
|
-
) {
|
|
3146
|
-
return {
|
|
3147
|
-
boundaryId: findNearestBoundary(discoverResult.partialMatches).route.id,
|
|
3148
|
-
error: getInternalRouterError(400, {
|
|
3149
|
-
type: "route-discovery",
|
|
3150
|
-
pathname,
|
|
3151
|
-
message:
|
|
3152
|
-
discoverResult.error != null && "message" in discoverResult.error
|
|
3153
|
-
? discoverResult.error
|
|
3154
|
-
: String(discoverResult.error),
|
|
3155
|
-
}),
|
|
3156
|
-
};
|
|
3157
|
-
}
|
|
3158
|
-
|
|
3159
3121
|
function cancelActiveDeferreds(
|
|
3160
3122
|
predicate?: (routeId: string) => boolean
|
|
3161
3123
|
): string[] {
|
|
@@ -3243,13 +3205,6 @@ export function createRouter(init: RouterInit): Router {
|
|
|
3243
3205
|
pathname: string
|
|
3244
3206
|
): { active: boolean; matches: AgnosticDataRouteMatch[] | null } {
|
|
3245
3207
|
if (patchRoutesOnNavigationImpl) {
|
|
3246
|
-
// Don't bother re-calling patchRouteOnMiss for a path we've already
|
|
3247
|
-
// processed. the last execution would have patched the route tree
|
|
3248
|
-
// accordingly so `matches` here are already accurate.
|
|
3249
|
-
if (discoveredRoutes.has(pathname)) {
|
|
3250
|
-
return { active: false, matches };
|
|
3251
|
-
}
|
|
3252
|
-
|
|
3253
3208
|
if (!matches) {
|
|
3254
3209
|
let fogMatches = matchRoutesImpl<AgnosticDataRouteObject>(
|
|
3255
3210
|
routesToUse,
|
|
@@ -3298,21 +3253,30 @@ export function createRouter(init: RouterInit): Router {
|
|
|
3298
3253
|
pathname: string,
|
|
3299
3254
|
signal: AbortSignal
|
|
3300
3255
|
): Promise<DiscoverRoutesResult> {
|
|
3256
|
+
if (!patchRoutesOnNavigationImpl) {
|
|
3257
|
+
return { type: "success", matches };
|
|
3258
|
+
}
|
|
3259
|
+
|
|
3301
3260
|
let partialMatches: AgnosticDataRouteMatch[] | null = matches;
|
|
3302
3261
|
while (true) {
|
|
3303
3262
|
let isNonHMR = inFlightDataRoutes == null;
|
|
3304
3263
|
let routesToUse = inFlightDataRoutes || dataRoutes;
|
|
3264
|
+
let localManifest = manifest;
|
|
3305
3265
|
try {
|
|
3306
|
-
await
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3266
|
+
await patchRoutesOnNavigationImpl({
|
|
3267
|
+
path: pathname,
|
|
3268
|
+
matches: partialMatches,
|
|
3269
|
+
patch: (routeId, children) => {
|
|
3270
|
+
if (signal.aborted) return;
|
|
3271
|
+
patchRoutesImpl(
|
|
3272
|
+
routeId,
|
|
3273
|
+
children,
|
|
3274
|
+
routesToUse,
|
|
3275
|
+
localManifest,
|
|
3276
|
+
mapRouteProperties
|
|
3277
|
+
);
|
|
3278
|
+
},
|
|
3279
|
+
});
|
|
3316
3280
|
} catch (e) {
|
|
3317
3281
|
return { type: "error", error: e, partialMatches };
|
|
3318
3282
|
} finally {
|
|
@@ -3322,7 +3286,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
3322
3286
|
// trigger a re-run of memoized `router.routes` dependencies.
|
|
3323
3287
|
// HMR will already update the identity and reflow when it lands
|
|
3324
3288
|
// `inFlightDataRoutes` in `completeNavigation`
|
|
3325
|
-
if (isNonHMR) {
|
|
3289
|
+
if (isNonHMR && !signal.aborted) {
|
|
3326
3290
|
dataRoutes = [...dataRoutes];
|
|
3327
3291
|
}
|
|
3328
3292
|
}
|
|
@@ -3333,7 +3297,6 @@ export function createRouter(init: RouterInit): Router {
|
|
|
3333
3297
|
|
|
3334
3298
|
let newMatches = matchRoutes(routesToUse, pathname, basename);
|
|
3335
3299
|
if (newMatches) {
|
|
3336
|
-
addToFifoQueue(pathname, discoveredRoutes);
|
|
3337
3300
|
return { type: "success", matches: newMatches };
|
|
3338
3301
|
}
|
|
3339
3302
|
|
|
@@ -3352,7 +3315,6 @@ export function createRouter(init: RouterInit): Router {
|
|
|
3352
3315
|
(m, i) => m.route.id === newPartialMatches![i].route.id
|
|
3353
3316
|
))
|
|
3354
3317
|
) {
|
|
3355
|
-
addToFifoQueue(pathname, discoveredRoutes);
|
|
3356
3318
|
return { type: "success", matches: null };
|
|
3357
3319
|
}
|
|
3358
3320
|
|
|
@@ -3360,14 +3322,6 @@ export function createRouter(init: RouterInit): Router {
|
|
|
3360
3322
|
}
|
|
3361
3323
|
}
|
|
3362
3324
|
|
|
3363
|
-
function addToFifoQueue(path: string, queue: Set<string>) {
|
|
3364
|
-
if (queue.size >= discoveredRoutesMaxSize) {
|
|
3365
|
-
let first = queue.values().next().value;
|
|
3366
|
-
queue.delete(first);
|
|
3367
|
-
}
|
|
3368
|
-
queue.add(path);
|
|
3369
|
-
}
|
|
3370
|
-
|
|
3371
3325
|
function _internalSetRoutes(newRoutes: AgnosticDataRouteObject[]) {
|
|
3372
3326
|
manifest = {};
|
|
3373
3327
|
inFlightDataRoutes = convertRoutesToDataRoutes(
|
|
@@ -3538,11 +3492,11 @@ export function createStaticHandler(
|
|
|
3538
3492
|
{
|
|
3539
3493
|
requestContext,
|
|
3540
3494
|
skipLoaderErrorBubbling,
|
|
3541
|
-
|
|
3495
|
+
dataStrategy,
|
|
3542
3496
|
}: {
|
|
3543
3497
|
requestContext?: unknown;
|
|
3544
3498
|
skipLoaderErrorBubbling?: boolean;
|
|
3545
|
-
|
|
3499
|
+
dataStrategy?: DataStrategyFunction;
|
|
3546
3500
|
} = {}
|
|
3547
3501
|
): Promise<StaticHandlerContext | Response> {
|
|
3548
3502
|
let url = new URL(request.url);
|
|
@@ -3594,7 +3548,7 @@ export function createStaticHandler(
|
|
|
3594
3548
|
location,
|
|
3595
3549
|
matches,
|
|
3596
3550
|
requestContext,
|
|
3597
|
-
|
|
3551
|
+
dataStrategy || null,
|
|
3598
3552
|
skipLoaderErrorBubbling === true,
|
|
3599
3553
|
null
|
|
3600
3554
|
);
|
|
@@ -3639,11 +3593,11 @@ export function createStaticHandler(
|
|
|
3639
3593
|
{
|
|
3640
3594
|
routeId,
|
|
3641
3595
|
requestContext,
|
|
3642
|
-
|
|
3596
|
+
dataStrategy,
|
|
3643
3597
|
}: {
|
|
3644
3598
|
requestContext?: unknown;
|
|
3645
3599
|
routeId?: string;
|
|
3646
|
-
|
|
3600
|
+
dataStrategy?: DataStrategyFunction;
|
|
3647
3601
|
} = {}
|
|
3648
3602
|
): Promise<any> {
|
|
3649
3603
|
let url = new URL(request.url);
|
|
@@ -3677,7 +3631,7 @@ export function createStaticHandler(
|
|
|
3677
3631
|
location,
|
|
3678
3632
|
matches,
|
|
3679
3633
|
requestContext,
|
|
3680
|
-
|
|
3634
|
+
dataStrategy || null,
|
|
3681
3635
|
false,
|
|
3682
3636
|
match
|
|
3683
3637
|
);
|
|
@@ -3716,7 +3670,7 @@ export function createStaticHandler(
|
|
|
3716
3670
|
location: Location,
|
|
3717
3671
|
matches: AgnosticDataRouteMatch[],
|
|
3718
3672
|
requestContext: unknown,
|
|
3719
|
-
|
|
3673
|
+
dataStrategy: DataStrategyFunction | null,
|
|
3720
3674
|
skipLoaderErrorBubbling: boolean,
|
|
3721
3675
|
routeMatch: AgnosticDataRouteMatch | null
|
|
3722
3676
|
): Promise<Omit<StaticHandlerContext, "location" | "basename"> | Response> {
|
|
@@ -3732,7 +3686,7 @@ export function createStaticHandler(
|
|
|
3732
3686
|
matches,
|
|
3733
3687
|
routeMatch || getTargetMatch(matches, location),
|
|
3734
3688
|
requestContext,
|
|
3735
|
-
|
|
3689
|
+
dataStrategy,
|
|
3736
3690
|
skipLoaderErrorBubbling,
|
|
3737
3691
|
routeMatch != null
|
|
3738
3692
|
);
|
|
@@ -3743,7 +3697,7 @@ export function createStaticHandler(
|
|
|
3743
3697
|
request,
|
|
3744
3698
|
matches,
|
|
3745
3699
|
requestContext,
|
|
3746
|
-
|
|
3700
|
+
dataStrategy,
|
|
3747
3701
|
skipLoaderErrorBubbling,
|
|
3748
3702
|
routeMatch
|
|
3749
3703
|
);
|
|
@@ -3778,7 +3732,7 @@ export function createStaticHandler(
|
|
|
3778
3732
|
matches: AgnosticDataRouteMatch[],
|
|
3779
3733
|
actionMatch: AgnosticDataRouteMatch,
|
|
3780
3734
|
requestContext: unknown,
|
|
3781
|
-
|
|
3735
|
+
dataStrategy: DataStrategyFunction | null,
|
|
3782
3736
|
skipLoaderErrorBubbling: boolean,
|
|
3783
3737
|
isRouteRequest: boolean
|
|
3784
3738
|
): Promise<Omit<StaticHandlerContext, "location" | "basename"> | Response> {
|
|
@@ -3805,7 +3759,7 @@ export function createStaticHandler(
|
|
|
3805
3759
|
matches,
|
|
3806
3760
|
isRouteRequest,
|
|
3807
3761
|
requestContext,
|
|
3808
|
-
|
|
3762
|
+
dataStrategy
|
|
3809
3763
|
);
|
|
3810
3764
|
result = results[actionMatch.route.id];
|
|
3811
3765
|
|
|
@@ -3877,7 +3831,7 @@ export function createStaticHandler(
|
|
|
3877
3831
|
loaderRequest,
|
|
3878
3832
|
matches,
|
|
3879
3833
|
requestContext,
|
|
3880
|
-
|
|
3834
|
+
dataStrategy,
|
|
3881
3835
|
skipLoaderErrorBubbling,
|
|
3882
3836
|
null,
|
|
3883
3837
|
[boundaryMatch.route.id, result]
|
|
@@ -3902,7 +3856,7 @@ export function createStaticHandler(
|
|
|
3902
3856
|
loaderRequest,
|
|
3903
3857
|
matches,
|
|
3904
3858
|
requestContext,
|
|
3905
|
-
|
|
3859
|
+
dataStrategy,
|
|
3906
3860
|
skipLoaderErrorBubbling,
|
|
3907
3861
|
null
|
|
3908
3862
|
);
|
|
@@ -3924,7 +3878,7 @@ export function createStaticHandler(
|
|
|
3924
3878
|
request: Request,
|
|
3925
3879
|
matches: AgnosticDataRouteMatch[],
|
|
3926
3880
|
requestContext: unknown,
|
|
3927
|
-
|
|
3881
|
+
dataStrategy: DataStrategyFunction | null,
|
|
3928
3882
|
skipLoaderErrorBubbling: boolean,
|
|
3929
3883
|
routeMatch: AgnosticDataRouteMatch | null,
|
|
3930
3884
|
pendingActionResult?: PendingActionResult
|
|
@@ -3987,7 +3941,7 @@ export function createStaticHandler(
|
|
|
3987
3941
|
matches,
|
|
3988
3942
|
isRouteRequest,
|
|
3989
3943
|
requestContext,
|
|
3990
|
-
|
|
3944
|
+
dataStrategy
|
|
3991
3945
|
);
|
|
3992
3946
|
|
|
3993
3947
|
if (request.signal.aborted) {
|
|
@@ -4033,10 +3987,10 @@ export function createStaticHandler(
|
|
|
4033
3987
|
matches: AgnosticDataRouteMatch[],
|
|
4034
3988
|
isRouteRequest: boolean,
|
|
4035
3989
|
requestContext: unknown,
|
|
4036
|
-
|
|
3990
|
+
dataStrategy: DataStrategyFunction | null
|
|
4037
3991
|
): Promise<Record<string, DataResult>> {
|
|
4038
3992
|
let results = await callDataStrategyImpl(
|
|
4039
|
-
|
|
3993
|
+
dataStrategy || defaultDataStrategy,
|
|
4040
3994
|
type,
|
|
4041
3995
|
null,
|
|
4042
3996
|
request,
|
|
@@ -4179,16 +4133,23 @@ function normalizeTo(
|
|
|
4179
4133
|
path.hash = location.hash;
|
|
4180
4134
|
}
|
|
4181
4135
|
|
|
4182
|
-
//
|
|
4183
|
-
if (
|
|
4184
|
-
|
|
4185
|
-
activeRouteMatch &&
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4136
|
+
// Account for `?index` params when routing to the current location
|
|
4137
|
+
if ((to == null || to === "" || to === ".") && activeRouteMatch) {
|
|
4138
|
+
let nakedIndex = hasNakedIndexQuery(path.search);
|
|
4139
|
+
if (activeRouteMatch.route.index && !nakedIndex) {
|
|
4140
|
+
// Add one when we're targeting an index route
|
|
4141
|
+
path.search = path.search
|
|
4142
|
+
? path.search.replace(/^\?/, "?index&")
|
|
4143
|
+
: "?index";
|
|
4144
|
+
} else if (!activeRouteMatch.route.index && nakedIndex) {
|
|
4145
|
+
// Remove existing ones when we're not
|
|
4146
|
+
let params = new URLSearchParams(path.search);
|
|
4147
|
+
let indexValues = params.getAll("index");
|
|
4148
|
+
params.delete("index");
|
|
4149
|
+
indexValues.filter((v) => v).forEach((v) => params.append("index", v));
|
|
4150
|
+
let qs = params.toString();
|
|
4151
|
+
path.search = qs ? `?${qs}` : "";
|
|
4152
|
+
}
|
|
4192
4153
|
}
|
|
4193
4154
|
|
|
4194
4155
|
// If we're operating within a basename, prepend it to the pathname. If
|
|
@@ -4352,20 +4313,18 @@ function normalizeNavigateOptions(
|
|
|
4352
4313
|
return { path: createPath(parsedPath), submission };
|
|
4353
4314
|
}
|
|
4354
4315
|
|
|
4355
|
-
// Filter out all routes below any caught error as they aren't going to
|
|
4316
|
+
// Filter out all routes at/below any caught error as they aren't going to
|
|
4356
4317
|
// render so we don't need to load them
|
|
4357
4318
|
function getLoaderMatchesUntilBoundary(
|
|
4358
4319
|
matches: AgnosticDataRouteMatch[],
|
|
4359
|
-
boundaryId: string
|
|
4320
|
+
boundaryId: string,
|
|
4321
|
+
includeBoundary = false
|
|
4360
4322
|
) {
|
|
4361
|
-
let
|
|
4362
|
-
if (
|
|
4363
|
-
|
|
4364
|
-
if (index >= 0) {
|
|
4365
|
-
boundaryMatches = matches.slice(0, index);
|
|
4366
|
-
}
|
|
4323
|
+
let index = matches.findIndex((m) => m.route.id === boundaryId);
|
|
4324
|
+
if (index >= 0) {
|
|
4325
|
+
return matches.slice(0, includeBoundary ? index + 1 : index);
|
|
4367
4326
|
}
|
|
4368
|
-
return
|
|
4327
|
+
return matches;
|
|
4369
4328
|
}
|
|
4370
4329
|
|
|
4371
4330
|
function getMatchesToLoad(
|
|
@@ -4374,7 +4333,7 @@ function getMatchesToLoad(
|
|
|
4374
4333
|
matches: AgnosticDataRouteMatch[],
|
|
4375
4334
|
submission: Submission | undefined,
|
|
4376
4335
|
location: Location,
|
|
4377
|
-
|
|
4336
|
+
initialHydration: boolean,
|
|
4378
4337
|
skipActionErrorRevalidation: boolean,
|
|
4379
4338
|
isRevalidationRequired: boolean,
|
|
4380
4339
|
cancelledDeferredRoutes: string[],
|
|
@@ -4395,13 +4354,26 @@ function getMatchesToLoad(
|
|
|
4395
4354
|
let nextUrl = history.createURL(location);
|
|
4396
4355
|
|
|
4397
4356
|
// Pick navigation matches that are net-new or qualify for revalidation
|
|
4398
|
-
let
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4357
|
+
let boundaryMatches = matches;
|
|
4358
|
+
if (initialHydration && state.errors) {
|
|
4359
|
+
// On initial hydration, only consider matches up to _and including_ the boundary.
|
|
4360
|
+
// This is inclusive to handle cases where a server loader ran successfully,
|
|
4361
|
+
// a child server loader bubbled up to this route, but this route has
|
|
4362
|
+
// `clientLoader.hydrate` so we want to still run the `clientLoader` so that
|
|
4363
|
+
// we have a complete version of `loaderData`
|
|
4364
|
+
boundaryMatches = getLoaderMatchesUntilBoundary(
|
|
4365
|
+
matches,
|
|
4366
|
+
Object.keys(state.errors)[0],
|
|
4367
|
+
true
|
|
4368
|
+
);
|
|
4369
|
+
} else if (pendingActionResult && isErrorResult(pendingActionResult[1])) {
|
|
4370
|
+
// If an action threw an error, we call loaders up to, but not including the
|
|
4371
|
+
// boundary
|
|
4372
|
+
boundaryMatches = getLoaderMatchesUntilBoundary(
|
|
4373
|
+
matches,
|
|
4374
|
+
pendingActionResult[0]
|
|
4375
|
+
);
|
|
4376
|
+
}
|
|
4405
4377
|
|
|
4406
4378
|
// Don't revalidate loaders by default after action 4xx/5xx responses
|
|
4407
4379
|
// when the flag is enabled. They can still opt-into revalidation via
|
|
@@ -4423,15 +4395,8 @@ function getMatchesToLoad(
|
|
|
4423
4395
|
return false;
|
|
4424
4396
|
}
|
|
4425
4397
|
|
|
4426
|
-
if (
|
|
4427
|
-
|
|
4428
|
-
return true;
|
|
4429
|
-
}
|
|
4430
|
-
return (
|
|
4431
|
-
state.loaderData[route.id] === undefined &&
|
|
4432
|
-
// Don't re-run if the loader ran and threw an error
|
|
4433
|
-
(!state.errors || state.errors[route.id] === undefined)
|
|
4434
|
-
);
|
|
4398
|
+
if (initialHydration) {
|
|
4399
|
+
return shouldLoadRouteOnHydration(route, state.loaderData, state.errors);
|
|
4435
4400
|
}
|
|
4436
4401
|
|
|
4437
4402
|
// Always call the loader on new route instances and pending defer cancellations
|
|
@@ -4473,12 +4438,12 @@ function getMatchesToLoad(
|
|
|
4473
4438
|
let revalidatingFetchers: RevalidatingFetcher[] = [];
|
|
4474
4439
|
fetchLoadMatches.forEach((f, key) => {
|
|
4475
4440
|
// Don't revalidate:
|
|
4476
|
-
// - on initial
|
|
4441
|
+
// - on initial hydration (shouldn't be any fetchers then anyway)
|
|
4477
4442
|
// - if fetcher won't be present in the subsequent render
|
|
4478
4443
|
// - no longer matches the URL (v7_fetcherPersist=false)
|
|
4479
4444
|
// - was unmounted but persisted due to v7_fetcherPersist=true
|
|
4480
4445
|
if (
|
|
4481
|
-
|
|
4446
|
+
initialHydration ||
|
|
4482
4447
|
!matches.some((m) => m.route.id === f.routeId) ||
|
|
4483
4448
|
deletedFetchers.has(key)
|
|
4484
4449
|
) {
|
|
@@ -4558,6 +4523,38 @@ function getMatchesToLoad(
|
|
|
4558
4523
|
return [navigationMatches, revalidatingFetchers];
|
|
4559
4524
|
}
|
|
4560
4525
|
|
|
4526
|
+
function shouldLoadRouteOnHydration(
|
|
4527
|
+
route: AgnosticDataRouteObject,
|
|
4528
|
+
loaderData: RouteData | null | undefined,
|
|
4529
|
+
errors: RouteData | null | undefined
|
|
4530
|
+
) {
|
|
4531
|
+
// We dunno if we have a loader - gotta find out!
|
|
4532
|
+
if (route.lazy) {
|
|
4533
|
+
return true;
|
|
4534
|
+
}
|
|
4535
|
+
|
|
4536
|
+
// No loader, nothing to initialize
|
|
4537
|
+
if (!route.loader) {
|
|
4538
|
+
return false;
|
|
4539
|
+
}
|
|
4540
|
+
|
|
4541
|
+
let hasData = loaderData != null && loaderData[route.id] !== undefined;
|
|
4542
|
+
let hasError = errors != null && errors[route.id] !== undefined;
|
|
4543
|
+
|
|
4544
|
+
// Don't run if we error'd during SSR
|
|
4545
|
+
if (!hasData && hasError) {
|
|
4546
|
+
return false;
|
|
4547
|
+
}
|
|
4548
|
+
|
|
4549
|
+
// Explicitly opting-in to running on hydration
|
|
4550
|
+
if (typeof route.loader === "function" && route.loader.hydrate === true) {
|
|
4551
|
+
return true;
|
|
4552
|
+
}
|
|
4553
|
+
|
|
4554
|
+
// Otherwise, run if we're not yet initialized with anything
|
|
4555
|
+
return !hasData && !hasError;
|
|
4556
|
+
}
|
|
4557
|
+
|
|
4561
4558
|
function isNewLoader(
|
|
4562
4559
|
currentLoaderData: RouteData,
|
|
4563
4560
|
currentMatch: AgnosticDataRouteMatch,
|
|
@@ -4607,53 +4604,6 @@ function shouldRevalidateLoader(
|
|
|
4607
4604
|
return arg.defaultShouldRevalidate;
|
|
4608
4605
|
}
|
|
4609
4606
|
|
|
4610
|
-
/**
|
|
4611
|
-
* Idempotent utility to execute patchRoutesOnNavigation() to lazily load route
|
|
4612
|
-
* definitions and update the routes/routeManifest
|
|
4613
|
-
*/
|
|
4614
|
-
async function loadLazyRouteChildren(
|
|
4615
|
-
patchRoutesOnNavigationImpl: AgnosticPatchRoutesOnNavigationFunction,
|
|
4616
|
-
path: string,
|
|
4617
|
-
matches: AgnosticDataRouteMatch[],
|
|
4618
|
-
routes: AgnosticDataRouteObject[],
|
|
4619
|
-
manifest: RouteManifest,
|
|
4620
|
-
mapRouteProperties: MapRoutePropertiesFunction,
|
|
4621
|
-
pendingRouteChildren: Map<
|
|
4622
|
-
string,
|
|
4623
|
-
ReturnType<typeof patchRoutesOnNavigationImpl>
|
|
4624
|
-
>,
|
|
4625
|
-
signal: AbortSignal
|
|
4626
|
-
) {
|
|
4627
|
-
let key = [path, ...matches.map((m) => m.route.id)].join("-");
|
|
4628
|
-
try {
|
|
4629
|
-
let pending = pendingRouteChildren.get(key);
|
|
4630
|
-
if (!pending) {
|
|
4631
|
-
pending = patchRoutesOnNavigationImpl({
|
|
4632
|
-
path,
|
|
4633
|
-
matches,
|
|
4634
|
-
patch: (routeId, children) => {
|
|
4635
|
-
if (!signal.aborted) {
|
|
4636
|
-
patchRoutesImpl(
|
|
4637
|
-
routeId,
|
|
4638
|
-
children,
|
|
4639
|
-
routes,
|
|
4640
|
-
manifest,
|
|
4641
|
-
mapRouteProperties
|
|
4642
|
-
);
|
|
4643
|
-
}
|
|
4644
|
-
},
|
|
4645
|
-
});
|
|
4646
|
-
pendingRouteChildren.set(key, pending);
|
|
4647
|
-
}
|
|
4648
|
-
|
|
4649
|
-
if (pending && isPromise<AgnosticRouteObject[]>(pending)) {
|
|
4650
|
-
await pending;
|
|
4651
|
-
}
|
|
4652
|
-
} finally {
|
|
4653
|
-
pendingRouteChildren.delete(key);
|
|
4654
|
-
}
|
|
4655
|
-
}
|
|
4656
|
-
|
|
4657
4607
|
function patchRoutesImpl(
|
|
4658
4608
|
routeId: string | null,
|
|
4659
4609
|
children: AgnosticRouteObject[],
|
|
@@ -4661,32 +4611,79 @@ function patchRoutesImpl(
|
|
|
4661
4611
|
manifest: RouteManifest,
|
|
4662
4612
|
mapRouteProperties: MapRoutePropertiesFunction
|
|
4663
4613
|
) {
|
|
4614
|
+
let childrenToPatch: AgnosticDataRouteObject[];
|
|
4664
4615
|
if (routeId) {
|
|
4665
4616
|
let route = manifest[routeId];
|
|
4666
4617
|
invariant(
|
|
4667
4618
|
route,
|
|
4668
4619
|
`No route found to patch children into: routeId = ${routeId}`
|
|
4669
4620
|
);
|
|
4670
|
-
|
|
4671
|
-
children
|
|
4672
|
-
mapRouteProperties,
|
|
4673
|
-
[routeId, "patch", String(route.children?.length || "0")],
|
|
4674
|
-
manifest
|
|
4675
|
-
);
|
|
4676
|
-
if (route.children) {
|
|
4677
|
-
route.children.push(...dataChildren);
|
|
4678
|
-
} else {
|
|
4679
|
-
route.children = dataChildren;
|
|
4621
|
+
if (!route.children) {
|
|
4622
|
+
route.children = [];
|
|
4680
4623
|
}
|
|
4624
|
+
childrenToPatch = route.children;
|
|
4681
4625
|
} else {
|
|
4682
|
-
|
|
4683
|
-
children,
|
|
4684
|
-
mapRouteProperties,
|
|
4685
|
-
["patch", String(routesToUse.length || "0")],
|
|
4686
|
-
manifest
|
|
4687
|
-
);
|
|
4688
|
-
routesToUse.push(...dataChildren);
|
|
4626
|
+
childrenToPatch = routesToUse;
|
|
4689
4627
|
}
|
|
4628
|
+
|
|
4629
|
+
// Don't patch in routes we already know about so that `patch` is idempotent
|
|
4630
|
+
// to simplify user-land code. This is useful because we re-call the
|
|
4631
|
+
// `patchRoutesOnNavigation` function for matched routes with params.
|
|
4632
|
+
let uniqueChildren = children.filter(
|
|
4633
|
+
(newRoute) =>
|
|
4634
|
+
!childrenToPatch.some((existingRoute) =>
|
|
4635
|
+
isSameRoute(newRoute, existingRoute)
|
|
4636
|
+
)
|
|
4637
|
+
);
|
|
4638
|
+
|
|
4639
|
+
let newRoutes = convertRoutesToDataRoutes(
|
|
4640
|
+
uniqueChildren,
|
|
4641
|
+
mapRouteProperties,
|
|
4642
|
+
[routeId || "_", "patch", String(childrenToPatch?.length || "0")],
|
|
4643
|
+
manifest
|
|
4644
|
+
);
|
|
4645
|
+
|
|
4646
|
+
childrenToPatch.push(...newRoutes);
|
|
4647
|
+
}
|
|
4648
|
+
|
|
4649
|
+
function isSameRoute(
|
|
4650
|
+
newRoute: AgnosticRouteObject,
|
|
4651
|
+
existingRoute: AgnosticRouteObject
|
|
4652
|
+
): boolean {
|
|
4653
|
+
// Most optimal check is by id
|
|
4654
|
+
if (
|
|
4655
|
+
"id" in newRoute &&
|
|
4656
|
+
"id" in existingRoute &&
|
|
4657
|
+
newRoute.id === existingRoute.id
|
|
4658
|
+
) {
|
|
4659
|
+
return true;
|
|
4660
|
+
}
|
|
4661
|
+
|
|
4662
|
+
// Second is by pathing differences
|
|
4663
|
+
if (
|
|
4664
|
+
!(
|
|
4665
|
+
newRoute.index === existingRoute.index &&
|
|
4666
|
+
newRoute.path === existingRoute.path &&
|
|
4667
|
+
newRoute.caseSensitive === existingRoute.caseSensitive
|
|
4668
|
+
)
|
|
4669
|
+
) {
|
|
4670
|
+
return false;
|
|
4671
|
+
}
|
|
4672
|
+
|
|
4673
|
+
// Pathless layout routes are trickier since we need to check children.
|
|
4674
|
+
// If they have no children then they're the same as far as we can tell
|
|
4675
|
+
if (
|
|
4676
|
+
(!newRoute.children || newRoute.children.length === 0) &&
|
|
4677
|
+
(!existingRoute.children || existingRoute.children.length === 0)
|
|
4678
|
+
) {
|
|
4679
|
+
return true;
|
|
4680
|
+
}
|
|
4681
|
+
|
|
4682
|
+
// Otherwise, we look to see if every child in the new route is already
|
|
4683
|
+
// represented in the existing route's children
|
|
4684
|
+
return newRoute.children!.every((aChild, i) =>
|
|
4685
|
+
existingRoute.children?.some((bChild) => isSameRoute(aChild, bChild))
|
|
4686
|
+
);
|
|
4690
4687
|
}
|
|
4691
4688
|
|
|
4692
4689
|
/**
|
|
@@ -5038,7 +5035,7 @@ async function convertDataStrategyResultToDataResult(
|
|
|
5038
5035
|
};
|
|
5039
5036
|
}
|
|
5040
5037
|
|
|
5041
|
-
// Convert thrown
|
|
5038
|
+
// Convert thrown data() to ErrorResponse instances
|
|
5042
5039
|
result = new ErrorResponseImpl(
|
|
5043
5040
|
result.init?.status || 500,
|
|
5044
5041
|
undefined,
|
|
@@ -5310,7 +5307,6 @@ function processRouteLoaderData(
|
|
|
5310
5307
|
function processLoaderData(
|
|
5311
5308
|
state: RouterState,
|
|
5312
5309
|
matches: AgnosticDataRouteMatch[],
|
|
5313
|
-
matchesToLoad: AgnosticDataRouteMatch[],
|
|
5314
5310
|
results: Record<string, DataResult>,
|
|
5315
5311
|
pendingActionResult: PendingActionResult | undefined,
|
|
5316
5312
|
revalidatingFetchers: RevalidatingFetcher[],
|
|
@@ -5466,7 +5462,7 @@ function getInternalRouterError(
|
|
|
5466
5462
|
pathname?: string;
|
|
5467
5463
|
routeId?: string;
|
|
5468
5464
|
method?: string;
|
|
5469
|
-
type?: "defer-action" | "invalid-body"
|
|
5465
|
+
type?: "defer-action" | "invalid-body";
|
|
5470
5466
|
message?: string;
|
|
5471
5467
|
} = {}
|
|
5472
5468
|
) {
|
|
@@ -5475,11 +5471,7 @@ function getInternalRouterError(
|
|
|
5475
5471
|
|
|
5476
5472
|
if (status === 400) {
|
|
5477
5473
|
statusText = "Bad Request";
|
|
5478
|
-
if (
|
|
5479
|
-
errorMessage =
|
|
5480
|
-
`Unable to match URL "${pathname}" - the \`unstable_patchRoutesOnNavigation()\` ` +
|
|
5481
|
-
`function threw the following error:\n${message}`;
|
|
5482
|
-
} else if (method && pathname && routeId) {
|
|
5474
|
+
if (method && pathname && routeId) {
|
|
5483
5475
|
errorMessage =
|
|
5484
5476
|
`You made a ${method} request to "${pathname}" but ` +
|
|
5485
5477
|
`did not provide a \`loader\` for route "${routeId}", ` +
|