docs: 归档 demo 工程样例与插件实测报告

- 新增三个 demo 工程(demo-eslint / demo-sqlfluff / demo-stylelint):各含 src/ 样例代码与覆盖率映射(run-coverage.mjs、coverage_map.json、stylelint_line_map.json)

- 归档各 demo 的插件实测导出报告(reports/):

  - demo-eslint:common.js / esm-demo.js / legacysyntax.cjs / typescript.ts 审查报告 + 覆盖率报告(292/292 条零偏差,126 种规则全覆盖)

  - demo-sqlfluff:11 个 SQL 样例审查报告 + 覆盖率报告(113/113 条零偏差,57 种规则全覆盖,含 JJ01 行号缺陷修复后的复测验证)

  - demo-stylelint:common.css / empty-source.css 审查报告 + 覆盖率报告(145/145 条零偏差,68 种规则全覆盖)
This commit is contained in:
范智鹏
2026-08-25 22:00:19 +08:00
parent 9c676e46e6
commit c92249a044
43 changed files with 6103 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
{
"dependencies": {
"@eslint/js": "^10.0.1",
"typescript-eslint": "^8.66.0"
}
}
+662
View File
@@ -0,0 +1,662 @@
# 代码审查报告
**文件:** `data\demo-eslint\src\common.js`
**语言:** javascript
**耗时:** 288.0s
**分析工具:** eslint
---
总计: 214 | 错误: 197 | 警告: 17 | 建议: 0
静态分析 · 210 个问题
- 🔴 `eslint:require-yield` L12
L12: 此生成器函数没有 'yield'。
建议: 在生成器函数体内添加 yield 表达式;若本意不是生成器,请改为普通函数。
- 🔴 `eslint:no-unused-vars` L12
L12: 'generatorWithoutYield' 已定义但从未使用。
建议: 删除该函数声明;若它应被外部使用,请添加导出或实际调用。
- 🔴 `eslint:no-undef` L13
L13: 未定义变量 'console'。
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
- 🔴 `eslint:no-unused-vars` L17
L17: 'asyncExecutor' 被赋值但从未使用。
建议: 删除该变量,或在使用处引用它。
- 🔴 `eslint:no-async-promise-executor` L17
L17: Promise 执行器不应是 async 函数。
建议: 移除 async 关键字,改为普通 executor,并使用 resolve/reject 控制 Promise 状态。
- 🔴 `eslint:no-unused-vars` L17
L17: 'reject' 已定义但从未使用。
建议: 若不需要拒绝路径,可省略 reject 参数,或实际调用 reject。
- 🔴 `eslint:no-unused-vars` L22
L22: 'executorReturn' 被赋值但从未使用。
建议: 删除该变量,或在使用处引用它。
- 🔴 `eslint:no-unused-vars` L22
L22: 'reject' 已定义但从未使用。
建议: 若不需要拒绝路径,可省略 reject 参数,或实际调用 reject。
- 🔴 `eslint:no-promise-executor-return` L23
L23: Promise 执行器的返回值无法被读取。
建议: 不要在 executor 内 return,直接调用 resolve 即可。
- 🔴 `eslint:no-unused-vars` L27
L27: 'awaitInLoop' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-undef` L30
L30: 未定义变量 'fetch'。
建议: 确保运行环境为浏览器或 Node 18+,并在 ESLint 配置中声明对应全局变量。
- 🔴 `eslint:no-unused-vars` L37
L37: 'returnAssign' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-return-assign` L39
L39: 返回语句中不应包含赋值。
建议: 将赋值与返回分开,避免歧义。
- 🔴 `eslint:no-unused-vars` L39
L39: 'assignedInReturn' 被赋值但从未使用。
建议: 若不需要该局部变量,可以直接 return x。
- 🔴 `eslint:no-unused-vars` L43
L43: 'throwLiteral' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-throw-literal` L44
L44: 应抛出错误对象。
建议: 使用 new Error() 抛错,以保留堆栈信息。
- 🔴 `eslint:no-unused-vars` L48
L48: 'uselessReturn' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-undef` L54
L54: 未定义变量 'console'。
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
- 🔴 `eslint:no-unused-vars` L62
L62: 'BadConstructor' 已定义但从未使用。
建议: 删除该类,或在使用处实例化。
- 🔴 `eslint:constructor-super` L63
L63: 继承类构造函数中应调用 super()。
建议: 在构造函数体内最先调用 super(message)。
- 🔴 `eslint:no-undef` L64
L64: 未定义变量 'console'。
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
- 🔴 `eslint:no-class-assign` L70
L70: 'Widget' 是类,不能重新赋值。
建议: 删除对类名的赋值,或用其他变量名保存覆盖后的值。
- 🔴 `eslint:no-unused-vars` L70
L70: 'Widget' 被赋值但从未使用。
建议: 删除该类或在使用处实例化。
- 🔴 `eslint:no-unused-vars` L73
L73: 'DupeMember' 已定义但从未使用。
建议: 删除该类,或在使用处实例化。
- 🔴 `eslint:no-dupe-class-members` L77
L77: 重复的成员名 'method'。
建议: 删除重复的方法定义,或改为不同方法名。
- 🔴 `eslint:no-unused-vars` L83
L83: 'EmptyStatic' 已定义但从未使用。
建议: 删除该类,或在使用处实例化。
- 🔴 `eslint:no-empty-static-block` L84
L84: 不应有空的 static 块。
建议: 删除空的 static 块,或在其中编写有意义的初始化逻辑。
- 🔴 `eslint:no-unused-vars` L88
L88: 'SetterReturn' 已定义但从未使用。
建议: 删除该类,或在使用处实例化。
- 🔴 `eslint:no-setter-return` L91
L91: setter 不能返回值。
建议: 删除 setter 中的 return 语句。
- 🔴 `eslint:no-unused-vars` L96
L96: 'GetterNoReturn' 已定义但从未使用。
建议: 删除该类,或在使用处实例化。
- 🔴 `eslint:getter-return` L97
L97: getter 'value' 应返回值。
建议: 在 getter 中返回一个有意义的表达式。
- 🔴 `eslint:no-unused-vars` L98
L98: 'x' 被赋值但从未使用。
建议: 删除 x 声明,或在实际使用后返回它。
- 🔴 `eslint:no-unused-vars` L103
L103: 'ThisBeforeSuper' 已定义但从未使用。
建议: 删除该类,或在使用处实例化。
- 🔴 `eslint:no-this-before-super` L105
L105: 在调用 super() 之前不允许使用 this。
建议: 将 super(message) 移到 this.message 赋值之前。
- 🔴 `eslint:no-unused-vars` L111
L111: 'UnusedPrivate' 已定义但从未使用。
建议: 删除该类,或在使用处实例化。
- 🔴 `eslint:no-unused-private-class-members` L112
L112: 私有成员 '#secret' 已定义但从未使用。
建议: 删除 #secret 定义,或在类方法中实际使用它。
- 🔴 `eslint:no-unused-vars` L123
L123: 'looseEquality' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:eqeqeq` L124
L124: 应使用 '===' 而非 '=='。
建议: 将 == 改为 ===,避免隐式类型转换。
- 🔴 `eslint:no-unused-vars` L128
L128: 'nullEquality' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-eq-null` L129
L129: 与 null 比较时应使用 '==='。
建议: 如果只想匹配 null,应使用 x === null;如需要 null/undefined 都匹配,可显式写成 x === null || x === undefined。
- 🔴 `eslint:eqeqeq` L129
L129: 应使用 '===' 而非 '=='。
建议: 将 == 改为 ===,避免隐式类型转换。
- 🔴 `eslint:no-unused-vars` L133
L133: 'selfCompare' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-self-compare` L134
L134: 与自身比较可能没有意义。
建议: 删除该比较;若想检测 NaN 应使用 Number.isNaN(x)。
- 🔴 `eslint:no-unused-vars` L138
L138: 'negZero' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-compare-neg-zero` L139
L139: 不要使用 '===' 与 -0 比较。
建议: 如果需要判断 -0,请使用 Object.is(x, -0)。
- 🔴 `eslint:no-unused-vars` L143
L143: 'isNanWrong' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:use-isnan` L144
L144: 与 NaN 比较时应使用 isNaN 函数。
建议: 使用 Number.isNaN(x) 进行 NaN 判断。
- 🔴 `eslint:no-unused-vars` L148
L148: 'typeofCheck' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:valid-typeof` L149
L149: typeof 比较值无效。
建议: 将 'strnig' 修正为 'string'。
- 🔴 `eslint:no-unused-vars` L153
L153: 'constBinary' 已定义但从未使用。
建议: 删除该变量,或在使用处引用它。
- 🔴 `eslint:no-unused-vars` L171
L171: 'fallthrough' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-undef` L174
L174: 未定义变量 'console'。
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
- 🔴 `eslint:no-fallthrough` L175
L175: 在 'case' 之前应有 'break' 语句。
建议: 在 case 1 的语句后添加 break,除非刻意需要穿透。
- 🔴 `eslint:no-undef` L176
L176: 未定义变量 'console'。
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
- 🔴 `eslint:no-unused-vars` L184
L184: 'caseDeclarations' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-case-declarations` L187
L187: case 块中不应有词法声明。
建议: 为 case 1 添加块级大括号 {},或把 let 声明移到 switch 外。
- 🔴 `eslint:no-undef` L188
L188: 未定义变量 'console'。
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
- 🔴 `eslint:no-unused-vars` L196
L196: 'duplicateCase' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-duplicate-case` L200
L200: 重复的 case 标签。
建议: 将第二个 case 1 改为其他值,或删除重复分支。
- 🔴 `eslint:no-unused-vars` L208
L208: 'dupeElseIf' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-dupe-else-if` L211
L211: 此分支永远无法执行,其条件与前序 if-else-if 条件重复或已被覆盖。
建议: 删除重复的 else if 分支,或改为不同的条件。
- 🔴 `eslint:no-unused-vars` L218
L218: 'noDefault' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:default-case` L219
L219: 应包含 default 分支。
建议: 在 switch 中添加 default 分支,处理未匹配的情况。
- 🔴 `eslint:no-unused-vars` L227
L227: 'defaultNotLast' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:default-case-last` L229
L229: default 分支应放在最后。
建议: 将 default 分支移到 switch 的最后一个分支。
- 🔴 `eslint:no-func-assign` L246
L246: 'assignableFn' 是函数,不能重新赋值。
建议: 删除对函数名的重新赋值,或将新函数赋值给其他变量名。
- 🔴 `eslint:no-unused-vars` L246
L246: 'assignableFn' 被赋值但从未使用。
建议: 删除该函数定义或在使用处调用它。
- 🔴 `eslint:no-unused-vars` L256
L256: 'forDirection' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:for-direction` L257
L257: 循环更新子句使变量向错误方向移动。
建议: 将 i-- 改为 i++,使循环条件 i < 10 最终为 false。
- 🔴 `eslint:no-undef` L258
L258: 未定义变量 'console'。
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
- 🔴 `eslint:no-unused-vars` L267
L267: 'my_snake_case_var' 被赋值但从未使用。
建议: 删除该变量,或在使用处引用它。
- 🔴 `eslint:no-unused-vars` L270
L270: 'generated' 被赋值但从未使用。
建议: 删除该变量,或在使用处引用它。
- 🔴 `eslint:no-undef` L270
L270: 未定义变量 'object'。
建议: 检查全局是否存在 object 构造函数;更推荐使用 {} 创建空对象。
- 🔴 `eslint:no-unused-vars` L273
L273: 'uselessConcat' 被赋值但从未使用。
建议: 删除该变量,或在使用处引用它。
- 🔴 `eslint:no-unused-vars` L276
L276: 'obj' 被赋值但从未使用。
建议: 删除该变量,或在使用处引用它。
- 🔴 `eslint:no-unused-vars` L279
L279: 'dupeKeyObj' 被赋值但从未使用。
建议: 删除该变量,或在使用处引用它。
- 🔴 `eslint:no-dupe-keys` L279
L279: 属性名 'a' 重复。
建议: 删除重复的 a 属性,保留最终需要的值。
- 🔴 `eslint:no-unused-vars` L282
L282: 'a' 被赋值但从未使用。
建议: 删除该解构赋值,或在使用处引用 a。
- 🔴 `eslint:no-unused-vars` L287
L287: 'preferTemplate' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-unused-vars` L292
L292: 'preferSpreadStyle' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-unused-vars` L298
L298: 'preferRest' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-unused-vars` L307
L307: 'preferSpreadCall' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-unused-vars` L313
L313: 'hasOwnPrefer' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-shadow` L313
L313: 'obj' 与第 276 行第 7 列的上层作用域中的 'obj' 重名。
建议: 将参数改名为 target 或 sourceObj,避免遮蔽外层变量。
- 🔴 `eslint:no-unused-vars` L318
L318: 'preferConstDemo' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-useless-assignment` L323
L323: 此赋值在后续语句中未被使用。
建议: 删除 someObj 声明,或在实际使用后保留。
- 🔴 `eslint:no-unused-vars` L334
L334: 'extendNative' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-extend-native` L335
L335: String 原型是只读的,不应添加属性。
建议: 不要修改原生原型;如需扩展功能,应使用包装函数或 WeakMap。
- 🔴 `eslint:no-unused-vars` L339
L339: 'evalDemo' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-eval` L340
L340: eval 可能存在安全风险。
建议: 避免使用 eval;如果只是解析 JSON,请用 JSON.parse。
- 🔴 `eslint:no-unused-vars` L344
L344: 'paramReassign' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-unused-vars` L350
L350: 'unmodifiedLoop' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-unmodified-loop-condition` L351
L351: 循环中 'i' 未被修改。
建议: 在循环体内修改 i,否则该循环可能无限执行。
- 🔴 `eslint:no-undef` L352
L352: 未定义变量 'console'。
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
- 🔴 `eslint:no-unused-vars` L357
L357: 'unreachableLoop' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-unreachable-loop` L359
L359: 无效循环,其循环体只能执行一次。
建议: 删除循环或移除循环体中的 break。
- 🔴 `eslint:no-unused-vars` L359
L359: 'item' 被赋值但从未使用。
建议: 若不需要 item,改用索引循环或 for...of 中实际使用 item。
- 🔴 `eslint:no-unreachable-loop` L362
L362: 无效循环,其循环体只能执行一次。
建议: 将 while(true) 改为 if(true),或移除 break 并重写逻辑。
- 🔴 `eslint:no-unused-vars` L376
L376: 'badRegex' 被赋值但从未使用。
建议: 删除该变量,或在使用处引用它。
- 🔴 `eslint:no-invalid-regexp` L376
L376: 无效正则表达式:/[/: 未结束的字符类。
建议: 如果要匹配左括号,应使用 new RegExp('\\[')。
- 🔴 `eslint:no-unused-vars` L379
L379: 'regexSpaces' 被赋值但从未使用。
建议: 删除该变量,或在使用处引用它。
- 🔴 `eslint:no-regex-spaces` L379
L379: 空格难以计数,请使用 {2}。
建议: 将连续空格改为 {2} 量词。
- 🔴 `eslint:no-unused-vars` L382
L382: 'controlRegex' 被赋值但从未使用。
建议: 删除该变量,或在使用处引用它。
- 🔴 `eslint:no-control-regex` L382
L382: 正则表达式中出现意外的控制字符:\x1f。
建议: 如果确实要匹配控制字符,请添加注释并临时禁用该规则;否则应移除该正则。
- 🔴 `eslint:no-unused-vars` L385
L385: 'emptyCharClass' 被赋值但从未使用。
建议: 删除该变量,或在使用处引用它。
- 🔴 `eslint:no-empty-character-class` L385
L385: 空字符类。
建议: 删除空的 [],或写入实际需要的字符。
- 🔴 `eslint:no-unused-vars` L388
L388: 'misleadingCharClass' 被赋值但从未使用。
建议: 删除该变量,或在使用处引用它。
- 🔴 `eslint:no-misleading-character-class` L388
L388: 字符类中出现意外的代理对,请使用 'u' 标志。
建议: 为正则添加 u 标志。
- 🔴 `eslint:no-misleading-character-class` L388
L388: 字符类中出现意外的代理对,请使用 'u' 标志。
建议: 为正则添加 u 标志。
- 🔴 `eslint:no-unused-vars` L391
L391: 'uselessBackref' 被赋值但从未使用。
建议: 删除该变量,或在使用处引用它。
- 🔴 `eslint:no-useless-backreference` L391
L391: 反向引用 '\1' 将被忽略,它引用了自身所在的组 '(\1a)'。
建议: 删除无意义的反向引用,或重写分组。
- 🔴 `eslint:no-unused-vars` L394
L394: 'uselessEscape' 被赋值但从未使用。
建议: 删除该变量,或在使用处引用它。
- 🔴 `eslint:no-useless-escape` L394
L394: 多余的转义字符:\.。
建议: 删除多余的转义,写成 'line.'。
- 🔴 `eslint:no-unused-vars` L397
L397: 'irregularWhitespace' 被赋值但从未使用。
建议: 删除该变量,或在使用处引用它。
- 🔴 `eslint:no-loss-of-precision` L400
L400: 此数字字面量在运行时将丢失精度。
建议: 使用可安全表示的浮点字面量,或改用 BigInt/字符串处理高精度数值。
- 🔴 `eslint:no-sparse-arrays` L403
L403: 数组中不应出现空槽位。
建议: 用 undefined 显式填充空位,或删除该槽位。
- 🔴 `eslint:no-unused-vars` L406
L406: 'emptyFunctionBody' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-empty` L409
L409: 空块语句。
建议: 在 if 块中填充逻辑,或删除空的 if 语句。
- 🔴 `eslint:no-unused-vars` L413
L413: 'constantBinary' 被赋值但从未使用。
建议: 删除该变量,或在使用处引用它。
- 🔴 `eslint:no-constant-binary-expression` L413
L413: '&&' 表达式的左侧出现意外的常量真值。
建议: 移除恒真左侧,或改写为普通值。
- 🔴 `eslint:no-constant-condition` L416
L416: 意外的常量条件。
建议: 删除固定的 if(true),或改为真实条件。
- 🔴 `eslint:no-undef` L417
L417: 未定义变量 'console'。
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
- 🔴 `eslint:no-cond-assign` L422
L422: 应是条件表达式,而不是赋值。
建议: 将 = 改为 ===,或者把赋值移到 if 之前。
- 🔴 `eslint:no-constant-condition` L422
L422: 意外的常量条件。
建议: 修正条件赋值后,条件将不再是常量。
- 🔴 `eslint:no-unused-vars` L422
L422: 'condVar' 被赋值但从未使用。
建议: 删除该变量,或在实际条件中使用它。
- 🔴 `eslint:no-undef` L423
L423: 未定义变量 'console'。
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
- 🔴 `eslint:no-unused-vars` L428
L428: 'selfAssigned' 被赋值但从未使用。
建议: 删除该变量,或在使用处引用它。
- 🔴 `eslint:no-useless-assignment` L428
L428: 此赋值在后续语句中未被使用。
建议: 删除自赋值语句。
- 🔴 `eslint:no-self-assign` L428
L428: 'selfAssigned' 被赋值为自身。
建议: 删除该无意义的自赋值。
- 🔴 `eslint:no-debugger` L431
L431: 不应使用 'debugger' 语句。
建议: 删除 debugger 语句。
- 🔴 `eslint:no-extra-boolean-cast` L434
L434: 多余的双重否定。
建议: 移除 !!,直接使用变量作为条件。
- 🔴 `eslint:no-undef` L435
L435: 未定义变量 'console'。
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
- 🔴 `eslint:no-ex-assign` L442
L442: 不应给 catch 异常参数赋值。
建议: 不要重新赋值 e;如需重新抛出,请直接 throw e 或 throw new Error(..., { cause: e })。
- 🔴 `eslint:no-unused-vars` L442
L442: 'e' 被赋值但从未使用。
建议: 删除赋值;如果需要新错误,直接抛出而不是赋值给 e。
- 🔴 `eslint:no-unused-vars` L446
L446: 'noEmptyPattern' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-empty-pattern` L446
L446: 不应使用空对象解构模式。
建议: 在解构模式中写出至少一个属性名,或直接使用普通参数。
- 🔴 `eslint:no-unused-vars` L451
L451: 'unreachableCode' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-unreachable` L453
L453: 不可达代码。
建议: 删除 return 之后永远不会执行的语句。
- 🔴 `eslint:no-undef` L453
L453: 未定义变量 'console'。
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
- 🔴 `eslint:no-unused-vars` L457
L457: 'unexpectedMultiline' 被赋值但从未使用。
建议: 删除该变量,或在使用处引用它。
- 🔴 `eslint:no-unexpected-multiline` L458
L458: 函数与函数调用左括号之间存在意外换行。
建议: 在前一行末尾补充分号,或将 IIFE 整体改成赋值表达式,避免被解释为对上文变量的调用。
- 🔴 `eslint:no-undef` L459
L459: 未定义变量 'console'。
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
- 🔴 `eslint:no-unused-vars` L463
L463: 'unsafeOptional' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-shadow` L463
L463: 'obj' 与第 276 行第 7 列的上层作用域中的 'obj' 重名。
建议: 将参数改名为 target 或 sourceObj,避免遮蔽外层变量。
- 🔴 `eslint:no-unsafe-optional-chaining` L464
L464: 可选链使用不安全;如果短路为 'undefined',后续运算会抛 TypeError。
建议: 将后续属性访问也改为可选链,或先判断 obj?.a 是否存在。
- 🔴 `eslint:no-unsafe-negation` L470
L470: 不应取反 'in' 运算符的左操作数。
建议: 将整个 in 表达式放入括号后再取反,例如 !(flagFlag in holderObj)。
- 🔴 `eslint:no-undef` L471
L471: 未定义变量 'console'。
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
- 🔴 `eslint:no-unused-vars` L475
L475: 'unsafeFinally' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-unsafe-finally` L479
L479: 在 finally 中使用返回语句不安全。
建议: 不要在 finally 中 return,否则会覆盖 try 中的 throw 或 return。
- 🔴 `eslint:no-unused-labels` L484
L484: 标签 'unusedLabel:' 已定义但从未使用。
建议: 移除未使用的标签。
- 🔴 `eslint:no-undef` L485
L485: 未定义变量 'console'。
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
- 🔴 `eslint:no-unused-vars` L489
L489: 'completelyUnusedVariable' 被赋值但从未使用。
建议: 删除该变量,或在使用处引用它。
- 🔴 `eslint:no-unused-vars` L492
L492: 'uselessCatch' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-useless-catch` L493
L493: 不必要的 try/catch 包装。
建议: 删除 try/catch,直接返回 try 中的值。
- 🔴 `eslint:no-unreachable` L495
L495: 不可达代码。
建议: 删除 catch 中被 no-useless-catch 覆盖后不再可达的 throw 逻辑。
- 🔴 `eslint:no-unused-vars` L514
L514: 'irregularWS' 被赋值但从未使用。
建议: 删除该变量,或在使用处引用它。
- 🔴 `eslint:no-irregular-whitespace` L514
L514: 不允许使用不规则空白。
建议: 将行尾的不可见空白(如 NBSP)替换为普通空格或删除。
- 🔴 `eslint:no-var` L521
L521: 不应使用 var,请改用 let 或 const。
建议: 改用 const,若需要重新赋值则改用 let。
- 🔴 `eslint:no-unused-vars` L521
L521: 'oldStyleVar' 被赋值但从未使用。
建议: 删除该变量,或在使用处引用它。
- 🔴 `eslint:no-undef` L524
L524: 未定义变量 'console'。
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
- 🔴 `eslint:no-undef` L524
L524: 未定义变量 'undefinedGlobalThing'。
建议: 检查该全局变量是否真实存在;若存在,请添加全局声明。
- 🔴 `eslint:no-unused-vars` L527
L527: 'redeclareDemo' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-var` L528
L528: 不应使用 var,请改用 let 或 const。
建议: 改用 let 或 const。
- 🔴 `eslint:no-var` L529
L529: 不应使用 var,请改用 let 或 const。
建议: 删除重复声明,并保留一次用 let/const 的声明。
- 🔴 `eslint:no-redeclare` L529
L529: 'redeclaredName' 已定义。
建议: 删除重复的 var 声明,只在函数中声明一次。
- 🔴 `eslint:no-unused-vars` L529
L529: 'redeclaredName' 被赋值但从未使用。
建议: 保留一次声明并在使用处引用,或删除整个演示函数。
- 🔴 `eslint:no-unused-vars` L539
L539: 'deleteVarDemo' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-shadow` L540
L540: 'obj' 与第 276 行第 7 列的上层作用域中的 'obj' 重名。
建议: 将局部变量 obj 改名为 targetObj 或 instance。
- 🔴 `eslint:no-unused-vars` L545
L545: 'shadowRestricted' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-shadow-restricted-names` L545
L545: 不允许遮蔽全局属性 'undefined'。
建议: 将参数 undefined 改名为 other 或 anyValue。
- 🔴 `eslint:no-unused-vars` L550
L550: 'globalAssign' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-global-assign` L551
L551: 只读全局变量 'Math' 不应被修改。
建议: 不要给 Math 赋值;如果需要自定义数学方法,请使用新的命名空间。
- 🔴 `eslint:no-unused-vars` L558
L558: 'blockScopedDemo' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-constant-condition` L559
L559: 意外的常量条件。
建议: 移除 if(true),或改为基于真实条件。
- 🔴 `eslint:no-var` L560
L560: 不应使用 var,请改用 let 或 const。
建议: 在函数顶部改用 let/const 声明,并确保块级语义正确。
- 🔴 `eslint:no-undef` L562
L562: 未定义变量 'console'。
建议: 在 ESLint 配置中声明浏览器或 Node 环境,或在文件顶部添加 /* global console */。
- 🔴 `eslint:block-scoped-var` L562
L562: 在第 560 行声明的 'blockScoped' 在绑定上下文之外使用。
建议: 将 var 移到函数顶部,或改用 let/const 并确保作用域一致。
- 🔴 `eslint:no-unused-vars` L566
L566: 'unassignedVarDemo' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-unassigned-vars` L567
L567: 'unassignedVariable' 因从未赋值而始终为 'undefined'。
建议: 初始化变量,或检查是否真的需要该变量。
- 🔴 `eslint:no-unused-vars` L572
L572: 'shadowDemo' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-unused-vars` L573
L573: 'value' 被赋值但从未使用。
建议: 删除该变量,或在使用处引用它。
- 🔴 `eslint:no-shadow` L575
L575: 'value' 与第 573 行第 7 列的上层作用域中的 'value' 重名。
建议: 将内层变量改名为 innerValue。
- 🔴 `eslint:no-unused-vars` L582
L582: 'uselessAssignment' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-useless-assignment` L583
L583: 此赋值在后续语句中未被使用。
建议: 删除初始赋值,只保留最终赋值。
- 🔴 `eslint:no-unused-vars` L589
L589: 'arrayViaConstructor' 被赋值但从未使用。
建议: 删除该变量,或在使用处引用它。
- 🔴 `eslint:no-array-constructor` L589
L589: 应优先使用数组字面量 []。
建议: 改用 [1, 2, 3]。
- 🔴 `eslint:no-unused-vars` L592
L592: 'symbolViaNew' 被赋值但从未使用。
建议: 删除该变量,或在使用处引用它。
- 🔴 `eslint:no-new-native-nonconstructor` L592
L592: 'Symbol' 不能作为构造函数调用。
建议: 移除 new,直接调用 Symbol('x')。
- 🔴 `eslint:no-unused-vars` L595
L595: 'jsonAsFunction' 被赋值但从未使用。
建议: 删除该变量,或在使用处引用它。
- 🔴 `eslint:no-obj-calls` L595
L595: 'JSON' 不是函数。
建议: 不要直接调用 JSON();如需解析 JSON 字符串,应使用 JSON.parse()。
- 🔴 `eslint:no-const-assign` L599
L599: 'immutableValue' 是常量,不能重新赋值。
建议: 改用 let,或删除赋值。
- 🔴 `eslint:no-unused-vars` L599
L599: 'immutableValue' 被赋值但从未使用。
建议: 删除该变量,或在使用处引用它。
- 🔴 `eslint:no-unused-vars` L602
L602: 'protoBuilds' 已定义但从未使用。
建议: 删除该函数,或在使用处调用它。
- 🔴 `eslint:no-shadow` L602
L602: 'obj' 与第 276 行第 7 列的上层作用域中的 'obj' 重名。
建议: 将参数改名为 target 或 sourceObj,避免遮蔽外层变量。
- 🔴 `eslint:no-prototype-builtins` L603
L603: 不应从目标对象上访问 Object.prototype 的方法 'hasOwnProperty'。
建议: 使用 Object.hasOwn(obj, 'key') 或 Object.prototype.hasOwnProperty.call(obj, 'key')。
- 🟡 `eslint:no-await-in-loop` L30
L30: 循环内不应使用 await。
建议: 若请求之间无依赖,应使用 Promise.all 并行执行;若有依赖,至少在循环外积累 Promise。
- 🟡 `eslint:no-useless-return` L50
L50: 不必要的 return 语句。
建议: 删除函数末尾无意义的 return。
- 🟡 `eslint:prefer-template` L154
L154: 不应使用字符串拼接。
建议: 改用模板字符串或直接写出拼接结果;注意当前表达式结果可能是 'aNaN'。
- 🟡 `eslint:camelcase` L267
L267: 标识符 'my_snake_case_var' 不是驼峰命名。
建议: 改为驼峰命名,如 mySnakeCaseVar。
- 🟡 `eslint:new-cap` L270
L270: 构造函数名不应以小写字母开头。
建议: 如果意图是创建对象,应使用 new Object() 或对象字面量 {}。
- 🟡 `eslint:no-useless-concat` L273
L273: 不应拼接字符串字面量。
建议: 直接写 'foobar'。
- 🟡 `eslint:no-useless-computed-key` L276
L276: 属性 ['key'] 的计算属性是不必要的。
建议: 直接写成 key: 'value'。
- 🟡 `eslint:no-useless-rename` L282
L282: 解构赋值 a 被无必要地重命名。
建议: 简化为 { a }。注意 someObj 当前在 L282 之后才声明,会导致运行时错误。
- 🟡 `eslint:prefer-template` L288
L288: 不应使用字符串拼接。
建议: 改用模板字符串。
- 🟡 `eslint:prefer-object-spread` L293
L293: 应使用对象展开代替 Object.assign,例如 `{ ...foo }`
建议: 改用对象字面量展开语法。
- 🟡 `eslint:prefer-rest-params` L299
L299: 应使用剩余参数代替 'arguments'。
建议: 将函数签名改为 function preferRest(...args),并直接返回 args。
- 🟡 `eslint:prefer-spread` L309
L309: 应使用展开运算符代替 '.apply()'。
建议: 使用 spreadThis(...nums)。
- 🟡 `eslint:prefer-object-has-own` L314
L314: 应使用 Object.hasOwn() 代替 Object.prototype.hasOwnProperty.call()。
建议: 改用 Object.hasOwn(obj, key)。
- 🟡 `eslint:no-param-reassign` L345
L345: 不应重新赋值函数参数 'x'。
建议: 改用局部变量保存计算结果。
- 🟡 `eslint:new-cap` L595
L595: 以大写字母开头的函数只应作为构造函数使用。
建议: JSON 不是构造函数,也不可调用;使用 JSON.parse 等静态方法。
AI 审查 · 4 条建议
- 🔴 [AI] [design] `top-level-fatal-expression` L270
**顶层存在多个必然抛出异常的表达式,会导致脚本加载中断**
合并文件在模块初始化阶段依次执行大量演示代码,包括 `new object()`(若 object 未定义会抛 ReferenceError)、`new RegExp('[')`(抛 SyntaxError)、`new Symbol('x')`(抛 TypeError)、`JSON()`(抛 TypeError)、`immutableValue = 2`(抛 TypeError)等。这些不是普通 lint 风格问题,而是脚本加载即崩溃的致命缺陷。即使静态分析逐条标记了相关规则,文件的“可运行性”仍无法保证。
建议: 将演示/规则触发代码放入独立函数或测试用例中,避免在模块顶层直接执行会抛异常的字面量表达式;必要时用 try/catch 包住这些演示片段。
- 🔴 [AI] [bug] `use-before-define` L282
**someObj 在声明前使用导致暂时性死区错误**
L282 的 `const { a: a } = someObj;` 引用了 L323 才声明的 `const someObj`。const 声明存在暂时性死区,代码执行到 L282 时会抛出 ReferenceError,导致合并脚本在后续逻辑执行前中断。
建议: 将 `const someObj = ...` 移到 L282 之前,或在 L282 直接使用对象字面量,避免引用未初始化的变量。
- 🟡 [AI] [bug] `string-coercion-bug` L154
**字符串拼接 `-'b'` 产生 'aNaN',很可能不是预期结果**
L154 的 `const y = 'a' + -'b';``-'b'` 的结果是 NaN,字符串拼接后得到 'aNaN'。若意图是拼接两个字符串,应直接写 'ab';若意图是数学运算,则操作数不应为字符串。
建议: 根据意图改写为 `const y = 'ab';``const y = 'a' + 'b';`
- 🟡 [AI] [bug] `exception-swallowed` L442
**catch 参数被重新赋值,原始异常被静默吞掉**
L442 的 `e = new Error('reassigned');` 只是改变 catch 局部变量 e 的引用,原始异常没有被记录也没有被重新抛出,这段 catch 块的语义很可能是错误的:异常在无人知晓的情况下被吞掉。
建议: 如需记录并重新抛出,应使用 `console.error(e); throw new Error('reassigned', { cause: e });`;如果只是演示重抛,应该写 `throw e;`
@@ -0,0 +1,119 @@
# demo-eslint 插件实测覆盖率报告
> **工程**`demo-eslint`(纯内置配置形态:无 `eslint.config.*`
> **测试对象**:插件 ESLint 适配器(引擎 eslint 9.39.4 + @eslint/js 9.39.4 + typescript-eslint 8.60.1
> **实测日期**2026-08-25
> **比对基线**:插件同版本依赖复现的模拟基线(比对键 = 规则 + 行号 + 严重级别)
---
## 一、核心结论
| 指标 | 结果 | 状态 |
|---|---|---|
| 插件行为一致性(vs 模拟基线) | **292/292 条逐条一致,零偏差** | ✅ 100% |
| 规则覆盖率(真实规则口径) | **126/126 种(100%** | ✅ 全覆盖 |
| 解析错误 | **0 处** | ✅ |
| 诊断总数 | 292 条静态(error 268 / warning 24 | — |
**实测完全达成全覆盖基准**:插件在「工程无配置文件 → 回退内置默认配置」路径下,输出的 292 条静态诊断与模拟基线逐条一致,126 种内置规则全部触发、零解析错误。**ESLint 适配器静态分析链路(规则加载、解析、诊断映射)验证通过。**
---
## 二、逐文件比对(实测 vs 基线)
| 文件 | 实测 | 基线 | 一致性 | 说明 |
|---|---:|---:|---|---|
| `common.js` | 210 | 210 | ✅ 逐条一致 | core ESLint 规则主样例文件 |
| `typescript.ts` | 65 | 65 | ✅ 逐条一致 | `@typescript-eslint/` 规则主样例文件 |
| `legacysyntax.cjs` | 14 | 14 | ✅ 逐条一致 | 旧式(宽松模式)语法规则样例文件 |
| `esm-demo.js` | 3 | 3 | ✅ 逐条一致 | ESM 相关规则样例文件 |
| **合计** | **292** | **292** | **✅ 零偏差** | — |
> 比对方法:将插件报告中的每条静态诊断(规则名 + 行号 + 严重级别)与模拟基线做多重集合比对;四文件全部 `missing = 0`、`extra = 0`。
---
## 三、全覆盖的达成机制
126 种内置规则中,有 6 条规则无法在常规 `.js`(module 模式)样例中触发,通过 `legacysyntax.cjs` 单文件解决,机制如下:
1. **5 条宽松模式规则**`no-nonoctal-decimal-escape``no-octal``no-with``no-delete-var``no-dupe-args`):触发代码(`'\8'` 转义、八进制 `010``with``delete` 变量、重复参数)在严格模式下本身是 SyntaxError,结构上只可能在宽松(sloppy)模式触发。`.cjs` 文件被 flat config 默认按 `sourceType: "commonjs"`(宽松模式)解析,无需任何配置文件。
2. **`preserve-caught-error`**eslint 9.39.4 引擎已实现该规则,但 @eslint/js 9.39.4 的 recommended 未启用(10.0.1 才收录)。通过文件首行 `/* eslint preserve-caught-error: "error" */` 行内注释启用。
### 6 条规则验证明细(`legacysyntax.cjs`
| 规则 | 实测行号 | 严重级别 | 状态 |
|---|---|---|---|
| `no-nonoctal-decimal-escape` | L8(×2 | error | ✅ 触发 |
| `no-octal` | L11 | error | ✅ 触发 |
| `no-with` | L16 | error | ✅ 触发 |
| `no-delete-var` | L22 | error | ✅ 触发 |
| `no-dupe-args` | L25 | error | ✅ 触发 |
| `preserve-caught-error` | L33 | error | ✅ 触发(行内注释) |
同文件连带触发的其他规则(与基线一致):`no-undef` L17(×3/ L36、`no-var` L21、`no-redeclare` L25、`no-unused-vars` L32。
---
## 四、诊断分布统计
**按严重级别**(静态 292 条):
| 级别 | 数量 | 占比 |
|---|---:|---:|
| error(🔴) | 268 | 91.8% |
| warning(🟡) | 24 | 8.2% |
**按规则类别**126 种):
| 类别 | 种数 | 说明 |
|---|---:|---|
| core ESLint 规则 | 94 | @eslint/js 9.39.4 recommended + 插件精选规则 |
| `@typescript-eslint/` 规则 | 32 | ts.configs.recommended + 插件精选规则 |
**按文件**`common.js` 210 · `typescript.ts` 65 · `legacysyntax.cjs` 14 · `esm-demo.js` 3。
> 注:插件报告中另有 AI 审查建议 12 条(common.js 4 / legacysyntax.cjs 4 / esm-demo.js 2 / typescript.ts 2),属 AI 增强能力,不计入静态规则覆盖口径。
---
## 五、实测环境判定
实测报告的文件路径与预期部署一致:
- `data\demo-eslint\src\legacysyntax.cjs`14 条诊断、无 parse-error → **`.cjs` 宽松模式解析生效,确认插件未发现任何工程配置、正确回退内置配置**)
- `data\demo-eslint\src\`common.js / esm-demo.js / typescript.ts,诊断数与基线完全相同)
**配置路径验证**:工程不含任何 `eslint.config.*``.eslintrc*`,插件按设计走「内置默认配置回退」路径。该路径与工作区打开方式无关(打开工程根或外层目录结果一致),292/292 逐条一致即为最终确认。
---
## 六、数据口径说明
| 项 | 说明 |
|---|---|
| 覆盖率口径 | 126 种 = 插件内置配置实际生效 125 种(@eslint/js 9.39.4 recommended + 精选规则)+ `preserve-caught-error`(由 `.cjs` 首行行内注释启用);纯规则口径,不含 parse-error |
| 比对引擎 | eslint 9.39.4 + @eslint/js 9.39.4 + typescript-eslint 8.60.1(与插件 `package-lock.json` 逐字一致) |
| 基线数据 | `/data/user/work/results/round2-verify.json`(本轮比对明细) |
| 已知口径差异 | 工程自检脚本 `run-coverage.mjs`(用工程自带 @eslint/js 10.0.1)计 294 条:多出的 1 条为 `typescript.ts` L189 `preserve-caught-error`10.0.1 的 recommended 启用该规则、插件 9.39.4 内置配置未启用),属引擎版本口径差异,非插件缺陷 |
| 版本备注 | 测试机 `typescript.ts` 为 202 行版本(65 条);当前版(212 行)多 1 条 `@typescript-eslint/no-unused-vars` L188,同步与否均不影响 126 种全覆盖结论 |
---
## 七、结论与后续
**demo-eslint 工程的插件实测验证完成**
1. ✅ 插件行为零偏差(292/292 逐条一致)
2. ✅ 内置配置全覆盖达成(126/126 种规则,零解析错误)
3. ✅ 「内置配置回退」路径完整验证(工程无配置文件的场景)
**后续建议**(如需继续扩展验证):
- **其他 3 个工程复测**`demo-sqlfluff``demo-pmd``demo-stylelint` 按同样方法(先本地模拟基线 → 实测比对),其中 PMD 注意 Java 版本与 ruleset、SQLFluff 注意 dialect 与 AL06 样例、Stylelint 注意配置优先级。
- **「工程配置优先」路径补验**(可选):若后续想验证插件发现工程配置的行为,可在工程根添加 `eslint.config.mjs` 并以工程根为第一个工作区文件夹打开,预期插件改用工程配置(诊断会有差异,属正常)。
---
*报告生成于 2026-08-25 · demo-eslint 插件实测 · 比对脚本:`round2-compare.mjs` · 数据:`round2-verify.json`*
@@ -0,0 +1,33 @@
# 代码审查报告
**文件:** `data\demo-eslint\src\esm-demo.js`
**语言:** javascript
**耗时:** 64.7s
**分析工具:** eslint
---
总计: 5 | 错误: 3 | 警告: 1 | 建议: 1
静态分析 · 3 个问题
- 🔴 `eslint:no-unused-vars` L7
第7行的 'reassignImport' 已定义但从未使用。
建议: 该函数未被调用,应删除;如果后续可能使用,请在代码中调用它。建议直接删除整个函数,避免死代码。
- 🔴 `eslint:no-import-assign` L8
第8行 'fs' 是只读的,不能为其赋值。
建议: import 导入的绑定是只读的,重新赋值会引发运行时错误。如果函数需要操作 fs,请使用另一个变量保存导入的对象;或删除该赋值语句。由于函数本身未使用,建议删除整个函数。
- 🔴 `eslint:no-undef` L11
第11行 'console' 未定义。
建议: console 在浏览器和 Node.js 中通常是全局对象,但 ESLint 环境未配置。请在 .eslintrc 中设置 env.node 或 env.browser,或在代码顶部添加 /* global console */ 注释。或者改用 globalThis.console.log(fs)。
AI 审查 · 2 条建议
- 🟡 [AI] [design] `dead-code-with-runtime-error` L7
**死代码中隐藏运行时错误**
reassignImport 函数为 import 绑定 fs 赋值,在 ESM 中这是非法操作,一旦调用会立即抛出 TypeError。虽然当前函数未使用,但保留这种死代码会误导后续维护者,并可能被误调用导致崩溃。
建议: 删除整个函数 reassignImport,或将其重构为合法操作(例如读取 fs 的属性)。
- 🔵 [AI] [design] `module-top-level-side-effect` L11
**模块顶层执行 console.log 产生副作用**
第 11 行的 console.log 在模块被导入时立即执行。如果这是被复用的模块,会导致控制台输出,污染调用方,且难以测试。
建议: 将 console.log 移到函数内部或通过导出接口提供;若仅是调试用途,在提交前移除。
@@ -0,0 +1,74 @@
# 代码审查报告
**文件:** `data\demo-eslint\src\legacysyntax.cjs`
**语言:** javascript
**耗时:** 152.1s
**分析工具:** eslint
---
总计: 18 | 错误: 14 | 警告: 2 | 建议: 2
静态分析 · 14 个问题
- 🔴 `eslint:no-nonoctal-decimal-escape` L8
不要使用 '\8' 转义序列。
建议: 在非严格模式字符串中 `\8` 会被静默当作普通字符 `8`。如果只是要字符 8,请直接写 `'8 and 9'`;如果要表示反斜杠加数字,请使用 `String.raw` 或双反斜杠转义。
- 🔴 `eslint:no-nonoctal-decimal-escape` L8
不要使用 '\9' 转义序列。
建议: 在非严格模式字符串中 `\9` 会被静默当作普通字符 `9`。如果只是要字符 9,请直接写 `'8 and 9'`;如果要表示反斜杠加数字,请使用 `String.raw` 或双反斜杠转义。
- 🔴 `eslint:no-octal` L11
不应使用旧式八进制字面量。
建议: `010` 是旧式八进制字面量,值为十进制 8,且在严格模式下是语法错误。若要表示八进制值 8,应写 `0o10`;若要表示十进制 10,应写 `10`
- 🔴 `eslint:no-with` L16
不应使用 `with` 语句。
建议: `with` 会动态修改作用域链,难以静态分析和优化,在严格模式或 ESM 中还是语法错误。建议先解构出所需属性:`const { pi, e } = { pi: 3.14, e: 2.72 };` 再使用。
- 🔴 `eslint:no-undef` L17
未定义变量 `console`
建议: ESLint 配置中没有声明 `console` 全局变量。建议在 `eslint.config.mjs` 中设置 `languageOptions.globals``globals.node`,或在本文件中使用 `globalThis.console`
- 🔴 `eslint:no-undef` L17
未定义变量 `pi`
建议: `with` 对象中的属性 `pi` 不会成为真正的词法作用域变量,因此静态分析无法识别。请参考 `no-with` 的修复:先解构出 `pi` 再使用。
- 🔴 `eslint:no-undef` L17
未定义变量 `e`
建议: `with` 对象中的属性 `e` 不会成为真正的词法作用域变量,因此静态分析无法识别。请参考 `no-with` 的修复:先解构出 `e` 再使用。
- 🔴 `eslint:no-var` L21
不应使用 `var`,请改用 `let``const`
建议: `deletable` 后续会被置空,使用 `let deletable = 42;`;如果确定不再重新赋值,可考虑 `const`
- 🔴 `eslint:no-delete-var` L22
不应删除变量。
建议: `delete` 只能删除对象属性,不能移除 `var`/`let`/`const` 声明的变量绑定。若要清理引用,请改为 `deletable = null;`;若要真正可删除,请把数据放进对象属性再 `delete`
- 🔴 `eslint:no-dupe-args` L25
函数参数名重复:`a`
建议: 重命名第二个参数,并保持函数行为不变:`function duplicateArgs(a, b) { return b; }`
- 🔴 `eslint:no-redeclare` L25
`a` 已重复声明。
建议: 移除重复参数名,将第二个参数改名为 `b`,并返回 `b`
- 🔴 `eslint:no-unused-vars` L32
变量 `err` 已被声明但从未使用。
建议: 如果要保留原始错误,请把它作为 `cause` 传给新错误:`throw new Error('parse failed', { cause: err });`;如果确实不需要原始错误,可使用省略绑定参数的 `catch { ... }`
- 🔴 `eslint:preserve-caught-error` L33
抛出的新错误没有附带原始错误作为 `cause`
建议: 在抛出 `Error` 时使用第二个 options 参数传递 `cause``throw new Error('parse failed', { cause: err });`,以保留原始错误链和堆栈信息。
- 🔴 `eslint:no-undef` L36
未定义变量 `console`
建议: 在 ESLint 配置中声明 `console` 全局变量,或使用 `globalThis.console.log(...)`
AI 审查 · 4 条建议
- 🟡 [AI] [bug] `legacy-octal-value-confusion` L11
**旧式八进制字面量 `010` 的实际值是 8,而非 10**
在宽松模式下,`010` 会被解析为旧式八进制字面量,等于十进制 8。如果写 `010` 的人以为是十进制 10,就会引入静默的数值错误;即使确实想表示八进制,也应使用明确的 `0o10`,避免歧义并绕过 `no-octal` 规则。
建议: 若语义是十进制 10,改为 `10`;若语义是八进制 8,改为 `0o10`
- 🟡 [AI] [bug] `delete-var-no-op` L22
**`delete deletable` 不会删除变量,清理意图不生效**
`delete` 操作符只对对象属性有意义;对 `var` 声明的标识符执行 `delete` 不会移除该绑定,在非严格模式下通常静默失败并返回 `false``deletable` 仍然是 42。这样的代码会给维护者造成“变量已被删除”的错觉。
建议: 如果只想清空引用,改为 `deletable = null;`;如果需要“可删除”的数据,请改为 `const data = { deletable: 42 };` 然后 `delete data.deletable;`
- 🔵 [AI] [design] `legacy-syntax-in-production-fixture` L3
**演示旧式语法的代码不应混入生产/业务源码**
该文件故意依靠 `.cjs` 和 sloppy mode 来使用 `with`、旧式八进制、重复参数等旧式语法。一旦文件被加入 `"use strict"`,或迁移为 ESM/被复制进严格模式环境,这些代码会立即变成 `SyntaxError`。作为语法规则演示/回归样例,应放在测试 fixtures 中并由 ESLint 显式配置解析。
建议: 将这类 legacy 语法样例移动到 `tests/fixtures/legacy/`,在测试配置中明确 `sourceType: 'script'` 和适用的 `ecmaVersion`,不要放在主代码目录。
- 🔵 [AI] [design] `missing-node-globals-config` L17
**ESLint 未声明 Node 全局变量,导致 `console` 反复触发 `no-undef`**
第 17、36 行的 `console` 在 Node 运行时是合法的全局变量,但当前 ESLint 配置没有引入 `globals.node` 或文件级注释,所以被重复报告为未定义。逐个改成 `globalThis.console` 会掩盖根因:ESLint 环境配置缺失。
建议: 在 `eslint.config.mjs``languageOptions.globals` 引入 `globals.node`;或在本文件顶部增加 `/* global console */`
@@ -0,0 +1,219 @@
# 代码审查报告
**文件:** `data\demo-eslint\src\typescript.ts`
**语言:** typescript
**耗时:** 257.3s
**分析工具:** eslint
---
总计: 67 | 错误: 57 | 警告: 10 | 建议: 0
静态分析 · 65 个问题
- 🔴 `eslint:@typescript-eslint/triple-slash-reference` L5
不要对 ./typesmodule.ts 使用三斜线引用,请改用 import 风格。
建议: 删除这一行;若必须引入副作用,使用 `import './typesmodule';`
- 🔴 `eslint:@typescript-eslint/consistent-type-imports` L8
声明中的所有导入都只被用作类型,请使用 `import type`
建议: 将第 8 行改为 `import type { DemoType } from './typesmodule';`
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L11
'legacyLoad' 已定义但从未被使用。
建议: 删除该函数,或将其加入底部 export 列表 / 添加 `export` 前缀。
- 🔴 `eslint:no-var` L12
检测到 var,建议使用 let 或 const。
建议: 将 `var mod` 改为 `const mod`,并考虑用 ES import 替代 require。
- 🔴 `eslint:@typescript-eslint/no-require-imports` L12
禁止使用 require 风格导入。
建议: 移除 require,改用 ES module:在文件顶部 `import * as mod from './typesmodule';`,并在函数中返回 `mod`
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L17
'arrayConstructor' 已定义但从未被使用。
建议: 删除函数,或添加 `export` 前缀导出。
- 🔴 `eslint:@typescript-eslint/no-array-constructor` L18
建议优先使用数组字面量 []。
建议: 用 `[1, 2, 3]` 代替 `new Array(1, 2, 3)`
- 🔴 `eslint:@typescript-eslint/no-dynamic-delete` L23
不要动态删除计算属性键。
建议: 改用 `Map<string, unknown>` 并通过 `map.delete(key)` 删除;如果必须使用对象,只能删除字面量属性或使用 `Reflect.deleteProperty` 并明确其风险。
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L27
'UtilityClass' 已定义但从未被使用。
建议: 导出该类或删除它。
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L34
'invalidVoid' 已定义但从未被使用。
建议: 删除函数或添加 `export` 前缀。
- 🔴 `eslint:@typescript-eslint/no-non-null-assertion` L41
禁止非空断言。
建议: 删除 `!`,让 null/undefined 交还给类型系统处理。
- 🔴 `eslint:@typescript-eslint/no-non-null-asserted-nullish-coalescing` L41
空值合并运算符本身就是为处理 null/undefined 设计的,使用非空断言没有意义。
建议: 直接写 `return value ?? 'fallback';`
- 🔴 `eslint:@typescript-eslint/no-non-null-assertion` L46
禁止非空断言。
建议: 不要在 `value` 上使用 `!`;如果值为 null,请先判空。
- 🔴 `eslint:@typescript-eslint/no-non-null-assertion` L46
禁止非空断言。
建议: 去掉非空断言并做空值保护。
- 🔴 `eslint:@typescript-eslint/no-extra-non-null-assertion` L46
禁止多余的非空断言。
建议: 至多保留一个 `!`(最好不用),并改为 `value?.length ?? 0`
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L50
'optionalChainAsserted' 已定义但从未被使用。
建议: 删除函数或添加 `export` 前缀。
- 🔴 `eslint:@typescript-eslint/no-non-null-assertion` L51
禁止非空断言。
建议: 移除可选链后的 `!`,改用 `??` 提供默认值。
- 🔴 `eslint:@typescript-eslint/no-non-null-asserted-optional-chain` L51
可选链表达式本来就可能返回 undefined,使用非空断言既不安全也错误。
建议: 删除 `!`,通过 `??` 或条件判断处理 undefined。
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L55
'thisAlias' 已定义但从未被使用。
建议: 删除函数或添加 `export` 前缀。
- 🔴 `eslint:@typescript-eslint/no-this-alias` L56
不允许将 this 赋值给局部变量。
建议: 直接访问 `this.x`;若在嵌套函数中需要 this,改用箭头函数或显式传入上下文对象。
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L62
'shadowed' 被赋值后从未使用。
建议: 删除外层未使用的变量,或改成 `void shadowed;` 进行使用。
- 🔴 `eslint:@typescript-eslint/no-shadow` L64
'shadowed' 已在第 62 行第 9 列的上层作用域中声明。
建议: 将内部变量重命名,避免遮蔽外层变量。
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L71
'preferConstInTs' 已定义但从未被使用。
建议: 删除函数或添加 `export` 前缀。
- 🔴 `eslint:prefer-const` L72
'fixed' 从未被重新赋值,建议使用 const。
建议: 把 `let fixed` 改为 `const fixed`
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L77
'EmptyConstructor' 已定义但从未被使用。
建议: 删除类或添加 `export` 前缀。
- 🔴 `eslint:@typescript-eslint/no-useless-empty-export` L82
空的 export 没有任何作用,可以删除。
建议: 删除 `export {};`,因为文件已有其他导出。
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L85
'UninitializedEnum' 已定义但从未被使用。
建议: 删除枚举或添加 `export` 前缀。
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L89
'DuplicateEnum' 已定义但从未被使用。
建议: 删除枚举或添加 `export` 前缀。
- 🔴 `eslint:@typescript-eslint/no-duplicate-enum-values` L91
重复的枚举成员值 1。
建议: 将 B 改为不同值,如 `B = 2`
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L96
'NonLiteralEnum' 已定义但从未被使用。
建议: 删除枚举或添加 `export` 前缀。
- 🔴 `eslint:@typescript-eslint/unified-signatures` L102
这些重载可以合并成一个签名 `string | 'x'`
建议: 合并重载;由于 `'x'``string` 的子类型,直接删除第二个重载即可。
- 🔴 `eslint:@typescript-eslint/no-explicit-any` L108
意外的 any 类型,请指定具体类型。
建议: 将参数类型改为泛型或 `unknown` 后收窄。
- 🔴 `eslint:@typescript-eslint/no-explicit-any` L108
意外的 any 类型,请指定具体类型。
建议: 将返回值类型改为泛型或具体类型。
- 🔴 `eslint:@typescript-eslint/no-empty-object-type` L113
`{}` 空对象类型允许任意非空值,包括字面量 `0``""`。如需保留此行为,请通过行内注释禁用该规则或配置 allowObjectTypes;如需表示任意对象,应使用 `object`;如需表示任意值,应使用 `unknown`
建议: 将 `EmptyObject` 定义为 `type EmptyObject = object;``unknown`
- 🔴 `eslint:@typescript-eslint/no-empty-object-type` L114
空接口声明允许任意非空值,包括字面量 `0``""`。如需保留此行为,请通过行内注释禁用该规则或配置 allowInterfaces;如需表示任意对象,应使用 `object`;如需表示任意值,应使用 `unknown`
建议: 将空接口改为 `type EmptyInterface = object;`(若想让任意对象类型)或 `unknown`
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L114
'EmptyInterface' 已定义但从未被使用。
建议: 删除接口或添加 `export` 前缀(改为 `export type EmptyInterface = object;` 可同时解决空类型问题)。
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L115
'emptyObj' 被赋值后从未使用。
建议: 删除变量或添加 `export` 前缀,或使用 `void emptyObj;` 标记为有意使用。
- 🔴 `eslint:@typescript-eslint/no-wrapper-object-types` L118
建议使用原始类型 `string` 作为类型名,而不是大写包装类型 `String`
建议: 将参数类型 `String` 改为 `string`
- 🔴 `eslint:@typescript-eslint/no-wrapper-object-types` L118
建议使用原始类型 `number` 作为类型名,而不是大写包装类型 `Number`
建议: 将参数类型 `Number` 改为 `number`
- 🔴 `eslint:@typescript-eslint/no-wrapper-object-types` L118
建议使用原始类型 `boolean` 作为类型名,而不是大写包装类型 `Boolean`
建议: 将参数类型 `Boolean` 改为 `boolean`
- 🔴 `eslint:@typescript-eslint/no-unsafe-function-type` L123
`Function` 类型可以接受任意函数式值,建议显式定义函数参数和返回类型。
建议: 将 `UnsafeFn` 定义为明确的函数签名,如 `type UnsafeFn = (...args: unknown[]) => unknown;`
- 🔴 `eslint:@typescript-eslint/no-unused-expressions` L130
期望赋值或函数调用,但看到的是表达式。
建议: 删除无副作用的 `flag && value;`,或改成 `if (flag) { return value; }`
- 🔴 `eslint:@typescript-eslint/no-unused-expressions` L131
期望赋值或函数调用,但看到的是表达式。
建议: 删除 `value;` 语句,或改为 `return value;`
- 🔴 `eslint:@typescript-eslint/no-unnecessary-type-constraint` L135
将泛型 `T` 约束为 `unknown` 没有任何作用,是不必要的。
建议: 移除 `extends unknown` 约束,直接写成 `<T>`
- 🔴 `eslint:@typescript-eslint/no-unsafe-declaration-merging` L140
类与接口之间的声明合并是不安全的。
建议: 不要在类与接口间做同名合并;将成员直接定义在类中。
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L140
'MergedThing' 已定义但从未被使用。
建议: 删除未使用声明,或添加 `export` 前缀。
- 🔴 `eslint:@typescript-eslint/no-unsafe-declaration-merging` L143
类与接口之间的声明合并是不安全的。
建议: 同上,消除同名合并,并在类中实现方法。
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L148
'tsIgnored' 已定义但从未被使用。
建议: 删除函数或添加 `export` 前缀。
- 🔴 `eslint:@typescript-eslint/ban-ts-comment` L149
请使用 `@ts-expect-error` 代替 `@ts-ignore`,因为 `@ts-ignore` 在下一行没有错误时不会报错。
建议: 将 `@ts-ignore` 改为 `@ts-expect-error`,或修复下一行的类型错误后删除注释。
- 🔴 `eslint:@typescript-eslint/no-unused-vars` L155
'MisusedNew' 已定义但从未被使用。
建议: 删除接口或添加 `export` 前缀。
- 🔴 `eslint:@typescript-eslint/no-misused-new` L156
接口不能被实例化,只有类可以。
建议: 将构造签名的定义放到 `class` 中,而不是 `interface`
- 🔴 `eslint:@typescript-eslint/no-misused-new` L157
接口不能被实例化,只有类可以。
建议: 不要在接口中声明 `constructor`,改写为类的构造器。
- 🔴 `eslint:@typescript-eslint/no-namespace` L161
ES2015 模块语法优于命名空间。
建议: 将 `namespace LegacyNamespace { ... }` 改为普通对象/ES module 导出。
- 🔴 `eslint:@typescript-eslint/prefer-namespace-keyword` L164
请使用 `namespace` 而不是 `module` 来声明自定义 TypeScript 模块。
建议: 将 `module LegacyModule` 改为 `namespace LegacyModule`(但更推荐 ES module 语法)。
- 🔴 `eslint:@typescript-eslint/no-namespace` L164
ES2015 模块语法优于命名空间。
建议: 将 `module LegacyModule` 改为普通对象/ES module 导出,避免模块命名空间。
- 🔴 `eslint:@typescript-eslint/prefer-as-const` L170
期望使用 const 断言,而不是字面量类型注解。
建议: 改为 `const literal = 'hello' as const;`
- 🟡 `eslint:@typescript-eslint/no-extraneous-class` L27
类中只有静态属性/方法,不应使用类。
建议: 改用普通对象:`export const UtilityClass = { helper: (): number => 42 };`
- 🟡 `eslint:@typescript-eslint/no-invalid-void-type` L34
void 不能作为联合类型的成员。
建议: 移除联合中的 void,改为返回 `undefined` 或直接使用 `: void`;若函数有返回值,使用具体类型。
- 🟡 `eslint:@typescript-eslint/no-extraneous-class` L77
类中只有一个空的构造器,不应使用类。
建议: 删除整个类;若需要实例化对象,写成普通对象或函数。
- 🟡 `eslint:@typescript-eslint/no-useless-constructor` L78
无用的构造器。
建议: 直接移除 `constructor() {}`,让类使用默认构造器。
- 🟡 `eslint:@typescript-eslint/prefer-enum-initializers` L86
成员 'First' 的值应显式定义。
建议: 显式赋值,如 `First = 0`
- 🟡 `eslint:@typescript-eslint/prefer-enum-initializers` L87
成员 'Second' 的值应显式定义。
建议: 显式赋值,如 `Second = 1`
- 🟡 `eslint:@typescript-eslint/prefer-literal-enum-member` L97
显式枚举值只能是字符串或数字字面量。
建议: 将 `A = someValue` 改为 `A = 10`
- 🟡 `eslint:prefer-rest-params` L176
请使用剩余参数代替 `arguments`
建议: 改为 `function restParams(...args: unknown[]) { return args; }`
- 🟡 `eslint:prefer-spread` L181
请使用展开运算符代替 `.apply()`
建议: 改为 `return mySpreadFn(...base);`
AI 审查 · 2 条建议
- 🔴 [AI] [bug] `missing-method-in-declaration-merging` L143
**接口与类合并后 method 缺少实现**
MergedThing 接口通过声明合并为同名类实例类型增加了 method(): void 签名,但类中没有实现该方法。因此类型检查允许 new MergedThing().method(),运行时会抛出 method is not a function 的 TypeError。静态规则只报告了合并不安全,没有指出缺少实现的问题。
建议: 删除 interface MergedThing,并将 method 的实现写到 class MergedThing 中;如果确实需要接口合并,也要在类中提供对应实现。
- 🟡 [AI] [design] `any-return-from-require` L12
**legacyLoad 隐式返回 any 类型**
require('./typesmodule') 的返回值类型为 any,函数没有显式返回类型,因此 legacyLoad 的返回类型被推断为 any,调用方会丢失类型检查。该类型泄漏不在 no-require-imports 规则的直接范围内。
建议: 改用 ES module 导入并声明返回类型,例如删除 var mod = require(...),在文件顶部使用 import * as mod from './typesmodule';,并让函数返回 typeof mod。
+166
View File
@@ -0,0 +1,166 @@
// ESLint 覆盖率测试 v3:复刻内置配置(与 ESLintAdapter.getDefaultConfig 逐字一致)
// + legacysyntax.js script 模式(对应 demo 工程配置 eslint.config.mjs
// + 以 calculateConfigForFile 计算各文件类型实际生效(severity != off)的规则集
import { ESLint } from 'eslint';
import js from '@eslint/js';
import ts from 'typescript-eslint';
import { readFileSync, readdirSync, writeFileSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const SRC = join(__dirname, 'src');
// ===== 以下与 src/rules/builtin-rules.ts 逐字一致 =====
const eslintExtraRules = {
'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-eval': 'error', 'no-extend-native': 'error', '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',
};
const eslintExtraTsRules = {
'@typescript-eslint/no-non-null-assertion': 'error',
'@typescript-eslint/no-dynamic-delete': 'error',
'@typescript-eslint/no-useless-empty-export': 'error',
'@typescript-eslint/consistent-type-imports': 'error',
'@typescript-eslint/unified-signatures': 'error',
'@typescript-eslint/no-extraneous-class': 'warn',
'@typescript-eslint/no-useless-constructor': 'warn',
'@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'error',
'@typescript-eslint/no-invalid-void-type': 'warn',
'@typescript-eslint/prefer-literal-enum-member': 'warn',
'@typescript-eslint/prefer-enum-initializers': 'warn',
'no-shadow': 'off',
'@typescript-eslint/no-shadow': 'error',
'no-array-constructor': 'off',
};
const TS_FILES = ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'];
const JS_FILES = ['**/*.js', '**/*.jsx', '**/*.mjs', '**/*.cjs'];
const tsConfigs = ts.configs.recommended.map(cfg => ({
...cfg,
files: (cfg).files ?? TS_FILES,
}));
// 复刻内置默认配置(与 ESLintAdapter.getDefaultConfig 一致)+ legacysyntax script 模式
const defaultConfig = [
js.configs.recommended,
{ files: JS_FILES, languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } } },
...tsConfigs,
{ rules: eslintExtraRules },
{ files: TS_FILES, rules: eslintExtraTsRules },
{ files: ['**/legacysyntax.js'], languageOptions: { sourceType: 'script' } },
];
const files = readdirSync(SRC).filter(f => /\.(js|ts|mjs|cjs)$/.test(f));
const engine = new ESLint({ cwd: SRC, overrideConfigFile: true, overrideConfig: defaultConfig });
// ===== 实际生效规则(severity != off=====
async function effectiveRules(sampleFile) {
const cfg = await engine.calculateConfigForFile(join(SRC, sampleFile));
const out = new Set();
for (const [name, val] of Object.entries(cfg.rules ?? {})) {
const sev = Array.isArray(val) ? val[0] : val;
if (sev !== 'off' && sev !== 0) out.add(name);
}
return out;
}
const jsEnabled = await effectiveRules('common.js');
const tsEnabled = await effectiveRules('typescript.ts');
const builtinAll = new Set([...jsEnabled, ...tsEnabled]);
// ===== 逐文件 lint =====
const actualByFile = {};
const parseErrors = {};
let violationTotal = 0;
for (const f of files) {
const text = readFileSync(join(SRC, f), 'utf8');
const results = await engine.lintText(text, { filePath: join(SRC, f) });
const rulesHit = new Set();
for (const r of results) {
for (const m of r.messages) {
if (m.fatal) { parseErrors[f] = m.message; continue; }
violationTotal++;
if (m.ruleId) rulesHit.add(m.ruleId);
}
}
actualByFile[f] = rulesHit;
}
const actualRaw = new Set();
for (const s of Object.values(actualByFile)) for (const r of s) actualRaw.add(r);
// ===== 期望规则提取(demo 注释标注)=====
const NOISE = new Set(['eslint', '本文件', '说明', '保留', '合并', '用于', '异步', '比较', '该文件', 'eslint.config.mjs', '顺手在此以', '文件', '演示', '规则',
// 注释噪音:文件名 / 指令 / 未启用规则名 / 笔误(真实规则为 no-unused-vars
'esm-demo', 'ts-ignore', 'no-empty-function', 'no-useless-vars']);
const expectByFile = {};
for (const f of files) {
const text = readFileSync(join(SRC, f), 'utf8');
const isTs = /\.ts$/.test(f);
const expects = new Set();
for (const line of text.split('\n')) {
const m = line.match(/^\s*\/\/\s*(.+)$/);
if (!m) continue;
const body = m[1];
// 候选 tokenkebab-case 规则名 或 @typescript-eslint/ 前缀规则名
for (const tm of body.matchAll(/(@typescript-eslint\/[a-z0-9-]+|[a-z][a-z0-9]*(-[a-z0-9]+)+)/g)) {
const name = tm[1];
if (NOISE.has(name)) continue;
if (!isTs && name.startsWith('@typescript-eslint/')) continue;
expects.add(name);
}
}
expectByFile[f] = expects;
}
const expectAll = new Set();
for (const s of Object.values(expectByFile)) for (const r of s) expectAll.add(r);
// ===== 统计 =====
// 名称归一化:注释里通常省略 @typescript-eslint/ 前缀,比较时两边都剥掉前缀
const normalize = r => r.replace(/^@typescript-eslint\//, '');
const actualNorm = new Set([...actualRaw].map(normalize));
const jsHit = [...jsEnabled].filter(r => actualRaw.has(r));
const tsHit = [...tsEnabled].filter(r => actualRaw.has(r));
const hitInBuiltin = [...builtinAll].filter(r => actualRaw.has(r));
const expectHit = [...expectAll].filter(r => actualNorm.has(normalize(r)));
const expectMissed = [...expectAll].filter(r => !actualNorm.has(normalize(r))).sort();
console.log('=== ESLint 覆盖率实测结果(v3,生效口径)===');
console.log(`测试文件: ${files.join(', ')}`);
console.log(`解析错误: ${Object.keys(parseErrors).length ? JSON.stringify(parseErrors) : '无'}`);
console.log(`JS 生效规则: ${jsEnabled.size} 条,命中 ${jsHit.length} = ${(jsHit.length / jsEnabled.size * 100).toFixed(1)}%`);
console.log(`TS 生效规则: ${tsEnabled.size} 条,命中 ${tsHit.length} = ${(tsHit.length / tsEnabled.size * 100).toFixed(1)}%`);
console.log(`合并生效规则: ${builtinAll.size} 条,命中 ${hitInBuiltin.length} = ${(hitInBuiltin.length / builtinAll.size * 100).toFixed(1)}%`);
console.log(`demo 期望规则: ${expectAll.size} 条,命中 ${expectHit.length}`);
console.log(`违规总数: ${violationTotal}`);
console.log(`\nJS 未命中 (${jsEnabled.size - jsHit.length}): ${[...jsEnabled].filter(r => !actualRaw.has(r)).sort().join(', ') || '无'}`);
console.log(`\nTS 未命中 (${tsEnabled.size - tsHit.length}): ${[...tsEnabled].filter(r => !actualRaw.has(r)).sort().join(', ') || '无'}`);
console.log(`\n期望未达成: ${expectMissed.join(', ') || '无'}`);
writeFileSync('/data/user/work/results/eslint_result.json', JSON.stringify({
tool: 'eslint',
builtinJsTotal: jsEnabled.size,
builtinTsTotal: tsEnabled.size,
builtinAllTotal: builtinAll.size,
jsHit: jsHit.sort(),
tsHit: tsHit.sort(),
jsMissed: [...jsEnabled].filter(r => !actualRaw.has(r)).sort(),
tsMissed: [...tsEnabled].filter(r => !actualRaw.has(r)).sort(),
actualHitRaw: [...actualRaw].sort(),
expectTotal: expectAll.size,
expectHit: expectHit.sort(),
expectMissed,
violationTotal,
parseErrors,
}, null, 2));
console.log('\n结果已写入 /data/user/work/results/eslint_result.json');
+608
View File
@@ -0,0 +1,608 @@
// ============ common.js(合并文件) ============
// 由 ESLint 演示工程原 10 个 JS 文件合并压缩而来,用于触发各条 ESLint 规则。
// 保留独立文件:legacysyntax.jsscript 模式解析)、typescript.ts / typesmodule.tsTS 规则)。
// 注:本文件为普通 script(无 import/export),且不含严格模式下的致命语法样例
//with / delete 局部变量 / 重复形参 / 顶层重复 let 等),确保 ESLint 能完整解析并触发全部规则。
// ===== 原 async.js =====
// ============ async.js ============
// 异步 / Promise 相关规则
// require-yield
function* generatorWithoutYield() {
console.log('no yield here');
}
// no-async-promise-executor
const asyncExecutor = new Promise(async (resolve, reject) => {
resolve(1);
});
// no-promise-executor-return
const executorReturn = new Promise((resolve, reject) => {
return resolve(1);
});
// no-await-in-loop
async function awaitInLoop(items, url) {
const results = [];
for (const item of items) {
const res = await fetch(url + item);
results.push(res);
}
return results;
}
// no-return-assign
function returnAssign(x) {
let assignedInReturn;
return assignedInReturn = x;
}
// no-throw-literal
function throwLiteral() {
throw 'not an error object';
}
// no-useless-return
function uselessReturn() {
doSomething();
return;
}
function doSomething() {
console.log('work');
}
// ===== 原 classes.js =====
// ============ classes.js ============
// 类相关规则
// constructor-super
class BadConstructor extends Error {
constructor(message) {
console.log(message);
}
}
// no-class-assign
class Widget {}
Widget = 'overwritten';
// no-dupe-class-members
class DupeMember {
method() {
return 1;
}
method() {
return 2;
}
}
// no-empty-static-block
class EmptyStatic {
static {}
}
// no-setter-return
class SetterReturn {
set value(v) {
this._value = v;
return v;
}
}
// getter-return
class GetterNoReturn {
get value() {
const x = 1;
}
}
// no-this-before-super
class ThisBeforeSuper extends Error {
constructor(message) {
this.message = message;
super(message);
}
}
// no-unused-private-class-members
class UnusedPrivate {
#secret = 42;
reveal() {
return 'no secret';
}
}
// ===== 原 comparison.js =====
// ============ comparison.js ============
// 比较运算类规则
// eqeqeq
function looseEquality(a, b) {
return a == b;
}
// no-eq-null
function nullEquality(x) {
return x == null;
}
// no-self-compare
function selfCompare(x) {
return x === x;
}
// no-compare-neg-zero
function negZero(x) {
return x === -0;
}
// use-isnan
function isNanWrong(x) {
return x === NaN;
}
// valid-typeof
function typeofCheck(x) {
return typeof x === 'strnig';
}
// no-constant-binary-expression
function constBinary() {
const y = 'a' + -'b';
return y;
}
// no-unexpected-multiline already in syntax
// no-constant-condition already in syntax
// no-unsafe-negation already in syntax
// no-constant-binary-expression already covered
// ===== 原 control.js =====
// ============ control.js ============
// 流程控制 / switch 相关规则
// no-fallthrough
function fallthrough(x) {
switch (x) {
case 1:
console.log('one');
case 2:
console.log('two');
break;
default:
break;
}
}
// no-case-declarations
function caseDeclarations(x) {
switch (x) {
case 1:
let caseLocal = 10;
console.log(caseLocal);
break;
default:
break;
}
}
// no-duplicate-case
function duplicateCase(x) {
switch (x) {
case 1:
break;
case 1:
break;
default:
break;
}
}
// no-dupe-else-if
function dupeElseIf(x) {
if (x === 1) {
return 'a';
} else if (x === 1) {
return 'b';
}
return 'c';
}
// default-case
function noDefault(x) {
switch (x) {
case 1:
return 'one';
}
return 'none';
}
// default-case-last
function defaultNotLast(x) {
switch (x) {
default:
return 'default';
case 1:
return 'one';
}
}
// no-constant-condition covered in syntax
// ===== 原 functions.js =====
// ============ functions.js ============
// 函数相关规则
// no-func-assign
function assignableFn() {
return 'original';
}
assignableFn = function () {
return 'reassigned';
};
// no-return-assign covered in async.js
// no-throw-literal covered in async.js
// no-useless-vars? no such rule
// for-direction
function forDirection() {
for (let i = 0; i < 10; i--) {
console.log(i);
}
}
// ===== 原 modern.js =====
// ============ modern.js ============
// 现代语法 / 风格偏好规则
// camelcase
const my_snake_case_var = 1;
// new-cap
const generated = new object();
// no-useless-concat
const uselessConcat = 'foo' + 'bar';
// no-useless-computed-key
const obj = { ['key']: 'value' };
// no-dupe-keys
const dupeKeyObj = { a: 1, a: 2 };
// no-useless-rename
const { a: a } = someObj;
// no-var covered in variables.js
// prefer-template
function preferTemplate(name) {
return 'Hello, ' + name + '!';
}
// prefer-object-spread
function preferSpreadStyle() {
const merged = Object.assign({}, { a: 1 }, { b: 2 });
return merged;
}
// prefer-rest-params
function preferRest() {
const args = Array.prototype.slice.call(arguments);
return args;
}
// prefer-spread
function spreadThis() {
return 1;
}
function preferSpreadCall() {
const nums = [1, 2, 3];
return spreadThis.apply(null, nums);
}
// prefer-object-has-own
function hasOwnPrefer(obj, key) {
return Object.prototype.hasOwnProperty.call(obj, key);
}
// prefer-const
function preferConstDemo() {
let x = 1;
return x;
}
const someObj = { a: 1, b: 2, key: 'v' };
// ===== 原 scope.js =====
// ============ scope.js ============
// 作用域相关规则
// no-with
// 说明:with 语句在严格模式(ESM/use strict)下是语法错误,无法在普通 script 中安全演示,
// 故此处移除。该规则仍可通过 .eslintrc 的 parserOptions 演示,或使用 eval('with(...)') 场景。
// no-extend-native
function extendNative() {
String.prototype.padOut = function () {};
}
// no-eval
function evalDemo(input) {
return eval(input);
}
// no-param-reassign
function paramReassign(x) {
x = x * 2;
return x;
}
// no-unmodified-loop-condition
function unmodifiedLoop(i) {
while (i < 10) {
console.log(i);
}
}
// no-unreachable-loop
function unreachableLoop() {
const items = [1, 2, 3];
for (const item of items) {
break;
}
while (true) {
break;
}
}
// no-unmodified-loop-condition already covered
// no-unreachable-loop already covered
// ===== 原 syntax.js =====
// ============ syntax.js ============
// 语法类规则:正则、转义、字面量语法问题
// no-invalid-regexp
const badRegex = new RegExp('[');
// no-regex-spaces
const regexSpaces = /a b/;
// no-control-regex
const controlRegex = /\x1f/;
// no-empty-character-class
const emptyCharClass = /abc[]/;
// no-misleading-character-class
const misleadingCharClass = /[👍👎]/g;
// no-useless-backreference
const uselessBackref = /(\1a)/;
// no-useless-escape
const uselessEscape = 'line\.';
// no-irregular-whitespace
const irregularWhitespace = 'dissimilar\u00A0space';
// no-loss-of-precision
const lossOfPrecision = 2.1234567890123456789012345678901234567890;
// no-sparse-arrays
const sparseArray = [1, , 2];
// no-empty
function emptyFunctionBody() {}
// no-empty (related with no-empty-function? no, plain block)
if (sparseArray.length > 0) {
}
// no-constant-binary-expression
const constantBinary = 'a' && 'b';
// no-constant-condition
if (true) {
console.log('always true');
}
// no-cond-assign
let condVar;
if (condVar = 1) {
console.log('assignment in condition');
}
// no-self-assign
let selfAssigned = 5;
selfAssigned = selfAssigned;
// no-debugger
debugger;
// no-extra-boolean-cast
if (!!lossOfPrecision) {
console.log('double negation');
}
// no-ex-assign
try {
throw new Error('boom');
} catch (e) {
e = new Error('reassigned');
}
// no-empty-pattern
function noEmptyPattern({}) {
return 1;
}
// no-unreachable
function unreachableCode() {
return 1;
console.log('never reached');
}
// no-unexpected-multiline
const unexpectedMultiline = lossOfPrecision
(function () {
console.log('IIFE');
})();
// no-unsafe-optional-chaining
function unsafeOptional(obj) {
return (obj?.a).b;
}
// no-unsafe-negation
let flagFlag = true;
const holderObj = {};
if (!flagFlag in holderObj) {
console.log('negated in');
}
// no-unsafe-finally
function unsafeFinally() {
try {
throw new Error('inner');
} finally {
return 1;
}
}
// no-unused-labels
unusedLabel: for (let i = 0; i < 3; i++) {
console.log(i);
}
// no-unused-vars
const completelyUnusedVariable = 42;
// no-useless-catch
function uselessCatch() {
try {
return 1;
} catch (err) {
throw err;
}
}
// no-useless-escape already covered above
// no-irregular-whitespace already covered
// no-empty-character-class already covered
// no-control-regex already covered
// no-regex-spaces already covered
// no-invalid-regexp already covered
// no-nonoctal-decimal-escape already covered
// no-irregular-whitespace (actual NBSP character on this line)
const irregularWS = 1; 
// ===== 原 variables.js =====
// ============ variables.js ============
// 变量声明类规则
// no-var
var oldStyleVar = 1;
// no-undef
console.log(undefinedGlobalThing);
// no-redeclare
function redeclareDemo() {
var redeclaredName = 1;
var redeclaredName = 2;
}
// no-dupe-args
// 说明:function(a, a) 重复形参在严格模式是语法错误(Argument name clash),
// 已由 class DupeMember 的 no-dupe-class-members 覆盖"重复成员"演示,此处移除。
// no-delete-var
// 说明:严格模式下 delete 局部变量是语法错误(Deleting local variable in strict mode),
// 此处改为删除对象属性以保留 no-delete-var 规则的演示语义(delete 一元操作符)。
function deleteVarDemo() {
const obj = { localVar: 1 };
delete obj.localVar;
}
// no-shadow-restricted-names
function shadowRestricted(undefined) {
return undefined;
}
// no-global-assign
function globalAssign() {
Math = {};
}
// no-func-assign (in functions.js maybe)
// no-class-assign (in classes.js)
// block-scoped-var
function blockScopedDemo() {
if (true) {
var blockScoped = 1;
}
console.log(blockScoped);
}
// no-unassigned-vars
function unassignedVarDemo() {
let unassignedVariable;
return unassignedVariable;
}
// no-shadow
function shadowDemo() {
let value = 1;
function inner() {
let value = 2;
return value;
}
return inner();
}
// no-useless-assignment
function uselessAssignment() {
let x = 1;
x = 2;
return x;
}
// no-array-constructor
const arrayViaConstructor = new Array(1, 2, 3);
// no-new-native-nonconstructor
const symbolViaNew = new Symbol('x');
// no-obj-calls
const jsonAsFunction = JSON();
// no-const-assign
const immutableValue = 1;
immutableValue = 2;
// no-prototype-builtins
function protoBuilds(obj) {
return obj.hasOwnProperty('key');
}
// no-import-assign
// 说明:本文件为普通 script(无 import/export),no-import-assign 需要 ESM 模块绑定,
// 该规则由 typesmodule.ts(ESM 模块文件)演示,此处移除占位。
+11
View File
@@ -0,0 +1,11 @@
// ============ esm-demo.js ============
// ESM 模块文件:演示 no-import-assignmodule 模式下对 import 绑定赋值)
// no-import-assign
import fs from 'fs';
function reassignImport() {
fs = 1;
}
console.log(fs);
+36
View File
@@ -0,0 +1,36 @@
/* eslint preserve-caught-error: "error" */
// ============ legacysyntax.cjs ============
// .cjs 扩展名使 ESLint flat config 默认按 commonjs(宽松)模式解析本文件,
// 无需任何工程配置(eslint.config.mjs)即可触发仅 sloppy 模式合法的旧式语法规则。
// 首行行内注释单独启用 preserve-caught-erroreslint 9.39.4 引擎已实现该规则,但内置配置未启用)。
// no-nonoctal-decimal-escape
const nonOctalEscape = '\8 and \9';
// 顺手在此以宽松模式再触发 no-octal(旧式八进制字面量)
const legacyOctal = 010;
// ===== 补充:其余仅宽松模式合法的旧式语法样例 =====
// no-with
with ({ pi: 3.14, e: 2.72 }) {
console.log(pi, e);
}
// no-delete-var
var deletable = 42;
delete deletable;
// no-dupe-args
function duplicateArgs(a, a) {
return a;
}
// preserve-caught-errorcatch 中抛新错误应保留原始错误)
try {
JSON.parse('not json');
} catch (err) {
throw new Error('parse failed');
}
console.log(nonOctalEscape, legacyOctal, duplicateArgs(1, 2));
+34
View File
@@ -0,0 +1,34 @@
// ============ legacysyntax.js ============
// 该文件以 script(非严格)模式解析,用于触发需要在 sloppy 模式下才能合法的旧式转义规则。
// eslint.config.mjs 中为其单独设置了 sourceType: "script"。
// no-nonoctal-decimal-escape
const nonOctalEscape = '\8 and \9';
// 顺手在此以 script 模式再触发 no-octal(旧式八进制字面量)
const legacyOctal = 010;
// ===== 补充:其余仅 sloppy 模式合法的旧式语法样例(原 common.js 中被移除的致命样例)=====
// no-with
with ({ pi: 3.14, e: 2.72 }) {
console.log(pi, e);
}
// no-delete-var
var deletable = 42;
delete deletable;
// no-dupe-args
function duplicateArgs(a, a) {
return a;
}
// preserve-caught-errorESLint 10 新增,catch 中抛新错误应保留原始错误)
try {
JSON.parse('not json');
} catch (err) {
throw new Error('parse failed');
}
console.log(nonOctalEscape, legacyOctal, duplicateArgs(1, 2));
+213
View File
@@ -0,0 +1,213 @@
// ============ typescript.ts ============
// TypeScript 规则演示(覆盖 ts.configs.recommended + 插件精选规则)
// triple-slash-reference: 禁止非类型导入的三斜线指令
/// <reference path="./typesmodule.ts" />
// consistent-type-imports
import { DemoType } from './typesmodule';
// no-require-imports / no-var
function legacyLoad() {
var mod = require('./typesmodule');
return mod;
}
// no-array-constructor
function arrayConstructor() {
return new Array(1, 2, 3);
}
// no-dynamic-delete
function dynamicDelete(obj: Record<string, unknown>, key: string) {
delete obj[key];
}
// no-extraneous-class
class UtilityClass {
static helper(): number {
return 42;
}
}
// no-invalid-void-type
function invalidVoid(): void | undefined {
return undefined;
}
// no-non-null-asserted-nullish-coalescing
function nullishAsserted(input: string | null) {
const value: string | null = input;
return value! ?? 'fallback';
}
// no-non-null-assertion 与 no-extra-non-null-assertion
function nonNullAssert(value: string | null) {
return value!!.length;
}
// no-non-null-asserted-optional-chain
function optionalChainAsserted(obj: { a?: { b?: string } }) {
return obj?.a?.b!;
}
// no-this-alias
function thisAlias(this: { x: number }) {
const self = this;
return self.x;
}
// no-shadow 与 prefer-const
function shadowInTs() {
const shadowed = 1;
const inner = () => {
const shadowed = 2;
return shadowed;
};
return inner();
}
// prefer-const@typescript-eslint/prefer-const:重新赋值前不应使用 let)
function preferConstInTs() {
let fixed = 1;
return fixed;
}
// no-useless-constructor
class EmptyConstructor {
constructor() {}
}
// no-useless-empty-export
export {};
// prefer-enum-initializers 与 no-duplicate-enum-values
enum UninitializedEnum {
First,
Second,
}
enum DuplicateEnum {
A = 1,
B = 1,
}
// prefer-literal-enum-member
const someValue = 10;
enum NonLiteralEnum {
A = someValue,
}
// unified-signatures
function unifiedDemo(a: string): void;
function unifiedDemo(a: 'x'): void;
function unifiedDemo(a: string): void {
console.log(a);
}
// no-explicit-any
function explicitAny(value: any): any {
return value;
}
// no-empty-object-type
type EmptyObject = {};
interface EmptyInterface {}
const emptyObj: EmptyObject = {};
// no-wrapper-object-types
function wrapperTypes(s: String, n: Number, b: Boolean) {
return s + n + b;
}
// no-unsafe-function-type
type UnsafeFn = Function;
function unsafeFn(fn: UnsafeFn) {
fn();
}
// no-unused-expressions
function unusedExpr(flag: boolean, value: number) {
flag && value;
value;
}
// no-unnecessary-type-constraint
function pointlessConstraint<T extends unknown>(t: T): T {
return t;
}
// no-unsafe-declaration-merging
interface MergedThing {
method(): void;
}
class MergedThing {
value = 1;
}
// ban-ts-comment
function tsIgnored() {
// @ts-ignore
const x = 1;
return x;
}
// no-misused-new
interface MisusedNew {
new (): MisusedNew;
constructor(): void;
}
// no-namespace 与 prefer-namespace-keyword
namespace LegacyNamespace {
export const value = 1;
}
module LegacyModule {
export const value = 2;
}
// prefer-as-const
function preferAsConst() {
const literal: 'hello' = 'hello';
return literal;
}
// prefer-rest-params 与 prefer-spread
function restParams() {
const args = arguments;
return Array.prototype.slice.call(args);
}
declare function mySpreadFn(...args: number[]): number;
function spreadArgs(base: number[]) {
return mySpreadFn.apply(undefined, base);
}
// preserve-caught-errorESLint 10 新增,catch 中抛新错误应保留原始错误)
function swallowError(input: string): number {
try {
return JSON.parse(input) as number;
} catch (err) {
throw new Error('parse failed');
}
}
const demoValue: DemoType = 'demo';
void demoValue;
void swallowError;
export {
dynamicDelete,
nullishAsserted,
nonNullAssert,
shadowInTs,
unifiedDemo,
explicitAny,
wrapperTypes,
unsafeFn,
unusedExpr,
pointlessConstraint,
preferAsConst,
restParams,
spreadArgs,
LegacyNamespace,
LegacyModule,
};
+8
View File
@@ -0,0 +1,8 @@
// ============ typesmodule.ts ============
// 供 typescript.ts 使用的类型模块
export interface stringifyType {
(input: string): string;
}
export type DemoType = string | number;