core/analyzers/reference.analyzer.ts

core/analyzers/reference.analyzer.ts is a file in GovLab Docs. 116 lines of code and 32 definitions.

import type { BrokenRef, PathRef, PathsCtx } from "#types/reference.types";
import { dirname, join } from "node:path";
import { existsSync } from "node:fs";
import { fileTokens } from "#core/parsers/metadata.parser";
import { isRelativeTarget } from "#core/predicates/reference.predicate";
import { normalizeTarget } from "#core/normalizers/reference.normalizer";
import { pathReferences } from "#core/parsers/location.parser";

const FM_LINE_OFFSET = 2;
const FENCE = "---";
const FENCE_CLOSE = "\n---";
const KEY_SEPARATOR = ":";
const WILDCARD = "*";
const HOME_MARK = "~";
const ROOT_MARK = "/";
const DEPENDENCY_DIR = "node_modules";
const FM_PROSE_FIELDS: ReadonlySet<string> = new Set(["description", "summary", "name", "title"]);

const extensionOf = function extensionOf(path: string): string {
    const dot = path.lastIndexOf(".");
    return dot === -1 ? "" : path.slice(dot);
};

const firstSegment = function firstSegment(path: string): string {
    const slash = path.indexOf("/");
    return slash === -1 ? path : path.slice(0, slash);
};

const existsBySuffix = function existsBySuffix(fileIndex: ReadonlyMap<string, string[]>, target: string): boolean {
    const suffix = `/${target}`;
    const candidates = fileIndex.get(target.slice(target.lastIndexOf("/") + 1)) ?? [];
    return candidates.some((relPath) => relPath === target || relPath.endsWith(suffix));
};

const existsFromAncestors = function existsFromAncestors(root: string, docPath: string, target: string): boolean {
    let dir = dirname(docPath);
    for (;;) {
        if (existsSync(join(dir, target))) {
            return true;
        }
        const parent = dirname(dir);
        if (dir === root || parent === dir) {
            return false;
        }
        dir = parent;
    }
};

const isOutOfScope = function isOutOfScope(context: PathsCtx, target: string): boolean {
    if (target === "" || target.startsWith(ROOT_MARK) || target.startsWith(HOME_MARK)) {
        return true;
    }
    return firstSegment(target) === DEPENDENCY_DIR || context.runtimeRoots.some((root) => target.startsWith(root));
};

const isCheckable = function isCheckable(context: PathsCtx, docPath: string, target: string): boolean {
    if (isOutOfScope(context, target)) {
        return false;
    }
    if (isRelativeTarget(target)) {
        return join(dirname(docPath), target).startsWith(context.root);
    }
    return context.codeExtensions.has(extensionOf(target)) || context.topLevel.has(firstSegment(target));
};

const targetFound = function targetFound(context: PathsCtx, docPath: string, target: string): boolean {
    if (isRelativeTarget(target)) {
        return existsSync(join(dirname(docPath), target));
    }
    return existsFromAncestors(context.root, docPath, target) || existsBySuffix(context.fileIndex, target);
};

const brokenRef = function brokenRef(context: PathsCtx, docPath: string, ref: PathRef): BrokenRef | null {
    const target = normalizeTarget(ref.path);
    if (!isCheckable(context, docPath, target) || targetFound(context, docPath, target)) {
        return null;
    }
    return { col: ref.col, line: ref.line, path: target };
};

export const brokenPaths = function brokenPaths(context: PathsCtx, docPath: string, source: string): BrokenRef[] {
    return pathReferences(source).flatMap((ref) => {
        const broken = brokenRef(context, docPath, ref);
        return broken === null ? [] : [broken];
    });
};

const tokenFound = function tokenFound(context: PathsCtx, docPath: string, token: string): boolean {
    return (
        existsSync(join(dirname(docPath), token)) ||
        existsBySuffix(context.fileIndex, token) ||
        existsFromAncestors(context.root, docPath, token)
    );
};

const lineFileRefs = function lineFileRefs(
    context: PathsCtx,
    docPath: string,
    line: string,
    index: number,
): BrokenRef[] {
    const colon = line.indexOf(KEY_SEPARATOR);
    if (colon !== -1 && FM_PROSE_FIELDS.has(line.slice(0, colon).trim())) {
        return [];
    }
    return fileTokens(line)
        .filter((token) => !token.includes(WILDCARD) && context.codeExtensions.has(extensionOf(token)))
        .filter((token) => !tokenFound(context, docPath, token))
        .map((token) => ({ col: 1, line: index + FM_LINE_OFFSET, path: token }));
};

export const frontmatterFileRefs = function frontmatterFileRefs(
    context: PathsCtx,
    docPath: string,
    source: string,
): BrokenRef[] {
    if (!source.startsWith(FENCE)) {
        return [];
    }
    const firstBreak = source.indexOf("\n");
    const end = firstBreak === -1 ? -1 : source.indexOf(FENCE_CLOSE, firstBreak);
    if (end === -1) {
        return [];
    }
    return source
        .slice(firstBreak + 1, end)
        .split("\n")
        .flatMap((line, index) => lineFileRefs(context, docPath, line, index));
};