@remix-run/router 1.19.0 → 1.19.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 +10 -0
- package/dist/index.d.ts +1 -1
- package/dist/router.cjs.js +45 -54
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.d.ts +2 -2
- package/dist/router.js +44 -53
- package/dist/router.js.map +1 -1
- package/dist/router.umd.js +45 -54
- 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 +1 -1
- package/index.ts +1 -1
- package/package.json +1 -1
- package/router.ts +50 -57
- package/utils.ts +1 -1
package/dist/utils.d.ts
CHANGED
|
@@ -199,7 +199,7 @@ export interface DataStrategyFunctionArgs<Context = any> extends DataFunctionArg
|
|
|
199
199
|
export interface DataStrategyFunction {
|
|
200
200
|
(args: DataStrategyFunctionArgs): Promise<HandlerResult[]>;
|
|
201
201
|
}
|
|
202
|
-
export interface
|
|
202
|
+
export interface AgnosticPatchRoutesOnNavigationFunction<M extends AgnosticRouteMatch = AgnosticRouteMatch> {
|
|
203
203
|
(opts: {
|
|
204
204
|
path: string;
|
|
205
205
|
matches: M[];
|
package/index.ts
CHANGED
|
@@ -23,7 +23,7 @@ export type {
|
|
|
23
23
|
LoaderFunctionArgs,
|
|
24
24
|
ParamParseKey,
|
|
25
25
|
Params,
|
|
26
|
-
|
|
26
|
+
AgnosticPatchRoutesOnNavigationFunction as unstable_AgnosticPatchRoutesOnNavigationFunction,
|
|
27
27
|
PathMatch,
|
|
28
28
|
PathParam,
|
|
29
29
|
PathPattern,
|
package/package.json
CHANGED
package/router.ts
CHANGED
|
@@ -35,7 +35,7 @@ import type {
|
|
|
35
35
|
UIMatch,
|
|
36
36
|
V7_FormMethod,
|
|
37
37
|
V7_MutationFormMethod,
|
|
38
|
-
|
|
38
|
+
AgnosticPatchRoutesOnNavigationFunction,
|
|
39
39
|
DataWithResponseInit,
|
|
40
40
|
} from "./utils";
|
|
41
41
|
import {
|
|
@@ -391,7 +391,7 @@ export interface RouterInit {
|
|
|
391
391
|
future?: Partial<FutureConfig>;
|
|
392
392
|
hydrationData?: HydrationState;
|
|
393
393
|
window?: Window;
|
|
394
|
-
|
|
394
|
+
unstable_patchRoutesOnNavigation?: AgnosticPatchRoutesOnNavigationFunction;
|
|
395
395
|
unstable_dataStrategy?: DataStrategyFunction;
|
|
396
396
|
}
|
|
397
397
|
|
|
@@ -798,7 +798,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
798
798
|
let inFlightDataRoutes: AgnosticDataRouteObject[] | undefined;
|
|
799
799
|
let basename = init.basename || "/";
|
|
800
800
|
let dataStrategyImpl = init.unstable_dataStrategy || defaultDataStrategy;
|
|
801
|
-
let
|
|
801
|
+
let patchRoutesOnNavigationImpl = init.unstable_patchRoutesOnNavigation;
|
|
802
802
|
|
|
803
803
|
// Config driven behavior flags
|
|
804
804
|
let future: FutureConfig = {
|
|
@@ -814,6 +814,10 @@ 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>();
|
|
817
821
|
// Externally-provided object to hold scroll restoration locations during routing
|
|
818
822
|
let savedScrollPositions: Record<string, number> | null = null;
|
|
819
823
|
// Externally-provided function to get scroll restoration keys
|
|
@@ -831,7 +835,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
831
835
|
let initialMatches = matchRoutes(dataRoutes, init.history.location, basename);
|
|
832
836
|
let initialErrors: RouteData | null = null;
|
|
833
837
|
|
|
834
|
-
if (initialMatches == null && !
|
|
838
|
+
if (initialMatches == null && !patchRoutesOnNavigationImpl) {
|
|
835
839
|
// If we do not match a user-provided-route, fall back to the root
|
|
836
840
|
// to allow the error boundary to take over
|
|
837
841
|
let error = getInternalRouterError(404, {
|
|
@@ -842,7 +846,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
842
846
|
initialErrors = { [route.id]: error };
|
|
843
847
|
}
|
|
844
848
|
|
|
845
|
-
// In SPA apps, if the user provided a
|
|
849
|
+
// In SPA apps, if the user provided a patchRoutesOnNavigation implementation and
|
|
846
850
|
// our initial match is a splat route, clear them out so we run through lazy
|
|
847
851
|
// discovery on hydration in case there's a more accurate lazy route match.
|
|
848
852
|
// In SSR apps (with `hydrationData`), we expect that the server will send
|
|
@@ -865,7 +869,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
865
869
|
initialMatches = [];
|
|
866
870
|
|
|
867
871
|
// If partial hydration and fog of war is enabled, we will be running
|
|
868
|
-
// `
|
|
872
|
+
// `patchRoutesOnNavigation` during hydration so include any partial matches as
|
|
869
873
|
// the initial matches so we can properly render `HydrateFallback`'s
|
|
870
874
|
if (future.v7_partialHydration) {
|
|
871
875
|
let fogOfWar = checkFogOfWar(
|
|
@@ -1020,11 +1024,11 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1020
1024
|
// we don't need to update UI state if they change
|
|
1021
1025
|
let blockerFunctions = new Map<string, BlockerFunction>();
|
|
1022
1026
|
|
|
1023
|
-
// Map of pending
|
|
1027
|
+
// Map of pending patchRoutesOnNavigation() promises (keyed by path/matches) so
|
|
1024
1028
|
// that we only kick them off once for a given combo
|
|
1025
1029
|
let pendingPatchRoutes = new Map<
|
|
1026
1030
|
string,
|
|
1027
|
-
ReturnType<
|
|
1031
|
+
ReturnType<AgnosticPatchRoutesOnNavigationFunction>
|
|
1028
1032
|
>();
|
|
1029
1033
|
|
|
1030
1034
|
// Flag to ignore the next history update, so we can revert the URL change on
|
|
@@ -3182,7 +3186,14 @@ export function createRouter(init: RouterInit): Router {
|
|
|
3182
3186
|
routesToUse: AgnosticDataRouteObject[],
|
|
3183
3187
|
pathname: string
|
|
3184
3188
|
): { active: boolean; matches: AgnosticDataRouteMatch[] | null } {
|
|
3185
|
-
if (
|
|
3189
|
+
if (patchRoutesOnNavigationImpl) {
|
|
3190
|
+
// Don't bother re-calling patchRouteOnMiss for a path we've already
|
|
3191
|
+
// processed. the last execution would have patched the route tree
|
|
3192
|
+
// accordingly so `matches` here are already accurate.
|
|
3193
|
+
if (discoveredRoutes.has(pathname)) {
|
|
3194
|
+
return { active: false, matches };
|
|
3195
|
+
}
|
|
3196
|
+
|
|
3186
3197
|
if (!matches) {
|
|
3187
3198
|
let fogMatches = matchRoutesImpl<AgnosticDataRouteObject>(
|
|
3188
3199
|
routesToUse,
|
|
@@ -3193,14 +3204,10 @@ export function createRouter(init: RouterInit): Router {
|
|
|
3193
3204
|
|
|
3194
3205
|
return { active: true, matches: fogMatches || [] };
|
|
3195
3206
|
} else {
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
) {
|
|
3201
|
-
// If we matched a splat, it might only be because we haven't yet fetched
|
|
3202
|
-
// the children that would match with a higher score, so let's fetch
|
|
3203
|
-
// around and find out
|
|
3207
|
+
if (Object.keys(matches[0].params).length > 0) {
|
|
3208
|
+
// If we matched a dynamic param or a splat, it might only be because
|
|
3209
|
+
// we haven't yet discovered other routes that would match with a
|
|
3210
|
+
// higher score. Call patchRoutesOnNavigation just to be sure
|
|
3204
3211
|
let partialMatches = matchRoutesImpl<AgnosticDataRouteObject>(
|
|
3205
3212
|
routesToUse,
|
|
3206
3213
|
pathname,
|
|
@@ -3236,16 +3243,12 @@ export function createRouter(init: RouterInit): Router {
|
|
|
3236
3243
|
signal: AbortSignal
|
|
3237
3244
|
): Promise<DiscoverRoutesResult> {
|
|
3238
3245
|
let partialMatches: AgnosticDataRouteMatch[] | null = matches;
|
|
3239
|
-
let route =
|
|
3240
|
-
partialMatches.length > 0
|
|
3241
|
-
? partialMatches[partialMatches.length - 1].route
|
|
3242
|
-
: null;
|
|
3243
3246
|
while (true) {
|
|
3244
3247
|
let isNonHMR = inFlightDataRoutes == null;
|
|
3245
3248
|
let routesToUse = inFlightDataRoutes || dataRoutes;
|
|
3246
3249
|
try {
|
|
3247
3250
|
await loadLazyRouteChildren(
|
|
3248
|
-
|
|
3251
|
+
patchRoutesOnNavigationImpl!,
|
|
3249
3252
|
pathname,
|
|
3250
3253
|
partialMatches,
|
|
3251
3254
|
routesToUse,
|
|
@@ -3273,26 +3276,9 @@ export function createRouter(init: RouterInit): Router {
|
|
|
3273
3276
|
}
|
|
3274
3277
|
|
|
3275
3278
|
let newMatches = matchRoutes(routesToUse, pathname, basename);
|
|
3276
|
-
let matchedSplat = false;
|
|
3277
3279
|
if (newMatches) {
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
if (leafRoute.index) {
|
|
3281
|
-
// If we found an index route, we can stop
|
|
3282
|
-
return { type: "success", matches: newMatches };
|
|
3283
|
-
}
|
|
3284
|
-
|
|
3285
|
-
if (leafRoute.path && leafRoute.path.length > 0) {
|
|
3286
|
-
if (leafRoute.path === "*") {
|
|
3287
|
-
// If we found a splat route, we can't be sure there's not a
|
|
3288
|
-
// higher-scoring route down some partial matches trail so we need
|
|
3289
|
-
// to check that out
|
|
3290
|
-
matchedSplat = true;
|
|
3291
|
-
} else {
|
|
3292
|
-
// If we found a non-splat route, we can stop
|
|
3293
|
-
return { type: "success", matches: newMatches };
|
|
3294
|
-
}
|
|
3295
|
-
}
|
|
3280
|
+
addToFifoQueue(pathname, discoveredRoutes);
|
|
3281
|
+
return { type: "success", matches: newMatches };
|
|
3296
3282
|
}
|
|
3297
3283
|
|
|
3298
3284
|
let newPartialMatches = matchRoutesImpl<AgnosticDataRouteObject>(
|
|
@@ -3302,26 +3288,30 @@ export function createRouter(init: RouterInit): Router {
|
|
|
3302
3288
|
true
|
|
3303
3289
|
);
|
|
3304
3290
|
|
|
3305
|
-
//
|
|
3306
|
-
// legit splat match above, or it's a 404. Also avoid loops if the
|
|
3307
|
-
// second pass results in the same partial matches
|
|
3291
|
+
// Avoid loops if the second pass results in the same partial matches
|
|
3308
3292
|
if (
|
|
3309
3293
|
!newPartialMatches ||
|
|
3310
|
-
partialMatches.
|
|
3311
|
-
|
|
3294
|
+
(partialMatches.length === newPartialMatches.length &&
|
|
3295
|
+
partialMatches.every(
|
|
3296
|
+
(m, i) => m.route.id === newPartialMatches![i].route.id
|
|
3297
|
+
))
|
|
3312
3298
|
) {
|
|
3313
|
-
|
|
3299
|
+
addToFifoQueue(pathname, discoveredRoutes);
|
|
3300
|
+
return { type: "success", matches: null };
|
|
3314
3301
|
}
|
|
3315
3302
|
|
|
3316
3303
|
partialMatches = newPartialMatches;
|
|
3317
|
-
route = partialMatches[partialMatches.length - 1].route;
|
|
3318
|
-
if (route.path === "*") {
|
|
3319
|
-
// The splat is still our most accurate partial, so run with it
|
|
3320
|
-
return { type: "success", matches: partialMatches };
|
|
3321
|
-
}
|
|
3322
3304
|
}
|
|
3323
3305
|
}
|
|
3324
3306
|
|
|
3307
|
+
function addToFifoQueue(path: string, queue: Set<string>) {
|
|
3308
|
+
if (queue.size >= discoveredRoutesMaxSize) {
|
|
3309
|
+
let first = queue.values().next().value;
|
|
3310
|
+
queue.delete(first);
|
|
3311
|
+
}
|
|
3312
|
+
queue.add(path);
|
|
3313
|
+
}
|
|
3314
|
+
|
|
3325
3315
|
function _internalSetRoutes(newRoutes: AgnosticDataRouteObject[]) {
|
|
3326
3316
|
manifest = {};
|
|
3327
3317
|
inFlightDataRoutes = convertRoutesToDataRoutes(
|
|
@@ -4554,24 +4544,27 @@ function shouldRevalidateLoader(
|
|
|
4554
4544
|
}
|
|
4555
4545
|
|
|
4556
4546
|
/**
|
|
4557
|
-
* Idempotent utility to execute
|
|
4547
|
+
* Idempotent utility to execute patchRoutesOnNavigation() to lazily load route
|
|
4558
4548
|
* definitions and update the routes/routeManifest
|
|
4559
4549
|
*/
|
|
4560
4550
|
async function loadLazyRouteChildren(
|
|
4561
|
-
|
|
4551
|
+
patchRoutesOnNavigationImpl: AgnosticPatchRoutesOnNavigationFunction,
|
|
4562
4552
|
path: string,
|
|
4563
4553
|
matches: AgnosticDataRouteMatch[],
|
|
4564
4554
|
routes: AgnosticDataRouteObject[],
|
|
4565
4555
|
manifest: RouteManifest,
|
|
4566
4556
|
mapRouteProperties: MapRoutePropertiesFunction,
|
|
4567
|
-
pendingRouteChildren: Map<
|
|
4557
|
+
pendingRouteChildren: Map<
|
|
4558
|
+
string,
|
|
4559
|
+
ReturnType<typeof patchRoutesOnNavigationImpl>
|
|
4560
|
+
>,
|
|
4568
4561
|
signal: AbortSignal
|
|
4569
4562
|
) {
|
|
4570
4563
|
let key = [path, ...matches.map((m) => m.route.id)].join("-");
|
|
4571
4564
|
try {
|
|
4572
4565
|
let pending = pendingRouteChildren.get(key);
|
|
4573
4566
|
if (!pending) {
|
|
4574
|
-
pending =
|
|
4567
|
+
pending = patchRoutesOnNavigationImpl({
|
|
4575
4568
|
path,
|
|
4576
4569
|
matches,
|
|
4577
4570
|
patch: (routeId, children) => {
|
|
@@ -5410,7 +5403,7 @@ function getInternalRouterError(
|
|
|
5410
5403
|
statusText = "Bad Request";
|
|
5411
5404
|
if (type === "route-discovery") {
|
|
5412
5405
|
errorMessage =
|
|
5413
|
-
`Unable to match URL "${pathname}" - the \`
|
|
5406
|
+
`Unable to match URL "${pathname}" - the \`unstable_patchRoutesOnNavigation()\` ` +
|
|
5414
5407
|
`function threw the following error:\n${message}`;
|
|
5415
5408
|
} else if (method && pathname && routeId) {
|
|
5416
5409
|
errorMessage =
|
package/utils.ts
CHANGED
|
@@ -254,7 +254,7 @@ export interface DataStrategyFunction {
|
|
|
254
254
|
(args: DataStrategyFunctionArgs): Promise<HandlerResult[]>;
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
-
export interface
|
|
257
|
+
export interface AgnosticPatchRoutesOnNavigationFunction<
|
|
258
258
|
M extends AgnosticRouteMatch = AgnosticRouteMatch
|
|
259
259
|
> {
|
|
260
260
|
(opts: {
|