core/quality/eslint/no-boolean-trap.eslint.rule.ts

core/quality/eslint/no-boolean-trap.eslint.rule.ts is a file in GovLab Quality. 43 lines of code and 7 definitions.

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

const MIN_BOOL_TRAP = 2;

const isBooleanLiteral = function isBooleanLiteral(arg: { type: string; value?: unknown }): boolean {
    return arg.type === "Literal" && typeof arg.value === "boolean";
};

const booleanLiteralCount = function booleanLiteralCount(args: { type: string; value?: unknown }[]): number {
    let count = 0;
    for (const arg of args) {
        if (isBooleanLiteral(arg)) {
            count += 1;
        }
    }
    return count;
};

export default {
    create(context: Rule.RuleContext): Rule.RuleListener {
        const check = (node: Rule.Node): void => {
            if (node.type !== "CallExpression" && node.type !== "NewExpression") {
                return;
            }
            const count = booleanLiteralCount(node.arguments);
            if (count >= MIN_BOOL_TRAP) {
                context.report({ data: { count: String(count) }, messageId: "booleanTrap", node });
            }
        };
        const handlers: [string, (node: Rule.Node) => void][] = [
            ["CallExpression", check],
            ["NewExpression", check],
        ];
        return Object.fromEntries(handlers);
    },
    meta: govlabMeta({
        canonical: ["boolean-trap"],
        description:
            "Disallow a call or construction passing two or more boolean literal arguments — mandate a named options object",
        messages: {
            booleanTrap:
                "This passes {{count}} boolean literals positionally — a boolean trap: the call site cannot say what each flag means, and adding a flag shifts every position. Pass a single named options object ({ active: true, archived: false }) so each intent is labeled where it is set.",
        },
        ruleId: "boolean_trap",
    }),
} satisfies Rule.RuleModule;