shared/matchers/location.matcher.ts

shared/matchers/location.matcher.ts is a file in GovLab Extension Host. 162 lines of code and 35 definitions.

const NON_PATH_CHARS = ["<", ">", "(", ")", ",", "::", "${", "*", "\\"];

const isSpecifier = function isSpecifier(value: string): boolean {
    return value.startsWith("@") || value.startsWith("http") || value.startsWith("node:");
};

export const isSegmentLike = function isSegmentLike(value: string): boolean {
    if (value.length < 2) {
        return false;
    }
    if (value.includes(" ") || value.includes("\n")) {
        return false;
    }
    for (const c of NON_PATH_CHARS) {
        if (value.includes(c)) {
            return false;
        }
    }
    if (value.startsWith("-") || isSpecifier(value)) {
        return false;
    }
    return !value.endsWith(".json");
};

const MIN_PATH_SEGMENTS = 2;

const SYSTEM_ROOTS: ReadonlySet<string> = new Set(["usr", "opt", "bin", "etc", "var", "tmp", "Applications"]);

const segmentCount = function segmentCount(value: string): number {
    return value.split("/").filter((segment) => segment.length > 0).length;
};

const isSystemPath = function isSystemPath(value: string): boolean {
    return value.startsWith("/") && SYSTEM_ROOTS.has(value.split("/")[1] ?? "");
};

const MEDIA_TOP_LEVEL_TYPES: ReadonlySet<string> = new Set([
    "application",
    "audio",
    "font",
    "image",
    "message",
    "model",
    "multipart",
    "text",
    "video",
]);

const WORD_STOPS: ReadonlySet<string> = new Set(['"', "'", "=", "[", " "]);

const wordBefore = function wordBefore(text: string, end: number): string {
    let start = end;
    while (start > 0 && !WORD_STOPS.has(text.charAt(start - 1))) {
        start -= 1;
    }
    return text.slice(start, end);
};

const isMediaType = function isMediaType(value: string): boolean {
    const slash = value.indexOf("/");
    if (slash === -1 || value.includes("/", slash + 1)) {
        return false;
    }
    return MEDIA_TOP_LEVEL_TYPES.has(wordBefore(value, slash));
};

export const isPathLike = function isPathLike(value: string): boolean {
    if (value.length < 4) {
        return false;
    }
    if (!isSegmentLike(value) || isSystemPath(value) || isMediaType(value)) {
        return false;
    }
    if (value.startsWith(".")) {
        return false;
    }
    if (value.includes("/")) {
        return segmentCount(value) >= MIN_PATH_SEGMENTS;
    }
    return value.endsWith(".ts");
};

const skipToLineEnd = function skipToLineEnd(text: string, at: number): number {
    const nl = text.indexOf("\n", at);
    return nl === -1 ? text.length : nl;
};

const skipToBlockEnd = function skipToBlockEnd(text: string, at: number): number {
    const end = text.indexOf("*/", at + 2);
    return end === -1 ? text.length : end + 2;
};

export const stripComments = function stripComments(text: string): string {
    let out = "";
    let i = 0;
    while (i < text.length) {
        if (text.startsWith("//", i)) {
            i = skipToLineEnd(text, i);
        } else if (text.startsWith("/*", i)) {
            i = skipToBlockEnd(text, i);
        } else {
            out += text[i];
            i += 1;
        }
    }
    return out;
};

const QUOTES = new Set(['"', "'"]);

export const stringLiteralsOf = function stringLiteralsOf(text: string): string[] {
    const out: string[] = [];
    let i = 0;
    while (i < text.length) {
        const ch = text[i] ?? "";
        if (!QUOTES.has(ch)) {
            i += 1;
            continue;
        }
        const close = text.indexOf(ch, i + 1);
        if (close === -1) {
            return out;
        }
        const body = text.slice(i + 1, close);
        if (!body.includes("\n")) {
            out.push(body);
        }
        i = close + 1;
    }
    return out;
};

const PATH_QUALIFIER = "path.";

const isPathJoin = function isPathJoin(text: string, at: number): boolean {
    if (at === 0) {
        return true;
    }
    if (text[at - 1] !== ".") {
        return true;
    }
    return text.slice(Math.max(0, at - PATH_QUALIFIER.length), at) === PATH_QUALIFIER;
};

export const joinedPathsOf = function joinedPathsOf(text: string): string[] {
    const out: string[] = [];
    const marker = "join(";
    let at = text.indexOf(marker);
    while (at >= 0) {
        const close = text.indexOf(")", at);
        if (close === -1) {
            break;
        }
        const segments = isPathJoin(text, at)
            ? stringLiteralsOf(text.slice(at + marker.length, close)).filter(
                  (s) => s !== ".." && s !== "." && s.length > 0,
              )
            : [];
        if (segments.length > 0) {
            out.push(segments.join("/"));
        }
        at = text.indexOf(marker, close);
    }
    return out;
};

const HINT_MARKER = "_HINTS";

export const hintLiteralsOf = function hintLiteralsOf(text: string): Set<string> {
    const out = new Set<string>();
    let at = text.indexOf(HINT_MARKER);
    while (at >= 0) {
        const open = text.indexOf("[", at);
        const close = open === -1 ? -1 : text.indexOf("]", open);
        if (open !== -1 && close > open) {
            for (const s of stringLiteralsOf(text.slice(open, close))) {
                out.add(s);
            }
        }
        at = text.indexOf(HINT_MARKER, at + HINT_MARKER.length);
    }
    return out;
};