本文主要面向前端和 Node.js 研发团队,介绍如何将 CR 经验更高效地转换成 ESLint 规则集,以提升团队研发质量下限。以下是探讨的内容:
- 如何写:使用 ChatGPT 生成 ESLint 规则,并通过 snapshot 测试用例快照提升规则的鲁棒性
- 如何配:搭建 ESLint 工程(主要以 Monorepo),根据不同应用规则集需求,在 precommit 和 CI 阶段对增量代码做检测
本文演示的代码见:
背景
ESLint 是一个广泛使用的 JavaScript 代码检查工具,可帮助团队确保代码质量和一致性。然而,ESLint 默认的规则集可能并不符合各团队的具体需求,因此,定制化 ESLint 规则集对于团队高效协作研发是较为常见的需求。
痛点
在研发过程中,使用过 ESLint 的团队通常会遇到以下几个痛点问题:
- 祖传代码改不动:接手项目代码时,大部分没有配备 ESLint,为了符合规则,修改祖传代码的风险极高!
- 无效规则集较多:实践中,有很多规则过于严格(例如 no-throw-literal、dot-notation 等)
- 规则定制困难:众口难调,每个团队的代码风格都不同,完全按照业界/公司的代码规范会让团队非常痛苦,而编写一个 ESLint 规则需要与 AST 斗智斗勇。
(按代码规范的代码检测错误数)
效果
以上痛点的解法大概是:
- 增量检测:只对当前变更的代码进行 ESLint 检测
- 取其精华:在公司代码规范基础上,关闭无效的规则,只开启『一定会出错』的规则
- 利用 ChatGPT:生成式 AI 最适合做这类事,99% 的规则都可以由 ChatGPT 生成的
最终,每个项目都可以定制规则,并按增量执行检测。如果 CI 检测未通过,则会给出修复命令,如下图所示:
不同目录执行不同规则
如何写?
ChatGPT
最初,直接使用 ChatGPT 在对话中生成规则。后来,发现可以通过固定 prompt,提供『规则描述』、『正确代码示例』和『错误代码示例』三个参数,让 AI 自动生成规则。
Prompt 使用的是(抛砖引玉,大家有更好的 Prompt 可以交流):
手工
ChatGPT 生成的规则也不是银弹,目前发现没法写 TS 相关的规则,所以这部分只能借助 @typescript-eslint/utils 工具来手写 AST。
TS 类型判断
这里举一个很有用的规则:React JSX 中不允许 number 数字类型直接和 &&(逻辑和) 使用,经常会出现页面直接渲染了
0
,而不是 hello
这个规则需要借助 typescript-eslint 来写, 解析成 TSEsTree AST 抽象语法树,最终的规则代码如下:
如何测?
快照测试用例
ESLint 官方推荐写规则测试用例通过
RuleTester
,但是这无疑增加了规则用例的复杂度,同时还需要处理有效和无效代码的格式。这里以使用者视角,用更简单的快照方式,规则开发者只需要补
good
、bad
、bad-stdout
三个快照即完成测试用例:执行测试用例:
有了快照用例后,可以让规则自身功能更健壮:
如何配?
这里我们以单仓 Monorepo 为例,配置 ESLint 规则集,多仓配置方式和单仓中的单个目录规则一致。
目录结构图
主要在两部分:
- 全量 ESLint 规则集 npm 包:这里用
packages/eslint
,包名为@infras/eslint-config-local
- 应用
apps/
,对 ESLint 的需求中一般有三类: - 使用默认规则
app1
- 规则定制
app2
:主要是开启一些规则 - 关闭/不使用 lint
app3
:不再迭代的废弃应用,不希望
(其中紫色部分为 ESLint 配置部分)
ESLint 规则集 npm 本地包
eslint 规则集是经常快速变化的,不建议通过发包形式使用,而是以本地包方式使用
packages/eslint
npm 包目录结构如下:package.json
先看下
package.json
,主要是在公司前端代码规范基础上进行定制:name 包名一定要以
规则集 react.js
再看下 React 应用的全量规则集
react.js
(若有其它类型补充对应规则集文件即可,例如vue.js
、 node.js
、electron.js
)其中:
- 关闭
prettier
代码风格规则,让 eslint 只专注做错误代码校验
- 以
rulesdir/*
开头的规则属于团队定制(如何写见后文),使用原生 JS 文件(不用编译规则立即生效): rules/text-specification.js
:文案校验rules/jsx-no-numeric-and
:避免出现0 && <div />
展示 0 的问题rules/lodash-import
:前端项目优先使用lodash-es
而非lodash
这里关掉了不少『公司级代码规范』规则,原则是只使用『能拦截有效错误的规则』
应用中使用
都是在
apps/*/package.json
中添加 eslint 规则集 npm 依赖使用默认规则
在
apps/{子应用}
里新建 .eslintrc.js
:规则开关
在
apps/{子应用}
里新建 .eslintrc.js
,在 rules
里对具体规则进行 开/关、配置:所有 ESLint 规则都放到 packages/eslint 里面,应用使用时只做规则开/关
完全关闭/不使用 lint
不新建
apps/{子应用}/.eslintrc.js
,这时候会使用根目录下的 .eslintrc.js
规则(即ignorePatterns
忽略所有文件检测):工程化
为了系统性严格执行 ESLint 规则,这里在两个阶段对增量代码进行 eslint 检测:
- Git 提交前:在本地对变更的代码执行 eslint 检测
- CI MR 环节:对当前 MR 中的代码做检测(主要防止通过
git commit -n
方式绕过本地检测)
Git 提交(precommit)
这里使用较成熟的 husky + lint-staged,主要改动:
- 根目录
package.json
- 执行 husky 安装
- 配置根目录的
.lintstagedrc.json
需执行的校验命令
这样每次
git commit
时就会执行规则检测:CI
CI 为了提高 CR 效率,CI 不过 不 CR,其中 CI 配置有一些优化点:
- 使用
git diff
将 MR 中变更文件筛选出来做增量检测
- Lint CI 单独配置,这样可以更快执行 lint,给开发者更快地反馈(一次 push 大概 1min 内就可以知道校验结果)
完整配置如下:
一些感受
- 只有团队定制的 eslint 规则才有较高价值
- 99% 的规则应该由 AIGC(人工智能生成内容) 来完成