core/quality/eslint/no-optional-dependency.eslint.rule.ts

core/quality/eslint/no-optional-dependency.eslint.rule.ts is a file in GovLab Quality. 58 lines of code and 12 definitions.

import type { Rule } from "eslint";
import { govlabMeta } from "#core/factories/eslint.factory";

interface TsParam {
    type: string;
    parameter?: { optional?: boolean; typeAnnotation?: { typeAnnotation?: { type: string } } };
}

const isOptionalInjectedDependency = function isOptionalInjectedDependency(param: TsParam): boolean {
    if (param.type !== "TSParameterProperty" || !param.parameter) {
        return false;
    }
    const inner = param.parameter;
    if (inner.optional !== true) {
        return false;
    }
    const annotation = inner.typeAnnotation?.typeAnnotation;
    return annotation?.type === "TSTypeReference";
};

const isRecord = function isRecord(value: unknown): value is Record<string, unknown> {
    return value !== null && typeof value === "object";
};

const isRuleNode = function isRuleNode(value: unknown): value is Rule.Node {
    return value !== null && typeof value === "object" && "type" in value;
};

const isTsParam = function isTsParam(value: unknown): value is TsParam {
    return isRecord(value) && typeof value["type"] === "string";
};

const paramsOf = function paramsOf(node: unknown): TsParam[] {
    if (!isRecord(node) || !isRecord(node["value"])) {
        return [];
    }
    const { params } = node["value"];
    return Array.isArray(params) ? params.filter(isTsParam) : [];
};

export default {
    create(context: Rule.RuleContext): Rule.RuleListener {
        const onConstructor = (node: Rule.Node): void => {
            for (const param of paramsOf(node)) {
                if (isOptionalInjectedDependency(param) && isRuleNode(param)) {
                    context.report({ messageId: "optionalDependency", node: param });
                }
            }
        };
        const handlers: [string, (node: Rule.Node) => void][] = [
            ["MethodDefinition[kind='constructor']", onConstructor],
        ];
        return Object.fromEntries(handlers);
    },
    meta: govlabMeta({
        canonical: ["mandatory-dependency"],
        description:
            "Disallow an optional injected constructor dependency — mandate mandatory injection so behavior is system-defined",
        messages: {
            optionalDependency:
                "This injected constructor dependency is optional — every use must null-guard it, and the component's behavior silently changes with its presence (caller-defined, not system-defined). Make it mandatory: a required parameter, defaulted to a no-op implementation when absence is genuinely valid.",
        },
        ruleId: "optional_dependency",
    }),
} satisfies Rule.RuleModule;