Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | 1x 1x 1x 1x 1x 1x 978x 978x 978x 1x 324x 324x 324x 324x 324x 324x 324x 143x 143x 322x 322x 324x 324x 324x 324x 277x 277x 174x 258x 103x 1x 1x 103x 103x 103x 103x 103x 103x 324x 23x 23x 23x 5x 23x 18x 1x 1x 18x 18x 18x 18x 18x 18x 45x 16x 16x 16x 1x 1x 16x 16x 16x 16x 16x 16x 324x 1x 143x 143x 143x 143x 143x 143x | import { DYNAMIC_ROUTE_KEY, ROUTE_MAP_LINK, WILDCARD_ROUTE_KEY } from './constant.js';
import type { MatchedRoute, TRec, UnknownRoute } from './types.js';
/**
* A registry for organizing and matching routes.
*
* Extends Map to store child routes keyed by their segment names.
* Supports static, dynamic (`:param`), and wildcard (`*`) route matching.
*
* @example
* ```ts
* const registry = new RouteRegistry(route);
* const match = registry.match('/users/123');
* ```
*/
export class RouteRegistry extends Map {
/**
* Gets the name of the route this registry is associated with.
*
* @returns The route name
*/
public get name() {
return this.route.name;
}
/**
* Creates a new RouteRegistry instance.
*
* @param route - The route this registry is associated with
*/
constructor(public route: UnknownRoute) {
super();
ROUTE_MAP_LINK.set(this.route, this);
}
/**
* Matches a URL path against the registered routes.
*
* Recursively traverses the route tree to find the best match.
* Supports static segments, dynamic parameters, and wildcards.
*
* @param urlSegments - The URL path to match, as a string or array of segments
* @param segments - Accumulator for matched route segments (internal use)
* @param params - Accumulator for extracted parameters (internal use)
* @param index - Current segment index (internal use)
* @returns A matched route with segments and params, or undefined if no match
*
* @example
* ```ts
* const match = registry.match('/users/123');
* if (match) {
* console.log(match.route); // The matched route
* console.log(match.params); // { id: '123' }
* console.log(match.segments); // Array of matched routes
* }
* ```
*/
public match(
urlSegments: string | string[],
segments: UnknownRoute[] = [],
params: TRec = {},
index = 0
): MatchedRoute | void {
if (!urlSegments || !urlSegments.length) return;
if (typeof urlSegments === 'string') {
urlSegments = cleanPath(urlSegments).split('/');
}
const segment = urlSegments[index];
const recursive = urlSegments.length > index + 1;
const staticRoute = segment === '' ? this : (this.get(segment) as RouteRegistry);
const dynamicRoute = this.get(DYNAMIC_ROUTE_KEY) as RouteRegistry;
const wildcardRoute = this.get(WILDCARD_ROUTE_KEY) as RouteRegistry;
if (staticRoute) {
segments.push(staticRoute.route);
if (recursive) {
return staticRoute.match(urlSegments, segments, params, index + 1);
} else {
if (staticRoute.route.index) {
segments.push(staticRoute.route.index);
}
return {
route: staticRoute.route,
segments,
params,
};
}
} else if (dynamicRoute) {
params[dynamicRoute.name.replace(/^:/, '')] = segment;
segments.push(dynamicRoute.route);
if (recursive) {
return dynamicRoute.match(urlSegments, segments, params, index + 1);
} else {
if (dynamicRoute.route.index) {
segments.push(dynamicRoute.route.index);
}
return {
route: dynamicRoute.route,
segments,
params,
};
}
} else if (wildcardRoute) {
params['*'] = urlSegments.slice(index);
segments.push(wildcardRoute.route);
if (wildcardRoute.route.index) {
segments.push(wildcardRoute.route.index);
}
return {
route: wildcardRoute.route,
segments,
params,
};
}
}
}
/**
* Cleans a path string by normalizing slashes.
*
* Removes leading, trailing, and duplicate slashes.
*
* @param path - The path string to clean
* @returns The cleaned path string
*
* @internal
*/
function cleanPath(path: string) {
return path
.replace(/^[\/]+/, '/')
.replace(/[\/]+/g, '/')
.replace(/[\/]+$/, '');
}
|