core/validators/reference.validator.ts

core/validators/reference.validator.ts is a file in GovLab Docs. 118 lines of code and 25 definitions.

import { DECLARE_FORMS, EXPORT_FORMS } from "#configuration/constants/verb.constants";
import type {
    RefCheck,
    RefConstruct,
    RefConstructDefect,
    RefFinding,
    RefResolveCtx,
    RefScan,
    RefScanOptions,
} from "#types/reference.types";
import {
    emptyDirectory,
    emptyFile,
    missingPath,
    notDeclared,
    notExported,
    notReferenced,
    unknownClaim,
    unknownVerbResolved,
    unsatisfiedClaim,
} from "#configuration/strings/reference.strings";
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
import { listValues, parseFrontmatter } from "#core/parsers/metadata.parser";
import { join } from "node:path";
import { parseReferences } from "#core/parsers/reference.parser";

const FIRST_COLUMN = 1;
const CLAIM_KEY = "validates";
const LIST_SEPARATOR = ", ";

const declaredClaims = function declaredClaims(source: string): string[] {
    const raw = parseFrontmatter(source).fields[CLAIM_KEY];
    return typeof raw === "string" ? (listValues(raw) ?? []) : [];
};

const claimDefect = function claimDefect(
    claim: string,
    satisfied: ReadonlySet<string>,
    options: RefScanOptions,
): RefConstructDefect | null {
    if (!options.claims.includes(claim)) {
        const detail = unknownClaim(claim, options.claims.join(LIST_SEPARATOR));
        return { code: "unknown-claim", col: FIRST_COLUMN, detail, line: FIRST_COLUMN };
    }
    if (satisfied.has(claim)) {
        return null;
    }
    return { code: "unsatisfied-claim", col: FIRST_COLUMN, detail: unsatisfiedClaim(claim), line: FIRST_COLUMN };
};

const claimDefects = function claimDefects(
    source: string,
    used: readonly RefConstruct[],
    options: RefScanOptions,
): RefConstructDefect[] {
    const satisfied = new Set(used.flatMap((construct) => options.verbs[construct.verb]?.satisfies ?? []));
    return declaredClaims(source).flatMap((claim) => {
        const defect = claimDefect(claim, satisfied, options);
        return defect === null ? [] : [defect];
    });
};

export const refConstructs = function refConstructs(source: string, options: RefScanOptions): RefScan {
    const scan = parseReferences(source, Object.keys(options.verbs));
    return {
        constructs: scan.constructs,
        defects: [...scan.defects, ...claimDefects(source, scan.constructs, options)],
    };
};

const resolveIn = function resolveIn(roots: readonly string[], target: string): string | null {
    return roots.map((root) => join(root, target)).find((full) => existsSync(full)) ?? null;
};

const emptyDefect = function emptyDefect(full: string, target: string): string | null {
    const stat = statSync(full);
    if (stat.isDirectory()) {
        return readdirSync(full).length === 0 ? emptyDirectory(target) : null;
    }
    return stat.size === 0 ? emptyFile(target) : null;
};

const namedIn = function namedIn(text: string, forms: readonly string[], name: string): boolean {
    if (forms.some((form) => text.includes(form + name))) {
        return true;
    }
    const listed = [`{ ${name} }`, `{ ${name},`, `, ${name} }`, `, ${name},`];
    return listed.some((shape) => text.includes(shape));
};

const identDetail = function identDetail(check: RefCheck, full: string, construct: RefConstruct): string | null {
    if (statSync(full).isDirectory()) {
        return null;
    }
    const text = readFileSync(full, "utf8");
    const { identifier, path } = construct;
    if (check === "ident-exported-from-path") {
        return namedIn(text, EXPORT_FORMS, identifier) ? null : notExported(identifier, path);
    }
    if (check === "ident-declared-in-path") {
        return namedIn(text, DECLARE_FORMS, identifier) ? null : notDeclared(identifier, path);
    }
    return text.includes(identifier) ? null : notReferenced(identifier, path);
};

const detailFor = function detailFor(check: RefCheck, construct: RefConstruct, context: RefResolveCtx): string | null {
    const full = resolveIn(context.roots, construct.path);
    if (full === null) {
        return missingPath(construct.path);
    }
    return check === "path-exists" ? emptyDefect(full, construct.path) : identDetail(check, full, construct);
};

const constructFindings = function constructFindings(construct: RefConstruct, context: RefResolveCtx): RefFinding[] {
    const verb = context.verbs[construct.verb];
    if (verb === undefined) {
        return [{ col: construct.col, detail: unknownVerbResolved(construct.verb), line: construct.line }];
    }
    return verb.checks.flatMap((check) => {
        const detail = detailFor(check, construct, context);
        return detail === null ? [] : [{ col: construct.col, detail, line: construct.line }];
    });
};

export const resolveRefs = function resolveRefs(
    constructs: readonly RefConstruct[],
    context: RefResolveCtx,
): RefFinding[] {
    return constructs.flatMap((construct) => constructFindings(construct, context));
};