@tanstack/router-core 1.145.11 → 1.146.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/dist/cjs/new-process-route-tree.cjs +137 -33
- package/dist/cjs/new-process-route-tree.cjs.map +1 -1
- package/dist/cjs/new-process-route-tree.d.cts +50 -6
- package/dist/cjs/route.cjs.map +1 -1
- package/dist/cjs/route.d.cts +39 -0
- package/dist/cjs/router.cjs +37 -25
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +5 -0
- package/dist/esm/new-process-route-tree.d.ts +50 -6
- package/dist/esm/new-process-route-tree.js +137 -33
- package/dist/esm/new-process-route-tree.js.map +1 -1
- package/dist/esm/route.d.ts +39 -0
- package/dist/esm/route.js.map +1 -1
- package/dist/esm/router.d.ts +5 -0
- package/dist/esm/router.js +37 -25
- package/dist/esm/router.js.map +1 -1
- package/package.json +1 -1
- package/src/new-process-route-tree.ts +250 -49
- package/src/route.ts +39 -2
- package/src/router.ts +54 -27
package/dist/cjs/router.d.cts
CHANGED
|
@@ -460,8 +460,12 @@ export type InvalidateFn<TRouter extends AnyRouter> = (opts?: {
|
|
|
460
460
|
export type ParseLocationFn<TRouteTree extends AnyRoute> = (locationToParse: HistoryLocation, previousLocation?: ParsedLocation<FullSearchSchema<TRouteTree>>) => ParsedLocation<FullSearchSchema<TRouteTree>>;
|
|
461
461
|
export type GetMatchRoutesFn = (pathname: string) => {
|
|
462
462
|
matchedRoutes: ReadonlyArray<AnyRoute>;
|
|
463
|
+
/** exhaustive params, still in their string form */
|
|
463
464
|
routeParams: Record<string, string>;
|
|
465
|
+
/** partial params, parsed from routeParams during matching */
|
|
466
|
+
parsedParams: Record<string, unknown> | undefined;
|
|
464
467
|
foundRoute: AnyRoute | undefined;
|
|
468
|
+
parseError?: unknown;
|
|
465
469
|
};
|
|
466
470
|
export type EmitFn = (routerEvent: RouterEvent) => void;
|
|
467
471
|
export type LoadFn = (opts?: {
|
|
@@ -693,5 +697,6 @@ export declare function getMatchedRoutes<TRouteLike extends RouteLike>({ pathnam
|
|
|
693
697
|
matchedRoutes: readonly TRouteLike[];
|
|
694
698
|
routeParams: Record<string, string>;
|
|
695
699
|
foundRoute: TRouteLike | undefined;
|
|
700
|
+
parsedParams: Record<string, unknown> | undefined;
|
|
696
701
|
};
|
|
697
702
|
export {};
|
|
@@ -4,6 +4,7 @@ export declare const SEGMENT_TYPE_PARAM = 1;
|
|
|
4
4
|
export declare const SEGMENT_TYPE_WILDCARD = 2;
|
|
5
5
|
export declare const SEGMENT_TYPE_OPTIONAL_PARAM = 3;
|
|
6
6
|
declare const SEGMENT_TYPE_INDEX = 4;
|
|
7
|
+
declare const SEGMENT_TYPE_PATHLESS = 5;
|
|
7
8
|
/**
|
|
8
9
|
* All the kinds of segments that can be present in a route path.
|
|
9
10
|
*/
|
|
@@ -11,7 +12,7 @@ export type SegmentKind = typeof SEGMENT_TYPE_PATHNAME | typeof SEGMENT_TYPE_PAR
|
|
|
11
12
|
/**
|
|
12
13
|
* All the kinds of segments that can be present in the segment tree.
|
|
13
14
|
*/
|
|
14
|
-
type ExtendedSegmentKind = SegmentKind | typeof SEGMENT_TYPE_INDEX;
|
|
15
|
+
type ExtendedSegmentKind = SegmentKind | typeof SEGMENT_TYPE_INDEX | typeof SEGMENT_TYPE_PATHLESS;
|
|
15
16
|
type ParsedSegment = Uint16Array & {
|
|
16
17
|
/** segment type (0 = pathname, 1 = param, 2 = wildcard, 3 = optional param) */
|
|
17
18
|
0: SegmentKind;
|
|
@@ -50,7 +51,7 @@ start: number,
|
|
|
50
51
|
/** A Uint16Array (length: 6) to populate with the parsed segment data. */
|
|
51
52
|
output?: Uint16Array): ParsedSegment;
|
|
52
53
|
type StaticSegmentNode<T extends RouteLike> = SegmentNode<T> & {
|
|
53
|
-
kind: typeof SEGMENT_TYPE_PATHNAME | typeof SEGMENT_TYPE_INDEX;
|
|
54
|
+
kind: typeof SEGMENT_TYPE_PATHNAME | typeof SEGMENT_TYPE_PATHLESS | typeof SEGMENT_TYPE_INDEX;
|
|
54
55
|
};
|
|
55
56
|
type DynamicSegmentNode<T extends RouteLike> = SegmentNode<T> & {
|
|
56
57
|
kind: typeof SEGMENT_TYPE_PARAM | typeof SEGMENT_TYPE_WILDCARD | typeof SEGMENT_TYPE_OPTIONAL_PARAM;
|
|
@@ -61,6 +62,7 @@ type DynamicSegmentNode<T extends RouteLike> = SegmentNode<T> & {
|
|
|
61
62
|
type AnySegmentNode<T extends RouteLike> = StaticSegmentNode<T> | DynamicSegmentNode<T>;
|
|
62
63
|
type SegmentNode<T extends RouteLike> = {
|
|
63
64
|
kind: ExtendedSegmentKind;
|
|
65
|
+
pathless: Array<StaticSegmentNode<T>> | null;
|
|
64
66
|
/** Exact index segment (highest priority) */
|
|
65
67
|
index: StaticSegmentNode<T> | null;
|
|
66
68
|
/** Static segments (2nd priority) */
|
|
@@ -79,14 +81,28 @@ type SegmentNode<T extends RouteLike> = {
|
|
|
79
81
|
fullPath: string;
|
|
80
82
|
parent: AnySegmentNode<T> | null;
|
|
81
83
|
depth: number;
|
|
84
|
+
/** route.options.params.parse function, set on the last node of the route */
|
|
85
|
+
parse: null | ((params: Record<string, string>) => any);
|
|
86
|
+
/** options.skipRouteOnParseError.params ?? false */
|
|
87
|
+
skipOnParamError: boolean;
|
|
88
|
+
/** options.skipRouteOnParseError.priority ?? 0 */
|
|
89
|
+
parsingPriority: number;
|
|
82
90
|
};
|
|
83
91
|
type RouteLike = {
|
|
92
|
+
id?: string;
|
|
84
93
|
path?: string;
|
|
85
94
|
children?: Array<RouteLike>;
|
|
86
95
|
parentRoute?: RouteLike;
|
|
87
96
|
isRoot?: boolean;
|
|
88
97
|
options?: {
|
|
98
|
+
skipRouteOnParseError?: {
|
|
99
|
+
params?: boolean;
|
|
100
|
+
priority?: number;
|
|
101
|
+
};
|
|
89
102
|
caseSensitive?: boolean;
|
|
103
|
+
params?: {
|
|
104
|
+
parse?: (params: Record<string, string>) => any;
|
|
105
|
+
};
|
|
90
106
|
};
|
|
91
107
|
} & ({
|
|
92
108
|
fullPath: string;
|
|
@@ -127,7 +143,16 @@ path: string,
|
|
|
127
143
|
/** The `processedTree` returned by the initial `processRouteTree` call. */
|
|
128
144
|
processedTree: ProcessedTree<any, T, any>): {
|
|
129
145
|
route: T;
|
|
130
|
-
|
|
146
|
+
/**
|
|
147
|
+
* The raw (unparsed) params extracted from the path.
|
|
148
|
+
* This will be the exhaustive list of all params defined in the route's path.
|
|
149
|
+
*/
|
|
150
|
+
rawParams: Record<string, string>;
|
|
151
|
+
/**
|
|
152
|
+
* The accumlulated parsed params of each route in the branch that had `skipRouteOnParseError` enabled.
|
|
153
|
+
* Will not contain all params defined in the route's path. Those w/ a `params.parse` but no `skipRouteOnParseError` will need to be parsed separately.
|
|
154
|
+
*/
|
|
155
|
+
parsedParams?: Record<string, unknown>;
|
|
131
156
|
} | null;
|
|
132
157
|
/**
|
|
133
158
|
* @deprecated keep until v2 so that `router.matchRoute` can keep not caring about the actual route tree
|
|
@@ -138,13 +163,23 @@ export declare function findSingleMatch(from: string, caseSensitive: boolean, fu
|
|
|
138
163
|
route: {
|
|
139
164
|
from: string;
|
|
140
165
|
};
|
|
141
|
-
|
|
166
|
+
/**
|
|
167
|
+
* The raw (unparsed) params extracted from the path.
|
|
168
|
+
* This will be the exhaustive list of all params defined in the route's path.
|
|
169
|
+
*/
|
|
170
|
+
rawParams: Record<string, string>;
|
|
171
|
+
/**
|
|
172
|
+
* The accumlulated parsed params of each route in the branch that had `skipRouteOnParseError` enabled.
|
|
173
|
+
* Will not contain all params defined in the route's path. Those w/ a `params.parse` but no `skipRouteOnParseError` will need to be parsed separately.
|
|
174
|
+
*/
|
|
175
|
+
parsedParams?: Record<string, unknown>;
|
|
142
176
|
} | null;
|
|
143
177
|
type RouteMatch<T extends Extract<RouteLike, {
|
|
144
178
|
fullPath: string;
|
|
145
179
|
}>> = {
|
|
146
180
|
route: T;
|
|
147
|
-
|
|
181
|
+
rawParams: Record<string, string>;
|
|
182
|
+
parsedParams?: Record<string, unknown>;
|
|
148
183
|
branch: ReadonlyArray<T>;
|
|
149
184
|
};
|
|
150
185
|
export declare function findRouteMatch<T extends Extract<RouteLike, {
|
|
@@ -182,6 +217,15 @@ initRoute?: (route: TRouteLike, index: number) => void): {
|
|
|
182
217
|
};
|
|
183
218
|
declare function findMatch<T extends RouteLike>(path: string, segmentTree: AnySegmentNode<T>, fuzzy?: boolean): {
|
|
184
219
|
route: T;
|
|
185
|
-
|
|
220
|
+
/**
|
|
221
|
+
* The raw (unparsed) params extracted from the path.
|
|
222
|
+
* This will be the exhaustive list of all params defined in the route's path.
|
|
223
|
+
*/
|
|
224
|
+
rawParams: Record<string, string>;
|
|
225
|
+
/**
|
|
226
|
+
* The accumlulated parsed params of each route in the branch that had `skipRouteOnParseError` enabled.
|
|
227
|
+
* Will not contain all params defined in the route's path. Those w/ a `params.parse` but no `skipRouteOnParseError` will need to be parsed separately.
|
|
228
|
+
*/
|
|
229
|
+
parsedParams?: Record<string, unknown>;
|
|
186
230
|
} | null;
|
|
187
231
|
export {};
|
|
@@ -6,6 +6,7 @@ const SEGMENT_TYPE_PARAM = 1;
|
|
|
6
6
|
const SEGMENT_TYPE_WILDCARD = 2;
|
|
7
7
|
const SEGMENT_TYPE_OPTIONAL_PARAM = 3;
|
|
8
8
|
const SEGMENT_TYPE_INDEX = 4;
|
|
9
|
+
const SEGMENT_TYPE_PATHLESS = 5;
|
|
9
10
|
const PARAM_W_CURLY_BRACES_RE = /^([^{]*)\{\$([a-zA-Z_$][a-zA-Z0-9_$]*)\}([^}]*)$/;
|
|
10
11
|
const OPTIONAL_PARAM_W_CURLY_BRACES_RE = /^([^{]*)\{-\$([a-zA-Z_$][a-zA-Z0-9_$]*)\}([^}]*)$/;
|
|
11
12
|
const WILDCARD_W_CURLY_BRACES_RE = /^([^{]*)\{\$\}([^}]*)$/;
|
|
@@ -96,6 +97,7 @@ function parseSegments(defaultCaseSensitive, data, route, start, node, depth, on
|
|
|
96
97
|
const path = route.fullPath ?? route.from;
|
|
97
98
|
const length = path.length;
|
|
98
99
|
const caseSensitive = route.options?.caseSensitive ?? defaultCaseSensitive;
|
|
100
|
+
const skipOnParamError = !!(route.options?.params?.parse && route.options?.skipRouteOnParseError?.params);
|
|
99
101
|
while (cursor < length) {
|
|
100
102
|
const segment = parseSegment(path, cursor, data);
|
|
101
103
|
let nextNode;
|
|
@@ -145,8 +147,8 @@ function parseSegments(defaultCaseSensitive, data, route, start, node, depth, on
|
|
|
145
147
|
const actuallyCaseSensitive = caseSensitive && !!(prefix_raw || suffix_raw);
|
|
146
148
|
const prefix = !prefix_raw ? void 0 : actuallyCaseSensitive ? prefix_raw : prefix_raw.toLowerCase();
|
|
147
149
|
const suffix = !suffix_raw ? void 0 : actuallyCaseSensitive ? suffix_raw : suffix_raw.toLowerCase();
|
|
148
|
-
const existingNode = node.dynamic?.find(
|
|
149
|
-
(s) => s.caseSensitive === actuallyCaseSensitive && s.prefix === prefix && s.suffix === suffix
|
|
150
|
+
const existingNode = !skipOnParamError && node.dynamic?.find(
|
|
151
|
+
(s) => !s.skipOnParamError && s.caseSensitive === actuallyCaseSensitive && s.prefix === prefix && s.suffix === suffix
|
|
150
152
|
);
|
|
151
153
|
if (existingNode) {
|
|
152
154
|
nextNode = existingNode;
|
|
@@ -172,8 +174,8 @@ function parseSegments(defaultCaseSensitive, data, route, start, node, depth, on
|
|
|
172
174
|
const actuallyCaseSensitive = caseSensitive && !!(prefix_raw || suffix_raw);
|
|
173
175
|
const prefix = !prefix_raw ? void 0 : actuallyCaseSensitive ? prefix_raw : prefix_raw.toLowerCase();
|
|
174
176
|
const suffix = !suffix_raw ? void 0 : actuallyCaseSensitive ? suffix_raw : suffix_raw.toLowerCase();
|
|
175
|
-
const existingNode = node.optional?.find(
|
|
176
|
-
(s) => s.caseSensitive === actuallyCaseSensitive && s.prefix === prefix && s.suffix === suffix
|
|
177
|
+
const existingNode = !skipOnParamError && node.optional?.find(
|
|
178
|
+
(s) => !s.skipOnParamError && s.caseSensitive === actuallyCaseSensitive && s.prefix === prefix && s.suffix === suffix
|
|
177
179
|
);
|
|
178
180
|
if (existingNode) {
|
|
179
181
|
nextNode = existingNode;
|
|
@@ -215,6 +217,18 @@ function parseSegments(defaultCaseSensitive, data, route, start, node, depth, on
|
|
|
215
217
|
}
|
|
216
218
|
node = nextNode;
|
|
217
219
|
}
|
|
220
|
+
if (skipOnParamError && route.children && !route.isRoot && route.id && route.id.charCodeAt(route.id.lastIndexOf("/") + 1) === 95) {
|
|
221
|
+
const pathlessNode = createStaticNode(
|
|
222
|
+
route.fullPath ?? route.from
|
|
223
|
+
);
|
|
224
|
+
pathlessNode.kind = SEGMENT_TYPE_PATHLESS;
|
|
225
|
+
pathlessNode.parent = node;
|
|
226
|
+
depth++;
|
|
227
|
+
pathlessNode.depth = depth;
|
|
228
|
+
node.pathless ??= [];
|
|
229
|
+
node.pathless.push(pathlessNode);
|
|
230
|
+
node = pathlessNode;
|
|
231
|
+
}
|
|
218
232
|
const isLeaf = (route.path || !route.children) && !route.isRoot;
|
|
219
233
|
if (isLeaf && path.endsWith("/")) {
|
|
220
234
|
const indexNode = createStaticNode(
|
|
@@ -227,6 +241,9 @@ function parseSegments(defaultCaseSensitive, data, route, start, node, depth, on
|
|
|
227
241
|
node.index = indexNode;
|
|
228
242
|
node = indexNode;
|
|
229
243
|
}
|
|
244
|
+
node.parse = route.options?.params?.parse ?? null;
|
|
245
|
+
node.skipOnParamError = skipOnParamError;
|
|
246
|
+
node.parsingPriority = route.options?.skipRouteOnParseError?.priority ?? 0;
|
|
230
247
|
if (isLeaf && !node.route) {
|
|
231
248
|
node.route = route;
|
|
232
249
|
node.fullPath = route.fullPath ?? route.from;
|
|
@@ -246,6 +263,10 @@ function parseSegments(defaultCaseSensitive, data, route, start, node, depth, on
|
|
|
246
263
|
}
|
|
247
264
|
}
|
|
248
265
|
function sortDynamic(a, b) {
|
|
266
|
+
if (a.skipOnParamError && !b.skipOnParamError) return -1;
|
|
267
|
+
if (!a.skipOnParamError && b.skipOnParamError) return 1;
|
|
268
|
+
if (a.skipOnParamError && b.skipOnParamError && (a.parsingPriority || b.parsingPriority))
|
|
269
|
+
return b.parsingPriority - a.parsingPriority;
|
|
249
270
|
if (a.prefix && b.prefix && a.prefix !== b.prefix) {
|
|
250
271
|
if (a.prefix.startsWith(b.prefix)) return -1;
|
|
251
272
|
if (b.prefix.startsWith(a.prefix)) return 1;
|
|
@@ -263,6 +284,11 @@ function sortDynamic(a, b) {
|
|
|
263
284
|
return 0;
|
|
264
285
|
}
|
|
265
286
|
function sortTreeNodes(node) {
|
|
287
|
+
if (node.pathless) {
|
|
288
|
+
for (const child of node.pathless) {
|
|
289
|
+
sortTreeNodes(child);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
266
292
|
if (node.static) {
|
|
267
293
|
for (const child of node.static.values()) {
|
|
268
294
|
sortTreeNodes(child);
|
|
@@ -296,6 +322,7 @@ function createStaticNode(fullPath) {
|
|
|
296
322
|
return {
|
|
297
323
|
kind: SEGMENT_TYPE_PATHNAME,
|
|
298
324
|
depth: 0,
|
|
325
|
+
pathless: null,
|
|
299
326
|
index: null,
|
|
300
327
|
static: null,
|
|
301
328
|
staticInsensitive: null,
|
|
@@ -304,13 +331,17 @@ function createStaticNode(fullPath) {
|
|
|
304
331
|
wildcard: null,
|
|
305
332
|
route: null,
|
|
306
333
|
fullPath,
|
|
307
|
-
parent: null
|
|
334
|
+
parent: null,
|
|
335
|
+
parse: null,
|
|
336
|
+
skipOnParamError: false,
|
|
337
|
+
parsingPriority: 0
|
|
308
338
|
};
|
|
309
339
|
}
|
|
310
340
|
function createDynamicNode(kind, fullPath, caseSensitive, prefix, suffix) {
|
|
311
341
|
return {
|
|
312
342
|
kind,
|
|
313
343
|
depth: 0,
|
|
344
|
+
pathless: null,
|
|
314
345
|
index: null,
|
|
315
346
|
static: null,
|
|
316
347
|
staticInsensitive: null,
|
|
@@ -320,6 +351,9 @@ function createDynamicNode(kind, fullPath, caseSensitive, prefix, suffix) {
|
|
|
320
351
|
route: null,
|
|
321
352
|
fullPath,
|
|
322
353
|
parent: null,
|
|
354
|
+
parse: null,
|
|
355
|
+
skipOnParamError: false,
|
|
356
|
+
parsingPriority: 0,
|
|
323
357
|
caseSensitive,
|
|
324
358
|
prefix,
|
|
325
359
|
suffix
|
|
@@ -412,19 +446,21 @@ function findMatch(path, segmentTree, fuzzy = false) {
|
|
|
412
446
|
const parts = path.split("/");
|
|
413
447
|
const leaf = getNodeMatch(path, parts, segmentTree, fuzzy);
|
|
414
448
|
if (!leaf) return null;
|
|
415
|
-
const
|
|
416
|
-
if ("**" in leaf) params["**"] = leaf["**"];
|
|
417
|
-
const route = leaf.node.route;
|
|
449
|
+
const [rawParams] = extractParams(path, parts, leaf);
|
|
418
450
|
return {
|
|
419
|
-
route,
|
|
420
|
-
|
|
451
|
+
route: leaf.node.route,
|
|
452
|
+
rawParams,
|
|
453
|
+
parsedParams: leaf.parsedParams
|
|
421
454
|
};
|
|
422
455
|
}
|
|
423
456
|
function extractParams(path, parts, leaf) {
|
|
424
457
|
const list = buildBranch(leaf.node);
|
|
425
458
|
let nodeParts = null;
|
|
426
|
-
const
|
|
427
|
-
|
|
459
|
+
const rawParams = {};
|
|
460
|
+
let partIndex = leaf.extract?.part ?? 0;
|
|
461
|
+
let nodeIndex = leaf.extract?.node ?? 0;
|
|
462
|
+
let pathIndex = leaf.extract?.path ?? 0;
|
|
463
|
+
for (; nodeIndex < list.length; partIndex++, nodeIndex++, pathIndex++) {
|
|
428
464
|
const node = list[nodeIndex];
|
|
429
465
|
const part = parts[partIndex];
|
|
430
466
|
const currentPathIndex = pathIndex;
|
|
@@ -441,10 +477,10 @@ function extractParams(path, parts, leaf) {
|
|
|
441
477
|
nodePart.length - sufLength - 1
|
|
442
478
|
);
|
|
443
479
|
const value = part.substring(preLength, part.length - sufLength);
|
|
444
|
-
|
|
480
|
+
rawParams[name] = decodeURIComponent(value);
|
|
445
481
|
} else {
|
|
446
482
|
const name = nodePart.substring(1);
|
|
447
|
-
|
|
483
|
+
rawParams[name] = decodeURIComponent(part);
|
|
448
484
|
}
|
|
449
485
|
} else if (node.kind === SEGMENT_TYPE_OPTIONAL_PARAM) {
|
|
450
486
|
if (leaf.skipped & 1 << nodeIndex) {
|
|
@@ -460,7 +496,7 @@ function extractParams(path, parts, leaf) {
|
|
|
460
496
|
nodePart.length - sufLength - 1
|
|
461
497
|
);
|
|
462
498
|
const value = node.suffix || node.prefix ? part.substring(preLength, part.length - sufLength) : part;
|
|
463
|
-
if (value)
|
|
499
|
+
if (value) rawParams[name] = decodeURIComponent(value);
|
|
464
500
|
} else if (node.kind === SEGMENT_TYPE_WILDCARD) {
|
|
465
501
|
const n = node;
|
|
466
502
|
const value = path.substring(
|
|
@@ -468,12 +504,13 @@ function extractParams(path, parts, leaf) {
|
|
|
468
504
|
path.length - (n.suffix?.length ?? 0)
|
|
469
505
|
);
|
|
470
506
|
const splat = decodeURIComponent(value);
|
|
471
|
-
|
|
472
|
-
|
|
507
|
+
rawParams["*"] = splat;
|
|
508
|
+
rawParams._splat = splat;
|
|
473
509
|
break;
|
|
474
510
|
}
|
|
475
511
|
}
|
|
476
|
-
|
|
512
|
+
if (leaf.rawParams) Object.assign(rawParams, leaf.rawParams);
|
|
513
|
+
return [rawParams, { part: partIndex, node: nodeIndex, path: pathIndex }];
|
|
477
514
|
}
|
|
478
515
|
function buildRouteBranch(route) {
|
|
479
516
|
const list = [route];
|
|
@@ -514,7 +551,15 @@ function getNodeMatch(path, parts, segmentTree, fuzzy) {
|
|
|
514
551
|
let bestMatch = null;
|
|
515
552
|
while (stack.length) {
|
|
516
553
|
const frame = stack.pop();
|
|
517
|
-
|
|
554
|
+
const { node, index, skipped, depth, statics, dynamics, optionals } = frame;
|
|
555
|
+
let { extract, rawParams, parsedParams } = frame;
|
|
556
|
+
if (node.skipOnParamError) {
|
|
557
|
+
const result = validateMatchParams(path, parts, frame);
|
|
558
|
+
if (!result) continue;
|
|
559
|
+
rawParams = frame.rawParams;
|
|
560
|
+
extract = frame.extract;
|
|
561
|
+
parsedParams = frame.parsedParams;
|
|
562
|
+
}
|
|
518
563
|
if (fuzzy && node.route && node.kind !== SEGMENT_TYPE_INDEX && isFrameMoreSpecific(bestFuzzy, frame)) {
|
|
519
564
|
bestFuzzy = frame;
|
|
520
565
|
}
|
|
@@ -523,7 +568,8 @@ function getNodeMatch(path, parts, segmentTree, fuzzy) {
|
|
|
523
568
|
if (node.route && !pathIsIndex && isFrameMoreSpecific(bestMatch, frame)) {
|
|
524
569
|
bestMatch = frame;
|
|
525
570
|
}
|
|
526
|
-
if (!node.optional && !node.wildcard && !node.index)
|
|
571
|
+
if (!node.optional && !node.wildcard && !node.index && !node.pathless)
|
|
572
|
+
continue;
|
|
527
573
|
}
|
|
528
574
|
const part = isBeyondPath ? void 0 : parts[index];
|
|
529
575
|
let lowerPart;
|
|
@@ -535,8 +581,15 @@ function getNodeMatch(path, parts, segmentTree, fuzzy) {
|
|
|
535
581
|
depth: depth + 1,
|
|
536
582
|
statics,
|
|
537
583
|
dynamics,
|
|
538
|
-
optionals
|
|
584
|
+
optionals,
|
|
585
|
+
extract,
|
|
586
|
+
rawParams,
|
|
587
|
+
parsedParams
|
|
539
588
|
};
|
|
589
|
+
if (node.index.skipOnParamError) {
|
|
590
|
+
const result = validateMatchParams(path, parts, indexFrame);
|
|
591
|
+
if (!result) continue;
|
|
592
|
+
}
|
|
540
593
|
if (statics === partsLength && !dynamics && !optionals && !skipped) {
|
|
541
594
|
return indexFrame;
|
|
542
595
|
}
|
|
@@ -558,15 +611,23 @@ function getNodeMatch(path, parts, segmentTree, fuzzy) {
|
|
|
558
611
|
const casePart = segment.caseSensitive ? end : end.toLowerCase();
|
|
559
612
|
if (casePart !== suffix) continue;
|
|
560
613
|
}
|
|
561
|
-
|
|
614
|
+
const frame2 = {
|
|
562
615
|
node: segment,
|
|
563
616
|
index: partsLength,
|
|
564
617
|
skipped,
|
|
565
618
|
depth,
|
|
566
619
|
statics,
|
|
567
620
|
dynamics,
|
|
568
|
-
optionals
|
|
621
|
+
optionals,
|
|
622
|
+
extract,
|
|
623
|
+
rawParams,
|
|
624
|
+
parsedParams
|
|
569
625
|
};
|
|
626
|
+
if (segment.skipOnParamError) {
|
|
627
|
+
const result = validateMatchParams(path, parts, frame2);
|
|
628
|
+
if (!result) continue;
|
|
629
|
+
}
|
|
630
|
+
wildcardMatch = frame2;
|
|
570
631
|
break;
|
|
571
632
|
}
|
|
572
633
|
}
|
|
@@ -582,7 +643,10 @@ function getNodeMatch(path, parts, segmentTree, fuzzy) {
|
|
|
582
643
|
depth: nextDepth,
|
|
583
644
|
statics,
|
|
584
645
|
dynamics,
|
|
585
|
-
optionals
|
|
646
|
+
optionals,
|
|
647
|
+
extract,
|
|
648
|
+
rawParams,
|
|
649
|
+
parsedParams
|
|
586
650
|
});
|
|
587
651
|
}
|
|
588
652
|
if (!isBeyondPath) {
|
|
@@ -601,7 +665,10 @@ function getNodeMatch(path, parts, segmentTree, fuzzy) {
|
|
|
601
665
|
depth: nextDepth,
|
|
602
666
|
statics,
|
|
603
667
|
dynamics,
|
|
604
|
-
optionals: optionals + 1
|
|
668
|
+
optionals: optionals + 1,
|
|
669
|
+
extract,
|
|
670
|
+
rawParams,
|
|
671
|
+
parsedParams
|
|
605
672
|
});
|
|
606
673
|
}
|
|
607
674
|
}
|
|
@@ -622,7 +689,10 @@ function getNodeMatch(path, parts, segmentTree, fuzzy) {
|
|
|
622
689
|
depth: depth + 1,
|
|
623
690
|
statics,
|
|
624
691
|
dynamics: dynamics + 1,
|
|
625
|
-
optionals
|
|
692
|
+
optionals,
|
|
693
|
+
extract,
|
|
694
|
+
rawParams,
|
|
695
|
+
parsedParams
|
|
626
696
|
});
|
|
627
697
|
}
|
|
628
698
|
}
|
|
@@ -638,7 +708,10 @@ function getNodeMatch(path, parts, segmentTree, fuzzy) {
|
|
|
638
708
|
depth: depth + 1,
|
|
639
709
|
statics: statics + 1,
|
|
640
710
|
dynamics,
|
|
641
|
-
optionals
|
|
711
|
+
optionals,
|
|
712
|
+
extract,
|
|
713
|
+
rawParams,
|
|
714
|
+
parsedParams
|
|
642
715
|
});
|
|
643
716
|
}
|
|
644
717
|
}
|
|
@@ -652,7 +725,28 @@ function getNodeMatch(path, parts, segmentTree, fuzzy) {
|
|
|
652
725
|
depth: depth + 1,
|
|
653
726
|
statics: statics + 1,
|
|
654
727
|
dynamics,
|
|
655
|
-
optionals
|
|
728
|
+
optionals,
|
|
729
|
+
extract,
|
|
730
|
+
rawParams,
|
|
731
|
+
parsedParams
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
if (node.pathless) {
|
|
736
|
+
const nextDepth = depth + 1;
|
|
737
|
+
for (let i = node.pathless.length - 1; i >= 0; i--) {
|
|
738
|
+
const segment = node.pathless[i];
|
|
739
|
+
stack.push({
|
|
740
|
+
node: segment,
|
|
741
|
+
index,
|
|
742
|
+
skipped,
|
|
743
|
+
depth: nextDepth,
|
|
744
|
+
statics,
|
|
745
|
+
dynamics,
|
|
746
|
+
optionals,
|
|
747
|
+
extract,
|
|
748
|
+
rawParams,
|
|
749
|
+
parsedParams
|
|
656
750
|
});
|
|
657
751
|
}
|
|
658
752
|
}
|
|
@@ -668,14 +762,24 @@ function getNodeMatch(path, parts, segmentTree, fuzzy) {
|
|
|
668
762
|
sliceIndex += parts[i].length;
|
|
669
763
|
}
|
|
670
764
|
const splat = sliceIndex === path.length ? "/" : path.slice(sliceIndex);
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
"**": decodeURIComponent(splat)
|
|
675
|
-
};
|
|
765
|
+
bestFuzzy.rawParams ??= {};
|
|
766
|
+
bestFuzzy.rawParams["**"] = decodeURIComponent(splat);
|
|
767
|
+
return bestFuzzy;
|
|
676
768
|
}
|
|
677
769
|
return null;
|
|
678
770
|
}
|
|
771
|
+
function validateMatchParams(path, parts, frame) {
|
|
772
|
+
try {
|
|
773
|
+
const [rawParams, state] = extractParams(path, parts, frame);
|
|
774
|
+
frame.rawParams = rawParams;
|
|
775
|
+
frame.extract = state;
|
|
776
|
+
const parsed = frame.node.parse(rawParams);
|
|
777
|
+
frame.parsedParams = Object.assign({}, frame.parsedParams, parsed);
|
|
778
|
+
return true;
|
|
779
|
+
} catch {
|
|
780
|
+
return null;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
679
783
|
function isFrameMoreSpecific(prev, next) {
|
|
680
784
|
if (!prev) return true;
|
|
681
785
|
return next.statics > prev.statics || next.statics === prev.statics && (next.dynamics > prev.dynamics || next.dynamics === prev.dynamics && (next.optionals > prev.optionals || next.optionals === prev.optionals && ((next.node.kind === SEGMENT_TYPE_INDEX) > (prev.node.kind === SEGMENT_TYPE_INDEX) || next.node.kind === SEGMENT_TYPE_INDEX === (prev.node.kind === SEGMENT_TYPE_INDEX) && next.depth > prev.depth)));
|