core/quality/eslint/no-increment-operator.eslint.rule.ts
core/quality/eslint/no-increment-operator.eslint.rule.ts is a file in GovLab Quality. 65 lines of code and 12 definitions.
import type { Rule } from "eslint";
import { govlabMeta } from "#core/factories/eslint.factory";
interface ParentNode {
type: string;
update?: unknown;
parent?: ParentNode;
}
const isParent = function isParent(value: unknown): value is ParentNode {
return value !== null && typeof value === "object" && "type" in value;
};
const asParent = function asParent(value: unknown): ParentNode | null {
return isParent(value) ? value : null;
};
const grandForUpdate = function grandForUpdate(parent: ParentNode): boolean {
if (parent.type !== "SequenceExpression") {
return false;
}
const grand = parent.parent;
return grand?.type === "ForStatement" && grand.update === parent;
};
const valueUnused = function valueUnused(node: Rule.Node): boolean {
const parent = asParent(node.parent);
if (parent === null) {
return false;
}
if (parent.type === "ExpressionStatement") {
return true;
}
if (parent.type === "ForStatement" && parent.update === node) {
return true;
}
return grandForUpdate(parent);
};
export default {
create(context: Rule.RuleContext): Rule.RuleListener {
const onUpdate = (node: Rule.Node): void => {
if (node.type !== "UpdateExpression") {
return;
}
const replacement = node.operator === "++" ? "+=" : "-=";
const fixable = valueUnused(node);
context.report({
data: { operator: node.operator, replacement },
...(fixable
? {
fix: (fixer): Rule.Fix =>
fixer.replaceText(node, `${context.sourceCode.getText(node.argument)} ${replacement} 1`),
}
: {}),
messageId: "unexpected",
node,
});
};
const handlers: [string, (node: Rule.Node) => void][] = [["UpdateExpression", onUpdate]];
return Object.fromEntries(handlers);
},
meta: govlabMeta({
canonical: ["operator-style"],
description: "Disallow the ++ and -- operators; use `+= 1` / `-= 1` (fixed where the value is unused)",
fixable: "code",
messages: { unexpected: "Unary operator '{{operator}}' is not allowed; use '{{replacement}} 1'." },
ruleId: "no_increment_operator",
type: "suggestion",
}),
} satisfies Rule.RuleModule;