@truenine/eslint9-config
Version:
ESLint 9 configuration package for Compose Client projects with TypeScript, Vue, and modern JavaScript support
97 lines (96 loc) • 3.3 kB
JavaScript
//#region src/rules/code-style/prefer-lookup-table.ts
/**
* ESLint rule: prefer-lookup-table
*
* Detects long chains of `||` checks against the same variable and suggests using a Set lookup or Array.includes.
*/
const rule = {
meta: {
type: "suggestion",
docs: {
description: "Prefer Set.has or Array.includes for multiple equality checks against the same variable",
recommended: false
},
fixable: "code",
schema: [{
type: "object",
properties: { threshold: {
type: "integer",
minimum: 3,
default: 4
} },
additionalProperties: false
}],
messages: { preferLookup: "Found {{count}} checks against the same value. Consider optimizing with a lookup table (Set/Array)." }
},
create(context) {
const { sourceCode } = context;
const threshold = (context.options[0] ?? {}).threshold ?? 4;
function getNormalizedText(node) {
return sourceCode.getText(node).replaceAll(/\s+/g, "");
}
function isEqualityCheck(node) {
if (node.type !== "BinaryExpression") return false;
const op = node.operator;
return op === "===" || op === "==";
}
function isPure(node) {
return node.type === "Identifier" || node.type === "MemberExpression" || node.type === "Literal";
}
function collectConditions(node, operator, conditions = []) {
if (node.type === "LogicalExpression" && node.operator === operator) {
const logicalNode = node;
collectConditions(logicalNode.left, operator, conditions);
collectConditions(logicalNode.right, operator, conditions);
} else conditions.push(node);
return conditions;
}
const reportSet = /* @__PURE__ */ new Set();
return { LogicalExpression(node) {
if (node.operator !== "||") return;
if (reportSet.has(node)) return;
const currentParent = node.parent;
if (currentParent?.type === "LogicalExpression" && currentParent.operator === "||") return;
const conditions = collectConditions(node, "||");
if (conditions.length < threshold) return;
const subjectMap = /* @__PURE__ */ new Map();
for (const condition of conditions) {
if (!isEqualityCheck(condition)) continue;
const binExpr = condition;
let subject = null;
let value = null;
if (isPure(binExpr.left) && binExpr.right.type === "Literal") {
subject = binExpr.left;
value = binExpr.right;
} else if (isPure(binExpr.right) && binExpr.left.type === "Literal") {
subject = binExpr.right;
value = binExpr.left;
}
if (subject !== null && value !== null) {
const subjectKey = getNormalizedText(subject);
if (!subjectMap.has(subjectKey)) subjectMap.set(subjectKey, {
subject,
values: []
});
subjectMap.get(subjectKey).values.push(value);
}
}
for (const [_, { subject, values }] of subjectMap.entries()) if (values.length >= threshold) {
context.report({
node,
messageId: "preferLookup",
data: { count: values.length.toString() },
fix(fixer) {
const valuesText = values.map((v) => sourceCode.getText(v)).join(", ");
const subjectText = sourceCode.getText(subject);
return fixer.replaceText(node, `new Set([${valuesText}]).has(${subjectText})`);
}
});
break;
}
} };
}
};
//#endregion
export { rule as default };
//# sourceMappingURL=prefer-lookup-table.mjs.map