ESLint yoda 自动修复后代码变了,检查语句开头的分号和括号
ESLint 10.13.0 的 yoda 翻转可能让下一行接入上一语句,或让箭头体误变成块。恢复语句边界,收紧自动修复范围,并检查语义与正式版本。
执行 eslint --fix 后,比较式两边虽然交换正确,下一行却接进了上一条语句;另一种表现是箭头函数突然报 Parsing error: Unexpected token .。如果启用了 yoda 规则,要检查修复后的表达式现在以什么 token 开头。
ESLint 10.13.0、默认 Espree 的具体触发形态包括:
foo()
0 === [a, b].indexOf(x) && go()
const f = () => 1 < {}.x
旧修复可能得到:
foo()
[a, b].indexOf(x) === 0 && go()
const f = () => {}.x > 1
第一段现在可能按 foo()[a, b] 一类连续表达式解析,不能保证仍调用了原来的数组方法。第二段的花括号出现在箭头体开头,会被看成块体,而不是原比较式里的对象字面量。问题涉及语句边界与语法位置,不是把 === 的方向调回来就能恢复。
先恢复变更前的语义
检查这次自动修复的 diff,只撤回受影响部分,不覆盖同一文件里其他人的修改。若仍按默认 yoda 风格手动交换比较式,保留明确的语句与表达式边界,例如:
foo();
[a, b].indexOf(x) === 0 && go();
const f = () => ({}.x > 1);
示例保留原来的 foo、go 与数据含义,实际项目应在自己的调用位置核对。第二行前面的明确分号,或该行开头的防御性分号,都可以隔开上一条语句;对象比较式外的括号则保留了箭头函数返回表达式的形式。
不只是数组字面量需要关注。翻转后的开头若是 (、-、+、模板或正则字面量,也可能与上一行结合;function、class 或对象字面量在语句开头还可能触发另一种解析。已有 if 条件中的比较没有同样的语句起点,不应为此给所有条件式机械加括号。
保留检查,先收紧自动写入范围
可先让原来的 lint 命令只报告问题,由编辑者核对这些位置。若流水线仍需自动处理其他类型,ESLint 的 --fix-type 可以筛选修复类别;yoda 在 10.13.0 中属于 suggestion,例如在受控工作区使用:
npx eslint --fix --fix-type problem,layout example.js
这会排除所有 suggestion 类修复,不只排除 yoda;它仍会改文件。示例假定项目已按锁文件安装了 ESLint,example.js 要换成需要检查的文件,并沿用项目自己的执行入口与配置,不能当作不写入的诊断命令。
针对语句起点与箭头体的修正已合入后续主分支,但正式 10.13.0 仍是旧实现。采用后续版本前,要核对实际正式发行的 yoda 实现,不把 PR 合并等同于当前项目已经更新。
验收分开看语法和行为:原文件应能解析,原来的两个语句仍各自执行,箭头函数仍返回比较结果;再运行项目既有测试。仅看到 ESLint 不再报告 yoda,可能只是规则已经把有问题的代码写完了,并不能证明修复安全。回退规则或工具版本时也要恢复已产生的错误 diff,停止自动修复不会自动还原旧改动。


