本文目录导读:

- 使用 ESLint 的复杂度规则
- 使用 Plato 工具
- 使用 Python 脚本来分析
- 使用 JavaScript 自定义检查脚本
- 使用 Husky + lint-staged(推荐)
- 高级检查 - 多种复杂度指标
- 最佳实践
Git钩子脚本可以通过多种方式检查函数复杂度,以下是几种常见的方法:
使用 ESLint 的复杂度规则
安装配置
npm install --save-dev eslint eslint-plugin-complexity
创建 .eslintrc.js
module.exports = {
rules: {
'complexity': ['error', { max: 10 }], // 最大圈复杂度
}
};
pre-commit 钩子脚本
#!/bin/bash # .git/hooks/pre-commit echo "检查函数复杂度..." # 获取暂存区的 .js 文件 STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep ".js$") if [ -z "$STAGED_FILES" ]; then echo "没有需要检查的 JS 文件" exit 0 fi # 运行 ESLint 检查复杂度 npx eslint --rule 'complexity: ["error", 10]' $STAGED_FILES if [ $? -ne 0 ]; then echo "复杂度检查失败!请简化函数复杂度" exit 1 fi echo "复杂度检查通过"
使用 Plato 工具
安装 Plato
npm install -g plato # 或 yarn global add plato
pre-commit 脚本
#!/bin/bash
# .git/hooks/pre-commit
THRESHOLD=15 # 复杂度阈值
# 检查每个暂存的文件
for file in $(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(js|ts|jsx|tsx)$'); do
if [ -f "$file" ]; then
# 使用 plato 检查复杂度
plato -r -d report_temp -t "Complexity Check" "$file" > /dev/null 2>&1
# 解析报告中的复杂性信息
COMPLEXITY=$(grep -oP '(?<=cyclomatic complexity )[0-9]+' report_temp/report.json 2>/dev/null)
if [ ! -z "$COMPLEXITY" ] && [ "$COMPLEXITY" -gt "$THRESHOLD" ]; then
echo "⚠️ 文件 $file 中存在函数复杂度超标 ($COMPLEXITY > $THRESHOLD)"
rm -rf report_temp
exit 1
fi
rm -rf report_temp
fi
done
echo "复杂度检查通过"
使用 Python 脚本来分析
安装 radon
pip install radon
pre-commit 脚本
#!/usr/bin/env python3
# .git/hooks/pre-commit-check.py
import subprocess
import json
from radon.complexity import cc_visit
from radon.visitors import Function
def check_complexity():
# 获取暂存的文件
result = subprocess.run(
['git', 'diff', '--cached', '--name-only', '--diff-filter=ACM'],
capture_output=True, text=True
)
files = [f for f in result.stdout.split('\n') if f.endswith('.py')]
threshold = 10 # 圈复杂度阈值
failed = False
for file_path in files:
try:
with open(file_path, 'r', encoding='utf-8') as f:
code = f.read()
# 分析复杂度
functions = cc_visit(code)
for func in functions:
if func.complexity > threshold:
print(f"⚠️ 文件 {file_path}")
print(f" 函数 {func.name} 复杂度: {func.complexity} (阈值: {threshold})")
failed = True
except Exception as e:
print(f"❌ 分析 {file_path} 时出错: {e}")
return failed
if __name__ == "__main__":
if check_complexity():
print("\n❌ 复杂度检查失败!请简化高复杂度函数")
exit(1)
else:
print("✅ 复杂度检查通过")
exit(0)
pre-commit 脚本调用
#!/bin/bash # .git/hooks/pre-commit python3 .git/hooks/pre-commit-check.py
使用 JavaScript 自定义检查脚本
创建复杂度检查工具
// complexity-check.js
const fs = require('fs');
const path = require('path');
function calculateCyclomaticComplexity(code) {
// 匹配会增加复杂度的关键字
const patterns = [
/if/g, /else if/g, /for/g, /while/g, /case/g,
/catch/g, /\&\&/g, /\|\|/g, /\?/g
];
let complexity = 1; // 基础复杂度
patterns.forEach(pattern => {
const matches = code.match(pattern);
if (matches) {
complexity += matches.length;
}
});
return complexity;
}
// 解析函数
function extractFunctions(code) {
const functionRegex = /(?:function\s+(\w+)|(\w+)\s*=\s*function|(\w+)\s*=>\s*{)/g;
const functions = [];
let match;
while ((match = functionRegex.exec(code)) !== null) {
const name = match[1] || match[2] || match[3];
const startIndex = match.index;
// 简单找到函数体结束
let endIndex = code.indexOf('}', startIndex);
if (endIndex > -1) {
const functionCode = code.substring(startIndex, endIndex + 1);
functions.push({
name,
code: functionCode,
start: startIndex,
end: endIndex
});
}
}
return functions;
}
function processFile(filePath, threshold = 10) {
const code = fs.readFileSync(filePath, 'utf8');
const functions = extractFunctions(code);
const issues = [];
functions.forEach(func => {
const complexity = calculateCyclomaticComplexity(func.code);
if (complexity > threshold) {
issues.push({
file: filePath,
function: func.name || 'anonymous',
complexity,
threshold
});
}
});
return issues;
}
// 主函数
const threshold = 10;
const stagedFiles = process.argv[2].split('\n');
let hasErrors = false;
stagedFiles.forEach(file => {
if (file.endsWith('.js') || file.endsWith('.ts')) {
const issues = processFile(file, threshold);
issues.forEach(issue => {
console.log(`⚠️ 函数复杂度超标: ${issue.file} - ${issue.function}`);
console.log(` 复杂度: ${issue.complexity} (阈值: ${issue.threshold})`);
hasErrors = true;
});
}
});
if (hasErrors) {
process.exit(1);
}
pre-commit 脚本
#!/bin/bash # .git/hooks/pre-commit echo "检查函数复杂度..." STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(js|ts)$') if [ -z "$STAGED_FILES" ]; then exit 0 fi node complexity-check.js "$STAGED_FILES" if [ $? -ne 0 ]; then echo "❌ 复杂度检查失败" exit 1 fi echo "✅ 复杂度检查通过"
使用 Husky + lint-staged(推荐)
安装和配置
npm install --save-dev husky lint-staged eslint
package.json 配置
{
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.js": [
"eslint --rule 'complexity: [\"error\", 10]'",
"git add"
]
}
}
高级检查 - 多种复杂度指标
综合检查脚本
#!/usr/bin/env python3
"""综合复杂度检查"""
import ast
import subprocess
from collections import defaultdict
class ComplexityChecker(ast.NodeVisitor):
def __init__(self):
self.complexities = defaultdict(int)
self.current_function = None
def visit_FunctionDef(self, node):
self.current_function = node.name
self.complexities[node.name] = 1 # 基础复杂度
self.generic_visit(node)
def visit_If(self, node):
if self.current_function:
self.complexities[self.current_function] += 1
self.generic_visit(node)
def visit_While(self, node):
if self.current_function:
self.complexities[self.current_function] += 1
self.generic_visit(node)
def visit_For(self, node):
if self.current_function:
self.complexities[self.current_function] += 1
self.generic_visit(node)
def visit_BoolOp(self, node):
if self.current_function:
# 每个布尔运算符增加复杂度
self.complexities[self.current_function] += len(node.values) - 1
self.generic_visit(node)
def check_files():
result = subprocess.run(
['git', 'diff', '--cached', '--name-only', '--diff-filter=ACM'],
capture_output=True, text=True
)
files = [f for f in result.stdout.split('\n') if f.endswith('.py')]
threshold = 10
failed = False
for file_path in files:
try:
with open(file_path, 'r') as f:
tree = ast.parse(f.read())
checker = ComplexityChecker()
checker.visit(tree)
for func_name, complexity in checker.complexities.items():
if complexity > threshold:
print(f"⚠️ {file_path}:{func_name} 复杂度 {complexity} (阈值 {threshold})")
failed = True
except SyntaxError:
print(f"❌ {file_path}: 语法错误")
failed = True
return failed
if __name__ == "__main__":
if check_files():
print("\n❌ 复杂度检查失败")
exit(1)
else:
print("✅ 复杂度检查通过")
exit(0)
最佳实践
-
阈值设置:
- 圈复杂度:建议 10-15
- 认知复杂度:建议 15-20
-
性能优化:
- 只检查暂存的文件
- 缓存已检查的结果
-
错误处理:
- 提供清晰的错误信息
- 标记具体的问题函数
-
集成到 CI/CD:
# .github/workflows/complexity.yml name: Check Complexity on: [push, pull_request] jobs: check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Check complexity run: | pip install radon radon cc . -s -n 10 -e "tests/*"
这些方法可以根据项目的技术栈和需求选择和调整,建议从简单的 ESLint 规则开始,逐步增加更复杂的检查。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播