Files
2026Technology-Competition/docs/superpowers/specs/2026-07-29-eslint-rule-enhancement-design.md
T
范智鹏 5848eaa82a feat: Linter 规则精细化增强 + 模板导入/导出闭环
ESLint: +29 条 P1/P2 规则 + 12 条 TS 专属规则(含 no-shadow/no-array-constructor 冲突处理)
Stylelint: 集成 stylelint-config-recommended + 27 条额外规则
PMD: 排除 20 弃用 + 17 噪音规则,补启 Security/Multithreading,274+12 条精选
SQL-lint: 内置精选 57 条规则配置 + 按 tier 分级 severity + 无项目配置时自动注入临时配置
模板导出: export-service.ts 导出 2-sheet xlsx(复用 xlsx 零新依赖)
模板导入: template-converter.ts 固定列映射解析 + dedup-prompt.ts AI 语义去重
导入预览增强: 错误规则分组置顶只读、跳过行提示、空数据提示
i18n: 新增 18 条模板导入/导出相关翻译
WebView: 静态分析/自定义规则项默认可展开显示 suggestion
2026-07-30 23:17:20 +08:00

20 KiB
Raw Blame History

ESLint 规则增强设计书

一、背景与目标

1.1 现状

当前 ESLintAdaptersrc/adapters/eslint.ts)内置默认配置为:

public static getDefaultConfig(): any[] {
    if (!ESLintAdapter.defaultConfig) {
        ESLintAdapter.defaultConfig = [
            js.configs.recommended,        // @eslint/js recommended → 61 条
            ...ts.configs.recommended,      // typescript-eslint recommended → 24 条
        ];
    }
    return ESLintAdapter.defaultConfig;
}

仅启用了官方 recommended 的 61 条 ESLint 核心规则,全部属于 "Possible Problems" 类(语法错误、死代码等)。ESLint 9.x 活跃规则共 199 条,当前覆盖率仅 31%。

1.2 目标

将内置配置从 61 条扩展到 94 条(P0 官方推荐 61 + P1 强烈推荐 17 + P2 建议启用 16),使静态分析能捕获更多高频真实 bug,同时保持低误报率。

1.3 设计原则

  • 不破坏现有配置优先级:全局配置 > 项目配置 > 内置配置,三层择一逻辑不变
  • 不引入新依赖P1/P2 规则全部来自 eslint 包内置规则,无需额外安装插件
  • 同步更新去重数据static-rules.json 必须同步追加新增规则,保证自定义规则导入时的去重检测覆盖完整
  • 不修改规则 ID 前缀格式:诊断结果仍使用 eslint:{ruleId} 格式

二、影响范围分析

2.1 需要修改的文件

文件 修改类型 修改内容
src/adapters/eslint.ts 代码修改 getDefaultConfig() 方法增加 P1+P2 规则配置
src/rules/static-rules.json 数据修改 rules.eslint 数组追加 33 条规则条目
package.json 依赖修改 dependencies 中补充 @eslint/js 显式声明

2.2 不需要修改的文件

文件 原因
scripts/build.mjs @eslint/js 不在 external 列表中,已被 esbuild 正确打包
src/adapters/jsp.ts 内部复用 ESLintAdapter 实例,自动继承新配置
src/orchestrator/orchestrator.ts 仅做调度,不涉及配置逻辑
src/config/linter.ts 配置读取层不变
eslint.config.mjs 项目自身 lint 配置,与运行时适配器配置无关

2.3 不受影响的功能

  • 全局配置(linters.eslintConfigPath):用户指定配置文件时完全替代内置配置,不受影响
  • 项目配置(.eslintrc.* / eslint.config.*):存在时完全替代内置配置,不受影响
  • JSP 适配器:复用 ESLintAdapter,自动获得增强后的配置
  • 规则 ID 输出格式:仍为 eslint:{ruleId},不变

三、详细设计

3.1 修改 src/adapters/eslint.ts

3.1.1 当前代码

import js from '@eslint/js';
import ts from 'typescript-eslint';

// ... 中间部分不变 ...

public static getDefaultConfig(): any[] {
    if (!ESLintAdapter.defaultConfig) {
        ESLintAdapter.defaultConfig = [
            js.configs.recommended,
            ...ts.configs.recommended,
        ];
    }
    return ESLintAdapter.defaultConfig;
}

3.1.2 修改后代码

import js from '@eslint/js';
import ts from 'typescript-eslint';

// 新增:P1+P2 额外规则配置常量
const extraRules: Record<string, 'error' | 'warn'> = {
    // === P1:强烈推荐(error===
    'eqeqeq': 'error',
    'no-eq-null': 'error',
    'no-self-compare': 'error',
    'no-promise-executor-return': 'error',
    'no-shadow': 'error',
    'no-unassigned-vars': 'error',
    'no-useless-assignment': 'error',
    'block-scoped-var': 'error',
    'default-case': 'error',
    'default-case-last': 'error',
    'no-unmodified-loop-condition': 'error',
    'no-unreachable-loop': 'error',
    'no-constant-binary-expression': 'error',
    'no-eval': 'error',
    'no-implied-eval': 'error',
    'no-extend-native': 'error',
    // === P2:建议启用 ===
    'no-var': 'error',
    'no-await-in-loop': 'warn',
    'prefer-template': 'warn',
    'prefer-object-spread': 'warn',
    'prefer-rest-params': 'warn',
    'prefer-spread': 'warn',
    'prefer-object-has-own': 'warn',
    'no-useless-concat': 'warn',
    'no-useless-return': 'warn',
    'no-useless-computed-key': 'warn',
    'no-useless-rename': 'warn',
    'no-param-reassign': 'warn',
    'no-return-assign': 'error',
    'no-throw-literal': 'error',
    'camelcase': 'warn',
    'new-cap': 'warn',
    'no-array-constructor': 'error',
};

// ... 中间部分不变 ...

public static getDefaultConfig(): any[] {
    if (!ESLintAdapter.defaultConfig) {
        ESLintAdapter.defaultConfig = [
            js.configs.recommended,
            ...ts.configs.recommended,
            // 新增:P1+P2 额外规则
            { rules: extraRules },
        ];
    }
    return ESLintAdapter.defaultConfig;
}

3.1.3 设计说明

为什么用 extraRules 常量而不是内联对象?

  • 可读性:33 条规则单独成文件级常量,与 getDefaultConfig() 逻辑分离
  • 可维护性:未来增删规则只需修改 extraRules 对象,不需要动方法逻辑
  • 可测试性:常量可以被测试文件直接导入验证

为什么放在 recommended 之后?

ESLint Flat Config 的规则是后者覆盖前者。extraRules 放在最后,如果其中某些规则已在 recommended 中启用(如 no-implied-eval 在 recommended 中已有),extraRules 的配置会覆盖其级别。但实际上 P1/P2 选型时已排除了与 recommended 重复的规则,不存在冲突。

为什么不用 @eslint/jsconfigs.all

js.configs.all 启用全部 199 条活跃规则,包含大量不应在代码审查工具中强制启用的规则(如 no-magic-numbersmax-lines 等)。精确选择 33 条更合理。

3.2 修改 src/rules/static-rules.json

3.2.1 修改内容

rules.eslint 数组末尾追加以下 33 条规则条目(格式与现有 61 条一致):

{"id": "eslint/eqeqeq", "description": "Require === and !=="},
{"id": "eslint/no-eq-null", "description": "Disallow null comparisons without type-checking"},
{"id": "eslint/no-self-compare", "description": "Disallow comparisons where both sides are the same"},
{"id": "eslint/no-await-in-loop", "description": "Disallow await inside loops"},
{"id": "eslint/no-promise-executor-return", "description": "Disallow returning values from Promise executor"},
{"id": "eslint/no-shadow", "description": "Disallow variable declarations from shadowing variables in outer scopes"},
{"id": "eslint/no-unassigned-vars", "description": "Disallow let or var variables that are read but never assigned"},
{"id": "eslint/no-useless-assignment", "description": "Disallow variable assignments where the value is not used"},
{"id": "eslint/block-scoped-var", "description": "Enforce variables within the scope they are defined"},
{"id": "eslint/default-case", "description": "Require default cases in switch statements"},
{"id": "eslint/default-case-last", "description": "Enforce default clauses in switch statements to be last"},
{"id": "eslint/no-unmodified-loop-condition", "description": "Disallow unmodified loop conditions"},
{"id": "eslint/no-unreachable-loop", "description": "Disallow loops with a body that allows only one iteration"},
{"id": "eslint/no-constant-binary-expression", "description": "Disallow expressions where the operation doesn't affect the value"},
{"id": "eslint/no-eval", "description": "Disallow the use of eval()"},
{"id": "eslint/no-extend-native", "description": "Disallow extending native types"},
{"id": "eslint/no-var", "description": "Require let or const instead of var"},
{"id": "eslint/prefer-template", "description": "Require template literals instead of string concatenation"},
{"id": "eslint/prefer-object-spread", "description": "Disallow Object.assign and prefer object spread"},
{"id": "eslint/prefer-rest-params", "description": "Require rest parameters instead of arguments"},
{"id": "eslint/prefer-spread", "description": "Require spread operator instead of .apply()"},
{"id": "eslint/prefer-object-has-own", "description": "Disallow Object.prototype.hasOwnProperty.call() and prefer Object.hasOwn()"},
{"id": "eslint/no-useless-concat", "description": "Disallow unnecessary concatenation of literals or template literals"},
{"id": "eslint/no-useless-return", "description": "Disallow redundant return statements"},
{"id": "eslint/no-useless-computed-key", "description": "Disallow unnecessary computed property keys in objects and classes"},
{"id": "eslint/no-useless-rename", "description": "Disallow renaming import, export, and destructured assignments to the same name"},
{"id": "eslint/no-param-reassign", "description": "Disallow reassigning function parameters"},
{"id": "eslint/no-return-assign", "description": "Disallow assignment operators in return statements"},
{"id": "eslint/no-throw-literal", "description": "Disallow throwing literals as exceptions"},
{"id": "eslint/camelcase", "description": "Enforce camelcase naming convention"},
{"id": "eslint/new-cap", "description": "Require constructor names to begin with a capital letter"},
{"id": "eslint/no-array-constructor", "description": "Disallow Array constructors"}

注:no-implied-eval 已在 recommended 中启用,不在追加列表中。追加的 33 条均为 recommended 之外的新增规则。

3.2.2 修改 linterVersion 字段

"linterVersion": {
    "eslint": "9.x (94 rules)",
    ...
}

"9.x (recommended)" 改为 "9.x (94 rules)",反映实际启用规则数量。

3.2.3 为什么必须同步更新 static-rules.json

static-rules.json 有两个用途:

  1. UI 展示:在设置面板中展示当前 linter 支持的规则清单
  2. 去重检测:自定义规则导入时,按 ID 匹配 static-rules.json 中的规则,若已存在则提示重复

如果不更新,用户新增的 eqeqeq 自定义规则不会被识别为重复(因为 static-rules.json 中没有这条),导致重复规则。

3.3 修改 package.json

3.3.1 当前代码

"dependencies": {
    "eslint": "^9.39.3",
    "stylelint": "^17.14.0",
    "typescript-eslint": "^8.56.1",
    "xlsx": "^0.18.5"
}

3.3.2 修改后代码

"dependencies": {
    "@eslint/js": "^9.39.3",
    "eslint": "^9.39.3",
    "stylelint": "^17.14.0",
    "typescript-eslint": "^8.56.1",
    "xlsx": "^0.18.5"
}

3.3.3 修改理由

eslint.tsimport js from '@eslint/js' 已被使用,但 @eslint/js 未在 package.json 中显式声明,仅作为 eslint 包的传递依赖存在。这属于版本迭代遗留问题(见 _AI_USAGE_LOG.md 第82条记录)。显式声明可消除依赖不确定性。


四、新增规则分类详解

4.1 P1 强烈推荐(17 条,error 级别)

这些规则检测高频真实 bug,误报率极低。

4.1.1 等值与比较(3 条)

规则 检测场景 误报评估
eqeqeq == 隐式类型转换 极低,几乎所有现代项目都启用
no-eq-null x == null 同时匹配 null 和 undefined 低,通常只需判断其一
no-self-compare a === a 恒为 true 零误报,几乎一定是笔误

4.1.2 异步与 Promise2 条)

规则 检测场景 误报评估
no-await-in-loop 循环中串行 await 导致性能问题 中,设为 warn,允许逐条忽略
no-promise-executor-return executor 返回值被忽略 零误报

4.1.3 作用域与变量(4 条)

规则 检测场景 误报评估
no-shadow 内层变量遮蔽外层变量 低,遮蔽几乎都是 bug
no-unassigned-vars 读取未赋值的 let/var 零误报
no-useless-assignment 赋值后值未被使用
block-scoped-var var 变量在声明作用域外使用

4.1.4 控制流与逻辑(5 条)

规则 检测场景 误报评估
default-case switch 缺少 default 分支
default-case-last default 不在最后 零误报
no-unmodified-loop-condition 循环条件未修改导致死循环 零误报
no-unreachable-loop 循环体只允许一次迭代
no-constant-binary-expression 操作不影响结果值

4.1.5 安全(3 条)

规则 检测场景 误报评估
no-eval eval() 执行任意代码 零误报
no-implied-eval setTimeout("code") 类似 eval 零误报(已在 recommended 中)
no-extend-native 修改 Array.prototype 等 零误报

4.2 P2 建议启用(16 条)

4.2.1 现代 JS 语法(6 条)

规则 级别 检测场景
no-var error 使用 var 代替 let/const
prefer-template warn 字符串拼接代替模板字面量
prefer-object-spread warn Object.assign 代替对象展开
prefer-rest-params warn arguments 代替 rest 参数
prefer-spread warn .apply() 代替展开运算符
prefer-object-has-own warn 旧式 hasOwnProperty 代替 Object.hasOwn

4.2.2 代码简洁性(4 条)

规则 级别 检测场景
no-useless-concat warn 不必要的字符串拼接
no-useless-return warn 多余的 return 语句
no-useless-computed-key warn 不必要的计算属性键
no-useless-rename warn 重命名为相同名称

4.2.3 防御性编程(3 条)

规则 级别 检测场景
no-param-reassign warn 修改函数参数(副作用风险)
no-return-assign error return 中赋值(笔误风险)
no-throw-literal error 抛出字面量而非 Error 对象

4.2.4 命名与风格(3 条)

规则 级别 检测场景
camelcase warn 非驼峰命名
new-cap warn 构造函数未大写
no-array-constructor error 使用 Array 构造函数

五、规则级别设计

5.1 级别分配原则

级别 适用场景 数量
error 确定性 bug、安全问题、语法错误 74 条
warn 代码质量建议、风格偏好(可逐条忽略) 20 条

5.2 为什么部分规则设为 warn 而非 error

以下规则设为 warn 是因为它们有合理的例外场景:

规则 warn 理由
no-await-in-loop 某些场景确实需要串行 await(如分页请求)
prefer-template 超长字符串拼接时 + 可能更清晰
prefer-rest-params 兼容旧环境时可能需要 arguments
no-param-reassign Redux reducer 等模式需要修改参数
camelcase 对接外部 API 时可能需要 snake_case

其余规则设为 error 是因为它们的触发几乎一定意味着 bug 或安全问题。


六、兼容性分析

6.1 对现有用户代码的影响

启用新规则后,之前能通过审查的代码可能会新增诊断:

影响程度 规则 说明
可能大量新增诊断 eqeqeq 大量旧代码使用 ==,但这是必要的改进
可能大量新增诊断 no-shadow 嵌套作用域中常见,需逐个审查
中等新增诊断 no-param-reassign 函数中修改参数较常见
少量新增诊断 default-case 大多数 switch 已有 default
少量新增诊断 no-var 现代 TS 项目已普遍使用 let/const
极少新增诊断 no-eval / no-extend-native 正常项目几乎不会用

6.2 对 JSP 适配器的影响

JSP 适配器(src/adapters/jsp.ts)内部持有 ESLintAdapter 实例,提取 <script> 块后复用其 check() 方法。新增规则自动生效,无需额外修改。

6.3 对去重功能的影响

static-rules.json 同步更新后,去重检测范围从 61 条扩展到 94 条。用户导入的自定义规则如果与这 33 条新规则 ID 匹配,将正确识别为重复。

6.4 对构建的影响

  • @eslint/js 已被 esbuild 正确打包(不在 external 列表中)
  • 新增的 33 条规则全部来自 eslint 包内置,无需安装额外 npm 包
  • 构建产物体积影响可忽略(规则配置仅为一个 JS 对象)

七、实施步骤

步骤 1:修改 package.json

dependencies 中添加 "@eslint/js": "^9.39.3"

步骤 2:修改 src/adapters/eslint.ts

  1. 在文件顶部(import 之后、PROJECT_CONFIG_FILES 之前)添加 extraRules 常量
  2. getDefaultConfig() 方法的返回数组末尾追加 { rules: extraRules }

步骤 3:修改 src/rules/static-rules.json

  1. rules.eslint 数组末尾追加 33 条规则条目
  2. 更新 linterVersion.eslint"9.x (94 rules)"

步骤 4:验证

  1. 执行 npm run lint 确认项目自身代码无新增报错
  2. 执行 npm run compile 确认编译通过
  3. 执行 npm run build 确认打包成功
  4. 检查 static-rules.json 中 eslint 规则数量为 94 条

八、测试要点

8.1 单元测试

测试项 验证内容
getDefaultConfig() 返回值 数组长度为 3recommended + ts recommended + extraRules
extraRules 规则数 恰好 33 条
extraRules 级别 17 条 error + 16 条 warn
规则 ID 无重复 与 recommended 61 条无交集

8.2 集成测试

测试项 验证内容
== 触发 eqeqeq 诊断结果包含 eslint:eqeqeq
var x = 1 触发 no-var 诊断结果包含 eslint:no-var
eval("code") 触发 no-eval 诊断结果包含 eslint:no-eval
switch 无 default 触发 default-case 诊断结果包含 eslint:default-case
全局配置存在时忽略内置配置 使用用户配置,不触发新规则
项目配置存在时忽略内置配置 使用项目配置,不触发新规则

8.3 去重测试

测试项 验证内容
导入 eqeqeq 自定义规则 提示与已有规则重复
导入 no-eval 自定义规则 提示与已有规则重复
导入 recommended 中已有的规则 仍提示重复(未改变原有行为)

九、风险评估

9.1 主要风险

风险 级别 缓解措施
用户代码新增大量诊断 P2 规则设为 warn,用户可逐条忽略
no-shadow 误报较多 该规则在大型项目中较常见,但遮蔽几乎都是 bug
camelcase 对接外部 API 报错 用户可通过项目配置覆盖该规则
@eslint/js 版本不匹配 锁定与 eslint 相同的 ^9.39.3

9.2 回滚方案

如果新规则导致严重问题,回滚步骤:

  1. getDefaultConfig() 中移除 { rules: extraRules }
  2. static-rules.json 中移除追加的 33 条规则
  3. 恢复 linterVersion.eslint"9.x (recommended)"

package.json@eslint/js 的声明可以保留(本就是应该声明的依赖)。


十、附录

10.1 规则来源参考

  • ESLint 官方规则文档:https://eslint.org/docs/latest/rules
  • 推荐规则分析文件:/workspace/eslint-recommended-rules.md
  • 完整规则清单:/workspace/eslint-9-rules.md

10.2 规则数量统计

分类 数量 级别
P0 官方 recommended 61 error(由 recommended 定义)
P1 强烈推荐 17 error
P2 建议启用 16 13 warn + 3 error
合计 94 74 error + 20 warn
未启用(冻结/弃用/格式化/过度限制) 218