All files matcher.ts

100% Statements 65/65
100% Branches 22/22
100% Functions 4/4
100% Lines 65/65

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 722x     2x 731x 731x 731x 731x 2338x 2338x 2338x 2338x 2338x 2338x 2338x 2338x 2422x 2422x 2422x 3638x 40x 40x   3598x 3598x 84x 3638x 36x 36x 36x   3638x 3554x 3638x 2204x 2204x 2204x 2204x 2204x 2204x 3638x 182x 2422x 2338x 2338x 2338x 731x   2x 906x 906x 906x 906x 2731x 2731x 2731x 2731x 2731x 2731x 2731x 2731x 2731x 173x 2557x 2731x 906x 906x   2x 2839x 2839x  
import { compile, match } from 'path-to-regexp';
import type { RouteConfig, RouteMatcher, RouteParsedConfig } from './types';
 
export function createMatcher(
    routes: RouteConfig[],
    compiledRoutes = createRouteMatches(routes)
): RouteMatcher {
    return (
        toURL: URL,
        baseURL: URL,
        cb?: (item: RouteParsedConfig) => boolean
    ) => {
        const matchPath = toURL.pathname.substring(baseURL.pathname.length - 1);
        const matches: RouteParsedConfig[] = [];
        const params: Record<string, string | string[]> = {};
        const collectMatchingRoutes = (
            routes: RouteParsedConfig[]
        ): boolean => {
            for (const item of routes) {
                if (cb && !cb(item)) {
                    continue;
                }
                // Depth-first traversal
                if (
                    item.children.length &&
                    collectMatchingRoutes(item.children)
                ) {
                    matches.unshift(item);
                    return true;
                }
                // At this point, children exist but none matched — if requireIndex is true, skip self-match
                if (item.requireIndex && item.children.length) continue;
                const result = item.match(matchPath);
                if (result) {
                    matches.unshift(item);
                    if (typeof result === 'object') {
                        Object.assign(params, result.params);
                    }
                    return true;
                }
            }
            return false;
        };
        collectMatchingRoutes(compiledRoutes);
        return { matches: Object.freeze(matches), params };
    };
}
 
export function createRouteMatches(
    routes: RouteConfig[],
    base = ''
): RouteParsedConfig[] {
    return routes.map((route: RouteConfig): RouteParsedConfig => {
        const compilePath = joinPathname(route.path, base);
        return {
            ...route,
            compilePath,
            match: match(compilePath),
            compile: compile(compilePath),
            meta: route.meta || {},
            requireIndex: route.requireIndex ?? false,
            children: Array.isArray(route.children)
                ? createRouteMatches(route.children, compilePath)
                : []
        };
    });
}
 
export function joinPathname(pathname: string, base = '') {
    return '/' + `${base}/${pathname}`.split('/').filter(Boolean).join('/');
}