在前端项目的长期维护中,我们经常会遇到这样的痛点:代码能跑,但"味道"不对。
虽然我们已经有了 ESLint 标准规则、Prettier 格式化,甚至引入了 SonarJS 这样严格的质量检测工具,但在实际的业务场景(特别是大型复杂应用)中,通用的规则往往显得"笨重"------要么太宽泛起不到约束作用,要么太死板导致满屏 // eslint-disable。
为了解决这些"现有规则无法满足的特定需求",我开发了 eslint-plugin-aegis。
Aegis (埃癸斯) 是希腊神话中雅典娜和宙斯持有的神盾,象征着保护、权威与智慧。我希望这个插件能像 Aegis 一样,成为守护代码质量的一道防线。
💡 背景:我们为什么需要它?
既然社区里已经有几千个插件了,为什么要造这个轮子?
核心原因只有一个:灵活性与业务场景的深度结合。
很多通用规则在设计时,很难顾及到具体的业务上下文。例如:
- 重复字符串检测 :通用规则会把所有重复字符串都揪出来。但在做国际化(i18n)时,
t('confirm')出现几十次是很正常的,强制提取常量反而降低了可读性。 - 魔法数字 :通用规则禁止所有数字。但在写 CSS-in-JS 或配置 UI 布局时,
exclude: ['width', 'opacity']这种简单的忽略名单往往不够用,我们需要更强大的正则匹配来忽略一类属性。 - 复杂对象类型 :虽然 TS 具备强大的推导能力,但在面对复杂对象时,缺乏显式定义的隐式推导 或过于臃肿的内联类型(Inline Type Literal) 会让代码契约变得模糊且难以复用,显著增加项目的维护难度。
eslint-plugin-aegis 就是为了解决这些细微但影响深远的痛点而生。
🚀 核心能力概览
1. 灵活可控的重复字符串检测:no-duplicate-string
痛点 :
sonarjs/no-duplicate-string很好,但它"杀伤力"太大了。有时候我只是在调用打点函数或者国际化函数,它也报错。
Aegis 的去重规则支持了更细粒度的控制:
- 🎯 基于上下文的忽略 :支持配置
ignorePatterns正则。不仅可以忽略特定格式的字符串(如 URL、CSS 单位),更强大的是它是可以忽略特定函数调用中的字符串 。- 场景 :
t('submit')和track('submit')中的'submit'虽然重复,但只要配置正则匹配t和track函数名,就能自动忽略这些调用中的字符串,无需手动 disable。
- 场景 :
- 🧩 精准避让类型定义 :自动忽略 TypeScript 的字面量类型定义(如
type Status = 'active' | 'active'),这通常是合法的业务定义。
配置项 (Options)
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
threshold |
integer |
2 |
重复次数阈值。达到该值时报错。 |
minLength |
integer |
5 |
触发检查的字符串最小长度。 |
ignoreStrings |
string[] |
[] |
精确匹配忽略的字符串列表。 |
ignorePatterns |
string[] |
[] |
正则表达式列表,匹配的字符串或所在的函数名将被忽略。 |
ignoreTSLiteralTypes |
boolean |
true |
是否忽略 TypeScript 的字面量类型 (例如 type T = 'draft')。 |
示例配置
javascript
"aegis/no-duplicate-string": ["error", {
"threshold": 2,
"minLength": 2,
"ignoreStrings": [
"application/json", // 常见 MIME
"YYYY-MM-DD" // 日期格式
],
"ignorePatterns": [
"^/", // 路径
"^http(s)?://", // URL
"^#([0-9A-Fa-f]{3,6})$", // HEX 颜色
"^[0-9]+(px|rem|em|vh|vw|%)$", // CSS 单位
"^t$" // 忽略国际化函数 t('...') 中的字符串
]
}]
2. 拒绝隐式黑盒:no-implicit-complex-object
痛点 :接手老代码时,看到
const config = { ...50个属性... }且没有类型定义,想知道这个对象到底长什么样,只能靠猜或者看赋值。而且在 Vue 3 中,不显式定义泛型的ref往往会降低类型提示的直观性。
这个规则强制要求:当对象字面量的属性超过一定数量(默认 2 个)时,必须显式定义 Interface 或 Type。
这不仅仅是为了 TS 类型检查,更是为了代码即文档。
- ⚡️ Vue 3 友好 :自动识别
ref()和reactive()。 - 🔧 渐进式 :可以通过
propertyThreshold调整阈值,逐步收紧标准。 - 📥 函数参数支持 :不仅检查变量赋值,还会自动检查函数参数中的复杂内联类型(如
function(user: { name: string, age: number }))。
配置项 (Options)
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
propertyThreshold |
integer |
2 |
属性数量阈值。当对象属性达到该值时且未显式定义类型时报错。 |
ignoreVue3Wrappers |
boolean |
false |
是否忽略 Vue 3 的 ref() 和 reactive() 包装对象。 |
ignorePatterns |
string[] |
[] |
正则表达式列表,匹配的变量名将被忽略。 |
示例配置
javascript
"aegis/no-implicit-complex-object": ["error", {
"propertyThreshold": 2,
"ignorePatterns": [".*Rules$"]
}]
typescript
// ❌ Error: 复杂对象未定义类型
const user = { name: "Li", age: 18, role: "admin", dept: "IT" };
// ✅ Pass: 显式契约,结构清晰
const user: IUser = { name: "Li", age: 18, role: "admin", dept: "IT" };
3. 严谨的魔法数字:no-magic-numbers-strict
痛点 :原生
no-magic-numbers很难用。在 UI 开发中,width: 100或opacity: 0.5都要提取常量实在太繁琐。
Aegis 复刻并增强了这一规则,加入了强大的正则过滤能力:
- 🎨 属性名正则过滤 :通过
ignorePropertyPatterns,你可以忽略width,minHeight,flexGrow甚至是所有以Cls结尾的属性中的数字。 - ⚙️ 函数调用正则过滤 :
setTimeout(fn, 500)中的500是魔法数字吗?也许是,但在这种通用函数中,我们通常希望放行。Aegis 允许你通过正则忽略特定函数(如setTimeout,Date)的参数。
配置项 (Options)
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
ignore |
number[] |
[] |
忽略的具体数字列表。 |
detectObjects |
boolean |
false |
是否检测对象属性中的数字。 |
enforceConst |
boolean |
false |
是否强制要求变量声明使用 const。 |
ignoreArrayIndexes |
boolean |
false |
是否忽略数组索引 arr[0]。 |
ignoreEnums |
boolean |
false |
是否忽略 enum 定义中的数字。 |
ignoreTypeIndexes |
boolean |
false |
是否忽略 TS 类型索引 Data[0]。 |
ignoreNumericLiteralTypes |
boolean |
false |
是否忽略 TS 数字字面量类型。 |
ignoreReadonlyClassProperties |
boolean |
false |
是否忽略类中的 readonly 属性初始值。 |
ignoreDefaultValues |
boolean |
false |
是否忽略参数默认值中的数字。 |
ignoreClassFieldInitialValues |
boolean |
false |
是否忽略类字段初始值。 |
ignorePropertyPatterns |
string[] |
[] |
正则表达式列表,匹配的对象属性名将被忽略。 |
ignoreCalleePatterns |
string[] |
[] |
正则表达式列表,作为这些函数参数的数字将被忽略。 |
示例配置
javascript
"aegis/no-magic-numbers-strict": ["error", {
"ignore": [-1, 0, 1, 2],
"ignoreArrayIndexes": true,
"ignoreTypeIndexes": true,
"ignoreEnums": true,
"detectObjects": true,
"enforceConst": true,
"ignorePropertyPatterns": ["^width$|Width$"],
"ignoreCalleePatterns": ["^(Date|setTimeout|setInterval|delay)$"]
}]
🛠 快速开始
插件已发布至 npm,支持 ESLint Flat Config。
安装
bash
npm install eslint-plugin-aegis --save-dev
# or
yarn add eslint-plugin-aegis -D
配置
在 eslint.config.js 中引入并配置:
javascript
import aegis from "eslint-plugin-aegis";
export default [
// 1. 使用推荐配置 (开箱即用)
aegis.configs.recommended,
// 2. 或者按需深度定制
{
plugins: {
aegis,
},
rules: {
// 检测重复字符串
"aegis/no-duplicate-string": [
"error",
{
threshold: 2, // 阈值
minLength: 2, // 最小检测长度
ignoreStrings: [], // 忽略的字符串列表
ignorePatterns: ["^t$", "^track$"], // 忽略 i18n 和埋点函数
},
],
// 检测魔法数字
"aegis/no-magic-numbers-strict": [
"error",
{
ignore: [-1, 0, 1, 2], // 忽略的魔法数字
ignoreEnums: true, // 忽略枚举定义
ignoreTypeIndexes: true, // 忽略类型索引
ignoreArrayIndexes: true, // 忽略数组索引
detectObjects: true, // 检测对象
enforceConst: true, // 允许使用 const 声明魔法数字,非 const 声明的会提示
ignorePropertyPatterns: ["^width$|Width$"], // 忽略的属性
ignoreCalleePatterns: ["^(Date|setTimeout|delay)$"], // 忽略的调用
},
],
// 检测隐式复杂对象
"aegis/no-implicit-complex-object": [
"error",
{
propertyThreshold: 2,
ignorePatterns: [".*Rules$"],
},
],
},
},
];
结语
工具的目的是服务于人,而不是束缚人。eslint-plugin-aegis 的初衷就是在严格的代码质量要求 和流畅的开发体验之间找到一个平衡点。
如果你也在被通用的 Lint 规则误伤,或者希望对团队代码风格有更细粒度的掌控,欢迎尝试一下 Aegis,也欢迎来 GitHub 提 Issue 或 PR,共同完善这面"神盾"!
🔗 GitHub : github.com/LLLaiYoung/...
Let's build better code, together.