core/quality/eslint/require-mapped-concept.eslint.rule.ts

core/quality/eslint/require-mapped-concept.eslint.rule.ts is a file in GovLab Quality. 64 lines of code and 12 definitions.

import type { Rule } from "eslint";
import { isRegisteredConcept } from "#configuration/quality/generated/canon.generated";

const MESSAGE =
    "Concept '{{concept}}' declared in a rule's canonical is not in the shared quality-relations concept registry. " +
    "Add it to the CONCEPTS registry (the single source) and run `npm run catalog:regenerate` — " +
    "the concept list this rule reads is regenerated from there. An island concept never joins the cross-ecosystem config surface. " +
    "[require_mapped_concept]";

interface ElementNode {
    type?: string;
    value?: unknown;
}

interface PropertyNode {
    type: string;
    key?: { type?: string; name?: string };
    value?: { type?: string; elements?: ElementNode[] };
}

const SELECTORS = ["Property"];

const isPropertyNode = function isPropertyNode(node: unknown): node is PropertyNode {
    return node !== null && typeof node === "object" && "type" in node;
};

const canonicalElements = function canonicalElements(prop: PropertyNode | null): ElementNode[] | null {
    if (prop?.key?.type !== "Identifier" || prop.key.name !== "canonical") {
        return null;
    }
    if (prop.value?.type !== "ArrayExpression") {
        return null;
    }
    return prop.value.elements ?? [];
};

const stringLiteral = function stringLiteral(element: ElementNode): string | null {
    return element.type === "Literal" && typeof element.value === "string" ? element.value : null;
};

const onProperty = function onProperty(context: Rule.RuleContext, node: Rule.Node): void {
    const elements = canonicalElements(isPropertyNode(node) ? node : null);
    if (elements === null) {
        return;
    }
    for (const element of elements) {
        const concept = stringLiteral(element);
        if (concept !== null && !isRegisteredConcept(concept)) {
            context.report({ data: { concept }, messageId: "unmapped", node });
        }
    }
};

export default {
    create(context): Rule.RuleListener {
        const listeners: Rule.RuleListener = {};
        for (const selector of SELECTORS) {
            listeners[selector] = (node: Rule.Node): void => {
                onProperty(context, node);
            };
        }
        return listeners;
    },
    meta: {
        docs: {
            description:
                "Require every rule's canonical concept(s) to be a first-class concept in the shared quality-relations registry",
        },
        messages: { unmapped: MESSAGE },
        schema: [],
        type: "problem",
    },
} satisfies Rule.RuleModule;