All files breadcrumbs.js

97.14% Statements 34/35
92.3% Branches 24/26
100% Functions 7/7
97.14% Lines 34/35

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 801x   8x 8x         8x     8x     1x 4x 4x 1x   3x                   1x 4x 4x 1x   3x 1x   2x     1x 8x 1x   7x 4x   3x                         1x 4x 4x 1x     3x 3x   3x 8x 8x 8x 8x            
const humanizeSegment = (segment) => {
    let value;
    try {
        value = decodeURIComponent(segment);
    } catch {
        value = segment;
    }
 
    return value
        .replace(/[-_]+/g, " ")
        .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
        .replace(/^\p{Ll}/u, (letter) => letter.toUpperCase());
};
 
const normalizeRoute = (route) => {
    const withoutQuery = route.split(/[?#]/, 1)[0] || "/";
    if (withoutQuery === "/") {
        return withoutQuery;
    }
    return withoutQuery.replace(/\.html$/, "").replace(/\/+$/, "");
};
 
/**
 * Remove a VitePress deployment base from a route path.
 *
 * @param {string} route - Current VitePress route.
 * @param {string} base - Configured VitePress base.
 * @returns {string} Route relative to the documentation root.
 */
export const routeWithoutBase = (route, base = "/") => {
    const normalizedBase = `/${base.replace(/^\/+|\/+$/g, "")}`;
    if (normalizedBase === "/") {
        return route;
    }
    if (route === normalizedBase || route === `${normalizedBase}/`) {
        return "/";
    }
    return route.startsWith(`${normalizedBase}/`) ? route.slice(normalizedBase.length) : route;
};
 
const normalizeEntry = (entry, fallbackText) => {
    if (typeof entry === "string") {
        return { text: entry, link: true };
    }
    if (entry) {
        return { text: entry.text || fallbackText, link: entry.link !== false };
    }
    return { text: fallbackText, link: false };
};
 
/**
 * Build breadcrumb items for a VitePress route.
 *
 * Only routes present in the route index become links. Virtual grouping routes
 * therefore remain readable without creating links to pages that do not exist.
 *
 * @param {string} route - Current VitePress route.
 * @param {object} routes - Route metadata keyed by absolute route.
 * @returns {Array<{text: string, href: string | null}>} Breadcrumb items.
 */
export const breadcrumbsForRoute = (route, routes = {}) => {
    const normalized = normalizeRoute(route);
    if (normalized === "/") {
        return [];
    }
 
    const segments = normalized.slice(1).split("/");
    let accumulated = "";
 
    return segments.map((segment, index) => {
        accumulated += `/${segment}`;
        const entry = normalizeEntry(routes[accumulated], humanizeSegment(segment));
        const isLast = index === segments.length - 1;
        return {
            text: entry.text,
            href: !isLast && entry.link ? accumulated : null,
        };
    });
};