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;