UNPKG

@truenine/eslint9-config

Version:

ESLint 9 configuration package for Compose Client projects with TypeScript, Vue, and modern JavaScript support

1 lines 6.67 kB
{"version":3,"file":"prefer-lookup-table.cjs","names":[],"sources":["../../../src/rules/code-style/prefer-lookup-table.ts"],"sourcesContent":["import type {Rule} from 'eslint'\n\n/**\n * ESLint rule: prefer-lookup-table\n *\n * Detects long chains of `||` checks against the same variable and suggests using a Set lookup or Array.includes.\n */\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n docs: {\n description: 'Prefer Set.has or Array.includes for multiple equality checks against the same variable',\n recommended: false\n },\n fixable: 'code',\n schema: [\n {\n type: 'object',\n properties: {\n threshold: {type: 'integer', minimum: 3, default: 4}\n },\n additionalProperties: false\n }\n ],\n messages: {\n preferLookup: 'Found {{count}} checks against the same value. Consider optimizing with a lookup table (Set/Array).'\n }\n },\n create(context) {\n const {sourceCode} = context\n const options = (context.options[0] ?? {}) as {threshold?: number}\n const threshold = options.threshold ?? 4\n\n function getNormalizedText(node: Rule.Node): string { /* 辅助函数:标准化节点文本,去除空格干扰 */\n return sourceCode.getText(node).replaceAll(/\\s+/g, '')\n }\n\n function isEqualityCheck(node: Rule.Node): boolean { /* 辅助函数:判断是否是简单的相等比较节点 */\n if (node.type !== 'BinaryExpression') return false\n const op = (node as unknown as {operator: string}).operator\n return op === '===' || op === '=='\n }\n\n function isPure(node: Rule.Node): boolean { /* 辅助函数:判断节点是否包含副作用 */\n return node.type === 'Identifier' || node.type === 'MemberExpression' || node.type === 'Literal'\n }\n\n function collectConditions(node: Rule.Node, operator: string, conditions: Rule.Node[] = []): Rule.Node[] { /* 递归收集 LogicalExpression 中的所有条件 */\n if (node.type === 'LogicalExpression' && (node as unknown as {operator: string}).operator === operator) {\n const logicalNode = node as Rule.Node & {left: Rule.Node, right: Rule.Node, operator: string}\n collectConditions(logicalNode.left, operator, conditions)\n collectConditions(logicalNode.right, operator, conditions)\n } else conditions.push(node)\n return conditions\n }\n\n const reportSet = new Set<Rule.Node>()\n\n return {\n LogicalExpression(node) {\n if ((node as unknown as {operator: string}).operator !== '||') return /* 只处理 || 链 */\n if (reportSet.has(node)) return /* 避免重复报告子节点 */\n const currentParent = node.parent\n if (currentParent?.type === 'LogicalExpression' && (currentParent as unknown as {operator: string}).operator === '||') return /* 让顶层处理 */\n\n const conditions = collectConditions(node, '||')\n if (conditions.length < threshold) return\n\n const subjectMap = new Map<string, {subject: Rule.Node, values: Rule.Node[]}>() /* 分析每个条件 */\n\n for (const condition of conditions) {\n if (!isEqualityCheck(condition)) continue /* 必须是相等比较 */\n\n const binExpr = condition as Rule.Node & {left: Rule.Node, right: Rule.Node}\n let subject: Rule.Node | null = null\n let value: Rule.Node | null = null\n\n if (isPure(binExpr.left) && binExpr.right.type === 'Literal') { /* 尝试找出 subject (变量) 和 value (字面量) */\n subject = binExpr.left\n value = binExpr.right\n } else if (isPure(binExpr.right) && binExpr.left.type === 'Literal') {\n subject = binExpr.right\n value = binExpr.left\n }\n\n if (subject !== null && value !== null) {\n const subjectKey = getNormalizedText(subject)\n if (!subjectMap.has(subjectKey)) subjectMap.set(subjectKey, {subject, values: []})\n subjectMap.get(subjectKey)!.values.push(value)\n }\n }\n\n for (const [_, {subject, values}] of subjectMap.entries()) { /* 检查是否有某个 subject 超过阈值 */\n if (values.length >= threshold) {\n context.report({\n node,\n messageId: 'preferLookup',\n data: {count: values.length.toString()},\n fix(fixer) {\n const valuesText = values.map((v: Rule.Node) => sourceCode.getText(v)).join(', ')\n const subjectText = sourceCode.getText(subject)\n return fixer.replaceText(node, `new Set([${valuesText}]).has(${subjectText})`)\n }\n })\n break\n }\n }\n }\n }\n }\n}\n\nexport default rule\n"],"mappings":";;;;;;;AAOA,MAAM,OAAwB;CAC5B,MAAM;EACJ,MAAM;EACN,MAAM;GACJ,aAAa;GACb,aAAa;GACd;EACD,SAAS;EACT,QAAQ,CACN;GACE,MAAM;GACN,YAAY,EACV,WAAW;IAAC,MAAM;IAAW,SAAS;IAAG,SAAS;IAAE,EACrD;GACD,sBAAsB;GACvB,CACF;EACD,UAAU,EACR,cAAc,uGACf;EACF;CACD,OAAO,SAAS;EACd,MAAM,EAAC,eAAc;EAErB,MAAM,aADW,QAAQ,QAAQ,MAAM,EAAE,EACf,aAAa;EAEvC,SAAS,kBAAkB,MAAyB;AAClD,UAAO,WAAW,QAAQ,KAAK,CAAC,WAAW,QAAQ,GAAG;;EAGxD,SAAS,gBAAgB,MAA0B;AACjD,OAAI,KAAK,SAAS,mBAAoB,QAAO;GAC7C,MAAM,KAAM,KAAuC;AACnD,UAAO,OAAO,SAAS,OAAO;;EAGhC,SAAS,OAAO,MAA0B;AACxC,UAAO,KAAK,SAAS,gBAAgB,KAAK,SAAS,sBAAsB,KAAK,SAAS;;EAGzF,SAAS,kBAAkB,MAAiB,UAAkB,aAA0B,EAAE,EAAe;AACvG,OAAI,KAAK,SAAS,uBAAwB,KAAuC,aAAa,UAAU;IACtG,MAAM,cAAc;AACpB,sBAAkB,YAAY,MAAM,UAAU,WAAW;AACzD,sBAAkB,YAAY,OAAO,UAAU,WAAW;SACrD,YAAW,KAAK,KAAK;AAC5B,UAAO;;EAGT,MAAM,4BAAY,IAAI,KAAgB;AAEtC,SAAO,EACL,kBAAkB,MAAM;AACtB,OAAK,KAAuC,aAAa,KAAM;AAC/D,OAAI,UAAU,IAAI,KAAK,CAAE;GACzB,MAAM,gBAAgB,KAAK;AAC3B,OAAI,eAAe,SAAS,uBAAwB,cAAgD,aAAa,KAAM;GAEvH,MAAM,aAAa,kBAAkB,MAAM,KAAK;AAChD,OAAI,WAAW,SAAS,UAAW;GAEnC,MAAM,6BAAa,IAAI,KAAwD;AAE/E,QAAK,MAAM,aAAa,YAAY;AAClC,QAAI,CAAC,gBAAgB,UAAU,CAAE;IAEjC,MAAM,UAAU;IAChB,IAAI,UAA4B;IAChC,IAAI,QAA0B;AAE9B,QAAI,OAAO,QAAQ,KAAK,IAAI,QAAQ,MAAM,SAAS,WAAW;AAC5D,eAAU,QAAQ;AAClB,aAAQ,QAAQ;eACP,OAAO,QAAQ,MAAM,IAAI,QAAQ,KAAK,SAAS,WAAW;AACnE,eAAU,QAAQ;AAClB,aAAQ,QAAQ;;AAGlB,QAAI,YAAY,QAAQ,UAAU,MAAM;KACtC,MAAM,aAAa,kBAAkB,QAAQ;AAC7C,SAAI,CAAC,WAAW,IAAI,WAAW,CAAE,YAAW,IAAI,YAAY;MAAC;MAAS,QAAQ,EAAE;MAAC,CAAC;AAClF,gBAAW,IAAI,WAAW,CAAE,OAAO,KAAK,MAAM;;;AAIlD,QAAK,MAAM,CAAC,GAAG,EAAC,SAAS,aAAY,WAAW,SAAS,CACvD,KAAI,OAAO,UAAU,WAAW;AAC9B,YAAQ,OAAO;KACb;KACA,WAAW;KACX,MAAM,EAAC,OAAO,OAAO,OAAO,UAAU,EAAC;KACvC,IAAI,OAAO;MACT,MAAM,aAAa,OAAO,KAAK,MAAiB,WAAW,QAAQ,EAAE,CAAC,CAAC,KAAK,KAAK;MACjF,MAAM,cAAc,WAAW,QAAQ,QAAQ;AAC/C,aAAO,MAAM,YAAY,MAAM,YAAY,WAAW,SAAS,YAAY,GAAG;;KAEjF,CAAC;AACF;;KAIP;;CAEJ"}