欢迎光临
我们一直在努力

代码规范工程化:从ESLint到AI辅助Review的自动化体系

代码规范工程化:从ESLint到AI辅助Review的自动化体系

一、代码规范的"纸面文章":为什么定了规范还是写不整齐

每个团队都有代码规范,但大部分规范只存在于文档里。Code Review时指出风格问题,对方改了这一处,下一处照样犯。不是态度问题,是工程问题——靠人执行规范,成本高、一致性差、容易遗漏。

ESLint解决了大部分格式问题,但解决不了架构层面的一致性。比如:React组件应该用函数组件还是类组件?状态管理应该用useState还是Zustand?API调用应该统一封装还是各自写?这些规范超出了ESLint的能力范围,需要更高级的自动化手段。

AI辅助Review是代码规范的新方向。LLM可以理解代码的语义,判断是否符合团队的架构约定。但AI Review的挑战在于:判断标准不明确、结果不确定、速度慢。如何将AI Review融入现有的CI/CD流程,是工程化的核心问题。

二、代码规范自动化体系架构

2.1 三层规范体系

flowchart TD
A[代码规范体系] –> B[格式层<br/>Prettier/EditorConfig]
A –> C[规则层<br/>ESLint/TypeScript]
A –> D[架构层<br/>AI辅助Review]

B –> B1[自动格式化<br/>零人工介入]
C –> C1[静态分析<br/>CI门禁]
D –> D1[语义理解<br/>Review辅助]

B –> E[100%自动化]
C –> F[95%自动化]
D –> G[辅助决策]

2.2 自动化执行链路

flowchart LR
A[开发者提交代码] –> B[Pre-commit Hook]
B –> C[Prettier格式化]
C –> D[ESLint检查]
D –> E[TypeScript编译]
E –> F[Git Push]

F –> G[CI Pipeline]
G –> H[ESLint –strict]
G –> I[单元测试]
G –> J[AI Review]

H –> K[结果合并]
I –> K
J –> K

K –>|通过| L[允许合并]
K –>|不通过| M[阻止合并]

三、规范自动化实现

3.1 ESLint深度配置

// .eslintrc.cjs – 生产级ESLint配置
module.exports = {
root: true,
env: {
browser: true,
es2022: true,
node: true,
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:jsx-a11y/recommended',
'prettier', // 必须放在最后,覆盖其他配置的格式规则
],
plugins: ['@typescript-eslint', 'react', 'import'],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
ecmaFeatures: { jsx: true },
},
settings: {
react: { version: 'detect' },
'import/resolver': {
typescript: true,
node: true,
},
},
rules: {
// ========== 错误级别:必须修复 ==========

// 禁止any类型(逐步收紧)
'@typescript-eslint/no-explicit-any': 'error',

// 禁止未使用的变量
'@typescript-eslint/no-unused-vars': ['error', {
argsIgnorePattern: '^_', // 以_开头的参数允许未使用
varsIgnorePattern: '^_',
}],

// 禁止console(生产代码)
'no-console': ['error', { allow: ['warn', 'error'] }],

// React规则
'react/react-in-jsx-scope': 'off', // React 17+不需要
'react/prop-types': 'off', // 使用TypeScript类型检查
'react/no-array-index-key': 'error', // 禁止用index做key
'react-hooks/exhaustive-deps': 'error', // 依赖数组必须完整

// 导入规则
'import/no-cycle': 'error', // 禁止循环依赖
'import/no-duplicates': 'error', // 禁止重复导入
'import/order': ['error', { // 导入排序
groups: [
'builtin', // Node内置模块
'external', // 第三方依赖
'internal', // 项目内部模块
'parent', // 父级目录
'sibling', // 同级目录
'index', // 当前目录
'type', // 类型导入
],
'newlines-between': 'never',
alphabetize: { order: 'asc' },
}],

// ========== 警告级别:建议修复 ==========

// 函数复杂度
'complexity': ['warn', { max: 10 }],

// 函数参数数量
'max-params': ['warn', { max: 4 }],

// 嵌套深度
'max-depth': ['warn', { max: 4 }],

// 文件行数
'max-lines': ['warn', { max: 300, skipBlankLines: true, skipComments: true }],

// ========== 自定义规则:架构约定 ==========

// 禁止直接使用axios(必须通过封装的http模块)
'no-restricted-imports': ['error', {
patterns: [{
group: ['axios'],
message: '请使用 @/utils/http 替代直接导入 axios',
}],
}],
},
overrides: [
// 测试文件放宽规则
{
files: ['**/*.test.ts', '**/*.test.tsx', '**/*.spec.ts'],
rules: {
'@typescript-eslint/no-explicit-any': 'off',
'max-lines': 'off',
},
},
],
};

3.2 自定义ESLint规则

// eslint-rules/no-direct-state-mutation.js
// 禁止直接修改Zustand store的状态(必须通过action)

module.exports = {
meta: {
type: 'problem',
docs: {
description: '禁止直接修改Zustand store状态',
category: 'Architecture',
},
messages: {
noDirectMutation: '禁止直接修改store状态,请使用store的action方法',
},
},

create(context) {
return {
// 检测 store.xxx = yyy 的赋值模式
AssignmentExpression(node) {
const left = node.left;
if (left.type === 'MemberExpression') {
const objectName = left.object.name;
// 检查是否为store变量(以Store结尾)
if (objectName && objectName.endsWith('Store')) {
context.report({
node,
messageId: 'noDirectMutation',
});
}
}
},

// 检测 store.xxx.yyy = zzz 的深层赋值
MemberExpression(node) {
if (
node.parent.type === 'AssignmentExpression' &&
node.parent.left === node
) {
let obj = node.object;
while (obj.type === 'MemberExpression') {
obj = obj.object;
}
if (obj.type === 'Identifier' && obj.name.endsWith('Store')) {
context.report({
node: node.parent,
messageId: 'noDirectMutation',
});
}
}
},
};
},
};

3.3 AI辅助Review

// ai-reviewer.ts – AI代码Review工具
interface ReviewComment {
file: string;
line: number;
severity: 'error' | 'warning' | 'suggestion';
category: string;
message: string;
suggestion?: string;
}

interface ProjectConvention {
stateManagement: string; // zustand/redux/jotai
cssSolution: string; // tailwind/css-modules/styled-components
apiPattern: string; // react-query/swr/raw-fetch
componentPattern: string; // functional/class
namingConvention: string; // camelCase/PascalCase
}

class AIReviewer {
private llmClient: LLMClient;
private convention: ProjectConvention;

constructor(llmClient: LLMClient, convention: ProjectConvention) {
this.llmClient = llmClient;
this.convention = convention;
}

/**
* Review代码变更
*/
async review(diff: string): Promise<ReviewComment[]> {
const prompt = `你是一个代码Review助手。请根据以下项目约定,检查代码变更是否符合规范。

项目约定:
– 状态管理: ${this.convention.stateManagement}
– CSS方案: ${this.convention.cssSolution}
– API调用: ${this.convention.apiPattern}
– 组件模式: ${this.convention.componentPattern}
– 命名风格: ${this.convention.namingConvention}

请检查以下问题:
1. 是否使用了项目约定之外的技术方案
2. 是否有违反架构原则的代码(如直接修改store状态)
3. 是否有明显的性能问题(如在渲染函数中创建新对象)
4. 是否有安全隐患(如XSS、未验证的用户输入)
5. 是否缺少错误处理

代码变更:
${diff}

请以JSON数组格式返回Review意见:
[{
"file": "文件路径",
"line": 行号,
"severity": "error/warning/suggestion",
"category": "问题类别",
"message": "问题描述",
"suggestion": "修改建议"
}]

如果没有问题,返回空数组 []`;

const response = await this.llmClient.chat(prompt);

try {
return JSON.parse(response);
} catch {
return [];
}
}
}

// CI集成脚本
async function runAIReview(): Promise<void> {
const llmClient = new LLMClient();
const reviewer = new AIReviewer(llmClient, {
stateManagement: 'zustand',
cssSolution: 'tailwind',
apiPattern: 'react-query',
componentPattern: 'functional',
namingConvention: 'camelCase',
});

// 获取当前PR的diff
const diff = await getPRDiff();
const comments = await reviewer.review(diff);

// 将Review意见发布到PR
for (const comment of comments) {
await postPRComment(comment);
}

// 如果有error级别的问题,CI失败
const hasErrors = comments.some(c => c.severity === 'error');
if (hasErrors) {
process.exit(1);
}
}

3.4 Git Hooks配置

# .husky/pre-commit
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

# 1. Prettier格式化
npx pretty-quick –staged

# 2. ESLint检查(只检查暂存文件)
npx lint-staged

# 3. TypeScript类型检查
npx tsc –noEmit

// lint-staged.config.cjs
module.exports = {
'*.{ts,tsx}': [
'eslint –fix',
() => 'tsc –noEmit', // 类型检查
],
'*.{css,scss}': [
'stylelint –fix',
],
'*.{json,md}': [
'prettier –write',
],
};

四、规范自动化的边界与权衡

4.1 ESLint规则的维护成本

自定义规则需要随项目演进更新。技术栈变更(如从Redux迁移到Zustand)时,相关规则需要同步修改。建议将规则纳入版本管理,变更时通知全团队。

4.2 AI Review的误报率

AI Review可能产生误报:将合理的代码标记为问题。建议将AI Review的结果标记为"suggestion"而非"error",由开发者判断是否采纳。随着反馈数据的积累,可以逐步提高AI Review的置信度。

4.3 CI时间增加

ESLint、TypeScript编译、AI Review都会增加CI时间。建议分层执行:Pre-commit只做快速检查(格式化+ESLint),CI做完整检查(类型检查+AI Review+测试),保持提交体验流畅。

4.4 禁用场景

代码规范自动化不适合以下场景:原型验证阶段(规范会拖慢迭代速度);遗留系统改造(先让代码跑起来,再逐步规范);个人项目(规范是团队协作的工具,个人项目不需要)。

五、总结

代码规范自动化的核心是"三层递进":Prettier处理格式,ESLint处理规则,AI Review处理架构约定。每一层的自动化程度不同:格式层100%自动化,规则层95%自动化(少数需要人工判断),架构层作为辅助决策。

工程落地的关键:Git Hooks在提交前拦截问题,CI在合并前做完整检查,AI Review补充静态分析无法覆盖的语义问题。规范不是约束,是协作的基础设施。自动化程度越高,团队在规范上花费的沟通成本越低。

赞(0)
未经允许不得转载:171主机测评 » 代码规范工程化:从ESLint到AI辅助Review的自动化体系
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址