code-simplifier 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -1
- package/bin/code-simplifier.js +73 -1
- package/cypress.config.js +45 -0
- package/jest.config.js +46 -0
- package/lib/auto-fix.js +382 -0
- package/lib/eslint-integration.js +328 -0
- package/lib/git-hooks.js +353 -0
- package/lib/master.js +117 -0
- package/lib/multi-language-analyzer.js +397 -0
- package/package.json +20 -4
package/README.md
CHANGED
|
@@ -11,7 +11,10 @@
|
|
|
11
11
|
- **质量监控** - 实时监控代码质量指标
|
|
12
12
|
- **知识库管理** - 积累最佳实践和经验教训
|
|
13
13
|
- **自动更新** - 保持工具始终最新
|
|
14
|
-
- **多语言支持** -
|
|
14
|
+
- **多语言支持** - JavaScript、TypeScript、Python、Java、C#、C++、PHP、Go、Rust、Kotlin、Swift
|
|
15
|
+
- **ESLint 集成** - 自动运行 ESLint 分析和修复
|
|
16
|
+
- **Git 钩子** - 自动安装 pre-commit 和 pre-push 钩子
|
|
17
|
+
- **自动修复** - 智能修复常见代码问题
|
|
15
18
|
|
|
16
19
|
## 快速开始
|
|
17
20
|
|
|
@@ -153,6 +156,58 @@ npx code-simplifier update
|
|
|
153
156
|
npx code-simplifier update --force
|
|
154
157
|
```
|
|
155
158
|
|
|
159
|
+
### eslint - ESLint 代码分析
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
# 运行 ESLint 分析
|
|
163
|
+
npx code-simplifier eslint
|
|
164
|
+
|
|
165
|
+
# 自动修复可修复的问题
|
|
166
|
+
npx code-simplifier eslint --fix
|
|
167
|
+
|
|
168
|
+
# 输出 JSON 格式
|
|
169
|
+
npx code-simplifier eslint --format json
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### hooks - Git 钩子管理
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
# 交互式安装 Git 钩子
|
|
176
|
+
npx code-simplifier hooks
|
|
177
|
+
|
|
178
|
+
# 安装 pre-commit 和 pre-push 钩子
|
|
179
|
+
npx code-simplifier hooks --install
|
|
180
|
+
|
|
181
|
+
# 列出已安装的钩子
|
|
182
|
+
npx code-simplifier hooks --list
|
|
183
|
+
|
|
184
|
+
# 卸载钩子
|
|
185
|
+
npx code-simplifier hooks --uninstall
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### autofix - 自动修复
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
# 自动修复代码问题
|
|
192
|
+
npx code-simplifier autofix
|
|
193
|
+
|
|
194
|
+
# 预览模式(不实际修改文件)
|
|
195
|
+
npx code-simplifier autofix --dry-run
|
|
196
|
+
|
|
197
|
+
# 指定目录
|
|
198
|
+
npx code-simplifier autofix --dir src
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### lang - 多语言分析
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
# 分析项目中的多种语言
|
|
205
|
+
npx code-simplifier lang
|
|
206
|
+
|
|
207
|
+
# 指定目录
|
|
208
|
+
npx code-simplifier lang --dir src
|
|
209
|
+
```
|
|
210
|
+
|
|
156
211
|
## 配置文件
|
|
157
212
|
|
|
158
213
|
配置存储在 `.code-simplifier/config.json`:
|
package/bin/code-simplifier.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Code-Simplifier 持续改进系统
|
|
@@ -178,6 +178,78 @@ program
|
|
|
178
178
|
config.run(options)
|
|
179
179
|
})
|
|
180
180
|
|
|
181
|
+
// eslint命令
|
|
182
|
+
program
|
|
183
|
+
.command('eslint')
|
|
184
|
+
.description(chalk.red('🔍 ESLint 代码分析'))
|
|
185
|
+
.option('--fix', '自动修复可修复的问题', false)
|
|
186
|
+
.option('-f, --format <type>', '输出格式 (json|text)', 'json')
|
|
187
|
+
.action(async (options) => {
|
|
188
|
+
try {
|
|
189
|
+
const eslint = require('../lib/eslint-integration')
|
|
190
|
+
await eslint.runAnalysis('.', options)
|
|
191
|
+
} catch (error) {
|
|
192
|
+
console.error(chalk.red('❌ ESLint 分析失败:'), error.message)
|
|
193
|
+
process.exit(1)
|
|
194
|
+
}
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
// hooks命令
|
|
198
|
+
program
|
|
199
|
+
.command('hooks')
|
|
200
|
+
.description(chalk.yellow('🔧 Git 钩子管理'))
|
|
201
|
+
.option('--install', '安装 Git 钩子')
|
|
202
|
+
.option('--uninstall', '卸载 Git 钩子')
|
|
203
|
+
.option('--list', '列出已安装的钩子')
|
|
204
|
+
.action(async (options) => {
|
|
205
|
+
try {
|
|
206
|
+
const master = require('../lib/master')
|
|
207
|
+
await master.manageGitHooks(options)
|
|
208
|
+
} catch (error) {
|
|
209
|
+
console.error(chalk.red('❌ 钩子管理失败:'), error.message)
|
|
210
|
+
process.exit(1)
|
|
211
|
+
}
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
// autofix命令
|
|
215
|
+
program
|
|
216
|
+
.command('autofix')
|
|
217
|
+
.description(chalk.green('🔧 自动修复代码问题'))
|
|
218
|
+
.option('-d, --dir <path>', '检查目录', 'src')
|
|
219
|
+
.option('--dry-run', '预览模式,不实际修改文件', false)
|
|
220
|
+
.action(async (options) => {
|
|
221
|
+
try {
|
|
222
|
+
const autoFix = require('../lib/auto-fix')
|
|
223
|
+
const result = await autoFix.run(options)
|
|
224
|
+
console.log(chalk.green('\n✅ 自动修复完成'))
|
|
225
|
+
console.log(chalk.cyan(` 检测问题: ${result.totalIssues}`))
|
|
226
|
+
console.log(chalk.cyan(` 已修复: ${result.applied}`))
|
|
227
|
+
console.log(chalk.cyan(` 跳过: ${result.skipped}`))
|
|
228
|
+
} catch (error) {
|
|
229
|
+
console.error(chalk.red('❌ 自动修复失败:'), error.message)
|
|
230
|
+
process.exit(1)
|
|
231
|
+
}
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
// lang命令
|
|
235
|
+
program
|
|
236
|
+
.command('lang')
|
|
237
|
+
.description(chalk.cyan('🌐 多语言代码分析'))
|
|
238
|
+
.option('-d, --dir <path>', '检查目录', '.')
|
|
239
|
+
.action(async (options) => {
|
|
240
|
+
try {
|
|
241
|
+
const multiLang = require('../lib/multi-language-analyzer')
|
|
242
|
+
const result = await multiLang.analyze('.', options)
|
|
243
|
+
if (result.success) {
|
|
244
|
+
console.log(chalk.green('\n✅ 多语言分析完成'))
|
|
245
|
+
console.log(chalk.cyan(` 检测到 ${result.languages.length} 种语言`))
|
|
246
|
+
}
|
|
247
|
+
} catch (error) {
|
|
248
|
+
console.error(chalk.red('❌ 多语言分析失败:'), error.message)
|
|
249
|
+
process.exit(1)
|
|
250
|
+
}
|
|
251
|
+
})
|
|
252
|
+
|
|
181
253
|
// demo命令
|
|
182
254
|
program
|
|
183
255
|
.command('demo')
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const { defineConfig } = require('cypress')
|
|
2
|
+
|
|
3
|
+
module.exports = defineConfig({
|
|
4
|
+
e2e: {
|
|
5
|
+
// 测试文件位置
|
|
6
|
+
specPattern: 'tests/e2e/**/*.cy.{js,jsx,ts,tsx}',
|
|
7
|
+
|
|
8
|
+
// 支持的文件类型
|
|
9
|
+
supportFile: 'tests/e2e/support/e2e.js',
|
|
10
|
+
|
|
11
|
+
// 测试结果和视频
|
|
12
|
+
videosFolder: 'tests/e2e/videos',
|
|
13
|
+
screenshotsFolder: 'tests/e2e/screenshots',
|
|
14
|
+
|
|
15
|
+
// 视口尺寸
|
|
16
|
+
viewportWidth: 1280,
|
|
17
|
+
viewportHeight: 720,
|
|
18
|
+
|
|
19
|
+
// 截图设置
|
|
20
|
+
screenshotOnRunFailure: true,
|
|
21
|
+
|
|
22
|
+
// 视频录制
|
|
23
|
+
video: false,
|
|
24
|
+
|
|
25
|
+
// 测试设置
|
|
26
|
+
defaultCommandTimeout: 10000,
|
|
27
|
+
requestTimeout: 10000,
|
|
28
|
+
responseTimeout: 10000,
|
|
29
|
+
|
|
30
|
+
// 环境变量
|
|
31
|
+
env: {
|
|
32
|
+
apiUrl: 'http://localhost:3000'
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
// baseUrl(用于测试监控仪表板)
|
|
36
|
+
baseUrl: 'http://localhost:3000',
|
|
37
|
+
|
|
38
|
+
setupNodeEvents(on, config) {
|
|
39
|
+
// 实现 node 事件监听器
|
|
40
|
+
// 例如:cypress run --record
|
|
41
|
+
// 或 cypress open
|
|
42
|
+
return config
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
})
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
// 测试环境
|
|
3
|
+
testEnvironment: 'node',
|
|
4
|
+
|
|
5
|
+
// 测试文件匹配模式
|
|
6
|
+
testMatch: [
|
|
7
|
+
'<rootDir>/tests/unit/**/*.test.js',
|
|
8
|
+
'<rootDir>/tests/integration/**/*.test.js'
|
|
9
|
+
],
|
|
10
|
+
|
|
11
|
+
// 覆盖率收集
|
|
12
|
+
collectCoverage: true,
|
|
13
|
+
collectCoverageFrom: [
|
|
14
|
+
'lib/**/*.js',
|
|
15
|
+
'!lib/**/*.test.js',
|
|
16
|
+
'!lib/**/node_modules/**',
|
|
17
|
+
'!lib/**/*.md'
|
|
18
|
+
],
|
|
19
|
+
|
|
20
|
+
// 覆盖率报告格式
|
|
21
|
+
coverageReporters: ['text', 'lcov', 'html'],
|
|
22
|
+
|
|
23
|
+
// 输出目录
|
|
24
|
+
coverageDirectory: 'coverage',
|
|
25
|
+
|
|
26
|
+
// 覆盖率阈值(当前阶段的目标)
|
|
27
|
+
// 暂时禁用全局阈值,因为大多数模块尚未测试
|
|
28
|
+
// coverageThreshold 将在添加更多测试后启用
|
|
29
|
+
|
|
30
|
+
// 模块路径映射
|
|
31
|
+
moduleNameMapper: {
|
|
32
|
+
'^@/(.*)$': '<rootDir>/lib/$1'
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
// 设置文件
|
|
36
|
+
setupFilesAfterEnv: ['<rootDir>/tests/setup.js'],
|
|
37
|
+
|
|
38
|
+
// 清除模拟
|
|
39
|
+
clearMocks: true,
|
|
40
|
+
|
|
41
|
+
// 详细输出
|
|
42
|
+
verbose: true,
|
|
43
|
+
|
|
44
|
+
// 在测试之间清理模拟
|
|
45
|
+
restoreMocks: true
|
|
46
|
+
}
|
package/lib/auto-fix.js
ADDED
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Code-Simplifier 自动修复
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs-extra')
|
|
8
|
+
const path = require('path')
|
|
9
|
+
const chalk = require('chalk')
|
|
10
|
+
const ora = require('ora')
|
|
11
|
+
|
|
12
|
+
class AutoFix {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.fixes = []
|
|
15
|
+
this.applied = []
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 运行自动修复
|
|
20
|
+
*/
|
|
21
|
+
async run(options = {}) {
|
|
22
|
+
console.log(chalk.blue.bold('\n🔧 Code-Simplifier 自动修复'))
|
|
23
|
+
console.log(chalk.gray('='.repeat(70)))
|
|
24
|
+
|
|
25
|
+
const spinner = ora(chalk.blue('正在分析代码问题...')).start()
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const {
|
|
29
|
+
dir = 'src',
|
|
30
|
+
pattern = '**/*.{js,ts,jsx,tsx,py,java}',
|
|
31
|
+
dryRun = false
|
|
32
|
+
} = options
|
|
33
|
+
|
|
34
|
+
// 1. 扫描文件
|
|
35
|
+
const files = await this.scanFiles(dir, pattern)
|
|
36
|
+
spinner.info(chalk.gray(`扫描文件: ${files.length} 个`))
|
|
37
|
+
|
|
38
|
+
// 2. 检测问题
|
|
39
|
+
const issues = await this.detectIssues(files)
|
|
40
|
+
spinner.info(chalk.gray(`检测到问题: ${issues.length} 个`))
|
|
41
|
+
|
|
42
|
+
// 3. 应用修复
|
|
43
|
+
if (!dryRun && issues.length > 0) {
|
|
44
|
+
const result = await this.applyFixes(issues, files)
|
|
45
|
+
spinner.succeed(chalk.green('自动修复完成'))
|
|
46
|
+
return result
|
|
47
|
+
} else if (issues.length > 0) {
|
|
48
|
+
spinner.info(chalk.yellow('这是预览模式,未实际修改文件'))
|
|
49
|
+
return {
|
|
50
|
+
success: true,
|
|
51
|
+
dryRun: true,
|
|
52
|
+
totalIssues: issues.length,
|
|
53
|
+
fixes: issues
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
spinner.succeed(chalk.green('没有发现需要修复的问题'))
|
|
57
|
+
return {
|
|
58
|
+
success: true,
|
|
59
|
+
dryRun: false,
|
|
60
|
+
totalIssues: 0,
|
|
61
|
+
fixes: []
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
} catch (error) {
|
|
65
|
+
spinner.fail(chalk.red('自动修复失败'))
|
|
66
|
+
throw error
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 扫描文件
|
|
72
|
+
*/
|
|
73
|
+
async scanFiles(dir, pattern) {
|
|
74
|
+
const glob = require('glob')
|
|
75
|
+
return await new Promise((resolve) => {
|
|
76
|
+
try {
|
|
77
|
+
glob(pattern, {
|
|
78
|
+
cwd: dir,
|
|
79
|
+
ignore: ['node_modules/**', 'dist/**', 'build/**', 'vendor/**', '.git/**'],
|
|
80
|
+
nodir: true
|
|
81
|
+
}, (err, matches) => {
|
|
82
|
+
resolve(err ? [] : matches)
|
|
83
|
+
})
|
|
84
|
+
} catch (e) {
|
|
85
|
+
resolve([])
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* 检测问题
|
|
92
|
+
*/
|
|
93
|
+
async detectIssues(files) {
|
|
94
|
+
const issues = []
|
|
95
|
+
|
|
96
|
+
for (const file of files) {
|
|
97
|
+
try {
|
|
98
|
+
const content = await fs.readFile(file, 'utf8')
|
|
99
|
+
const lines = content.split('\n')
|
|
100
|
+
|
|
101
|
+
// 检测各种问题
|
|
102
|
+
issues.push(...this.checkTrailingSpaces(file, lines))
|
|
103
|
+
issues.push(...this.checkConsoleLog(file, lines))
|
|
104
|
+
issues.push(...this.checkDebugger(file, lines))
|
|
105
|
+
issues.push(...this.checkMissingSemicolon(file, lines))
|
|
106
|
+
issues.push(...this.checkVarDeclaration(file, lines))
|
|
107
|
+
issues.push(...this.checkLongLine(file, lines))
|
|
108
|
+
issues.push(...this.checkTODO(file, lines))
|
|
109
|
+
} catch (error) {
|
|
110
|
+
// 忽略无法读取的文件
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return issues
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* 检查尾随空格
|
|
119
|
+
*/
|
|
120
|
+
checkTrailingSpaces(file, lines) {
|
|
121
|
+
const issues = []
|
|
122
|
+
lines.forEach((line, index) => {
|
|
123
|
+
if (/\s+$/.test(line) && line.trim().length > 0) {
|
|
124
|
+
issues.push({
|
|
125
|
+
file,
|
|
126
|
+
line: index + 1,
|
|
127
|
+
type: 'trailing-space',
|
|
128
|
+
message: 'Remove trailing whitespace',
|
|
129
|
+
fix: (content) => content.replace(/[ \t]+$/gm, '')
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
return issues
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* 检查 console.log
|
|
138
|
+
*/
|
|
139
|
+
checkConsoleLog(file, lines) {
|
|
140
|
+
const issues = []
|
|
141
|
+
lines.forEach((line, index) => {
|
|
142
|
+
if (line.includes('console.log') || line.includes('console.error')) {
|
|
143
|
+
issues.push({
|
|
144
|
+
file,
|
|
145
|
+
line: index + 1,
|
|
146
|
+
type: 'console-log',
|
|
147
|
+
message: 'Remove console statement',
|
|
148
|
+
fix: (content) => {
|
|
149
|
+
const lines = content.split('\n')
|
|
150
|
+
if (lines[index].includes('console.log')) {
|
|
151
|
+
lines[index] = lines[index].replace(/console\.log\([^)]*\);?/, '// TODO: Remove console.log')
|
|
152
|
+
}
|
|
153
|
+
if (lines[index].includes('console.error')) {
|
|
154
|
+
lines[index] = lines[index].replace(/console\.error\([^)]*\);?/, '// TODO: Remove console.error')
|
|
155
|
+
}
|
|
156
|
+
return lines.join('\n')
|
|
157
|
+
}
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
return issues
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* 检查 debugger
|
|
166
|
+
*/
|
|
167
|
+
checkDebugger(file, lines) {
|
|
168
|
+
const issues = []
|
|
169
|
+
lines.forEach((line, index) => {
|
|
170
|
+
if (line.trim() === 'debugger;') {
|
|
171
|
+
issues.push({
|
|
172
|
+
file,
|
|
173
|
+
line: index + 1,
|
|
174
|
+
type: 'debugger',
|
|
175
|
+
message: 'Remove debugger statement',
|
|
176
|
+
fix: (content) => {
|
|
177
|
+
const lines = content.split('\n')
|
|
178
|
+
lines[index] = lines[index].replace(/^\s*debugger;\s*$/, '// TODO: Remove debugger')
|
|
179
|
+
return lines.join('\n')
|
|
180
|
+
}
|
|
181
|
+
})
|
|
182
|
+
}
|
|
183
|
+
})
|
|
184
|
+
return issues
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* 检查缺失分号
|
|
189
|
+
*/
|
|
190
|
+
checkMissingSemicolon(file, lines) {
|
|
191
|
+
const issues = []
|
|
192
|
+
lines.forEach((line, index) => {
|
|
193
|
+
const trimmed = line.trim()
|
|
194
|
+
|
|
195
|
+
// 跳过注释、空行、函数定义等
|
|
196
|
+
if (trimmed.startsWith('//') ||
|
|
197
|
+
trimmed.startsWith('/*') ||
|
|
198
|
+
trimmed === '' ||
|
|
199
|
+
trimmed.endsWith(';') ||
|
|
200
|
+
trimmed.endsWith('{') ||
|
|
201
|
+
trimmed.endsWith('}') ||
|
|
202
|
+
trimmed.includes('function') ||
|
|
203
|
+
trimmed.includes('if') ||
|
|
204
|
+
trimmed.includes('for') ||
|
|
205
|
+
trimmed.includes('while')) {
|
|
206
|
+
return
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// 检查可能需要分号的语句
|
|
210
|
+
if (trimmed.includes('var ') ||
|
|
211
|
+
trimmed.includes('let ') ||
|
|
212
|
+
trimmed.includes('const ') ||
|
|
213
|
+
/^[a-zA-Z_$][a-zA-Z0-9_$]*\s*=\s*[^;]+$/.test(trimmed)) {
|
|
214
|
+
issues.push({
|
|
215
|
+
file,
|
|
216
|
+
line: index + 1,
|
|
217
|
+
type: 'missing-semicolon',
|
|
218
|
+
message: 'Add missing semicolon',
|
|
219
|
+
fix: (content) => {
|
|
220
|
+
const lines = content.split('\n')
|
|
221
|
+
lines[index] = lines[index].trimEnd() + ';'
|
|
222
|
+
return lines.join('\n')
|
|
223
|
+
}
|
|
224
|
+
})
|
|
225
|
+
}
|
|
226
|
+
})
|
|
227
|
+
return issues
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* 检查 var 声明(建议使用 let/const)
|
|
232
|
+
*/
|
|
233
|
+
checkVarDeclaration(file, lines) {
|
|
234
|
+
const issues = []
|
|
235
|
+
lines.forEach((line, index) => {
|
|
236
|
+
if (line.trim().startsWith('var ')) {
|
|
237
|
+
issues.push({
|
|
238
|
+
file,
|
|
239
|
+
line: index + 1,
|
|
240
|
+
type: 'var-declaration',
|
|
241
|
+
message: 'Consider using let or const instead of var',
|
|
242
|
+
fix: (content) => {
|
|
243
|
+
const lines = content.split('\n')
|
|
244
|
+
lines[index] = lines[index].replace(/^(\s*)var\s+/, '$1let ')
|
|
245
|
+
return lines.join('\n')
|
|
246
|
+
}
|
|
247
|
+
})
|
|
248
|
+
}
|
|
249
|
+
})
|
|
250
|
+
return issues
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* 检查长行
|
|
255
|
+
*/
|
|
256
|
+
checkLongLine(file, lines) {
|
|
257
|
+
const issues = []
|
|
258
|
+
const maxLength = 120
|
|
259
|
+
|
|
260
|
+
lines.forEach((line, index) => {
|
|
261
|
+
if (line.length > maxLength) {
|
|
262
|
+
issues.push({
|
|
263
|
+
file,
|
|
264
|
+
line: index + 1,
|
|
265
|
+
type: 'long-line',
|
|
266
|
+
message: `Line exceeds ${maxLength} characters`,
|
|
267
|
+
fix: null // 需要手动处理
|
|
268
|
+
})
|
|
269
|
+
}
|
|
270
|
+
})
|
|
271
|
+
return issues
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* 检查 TODO 注释
|
|
276
|
+
*/
|
|
277
|
+
checkTODO(file, lines) {
|
|
278
|
+
const issues = []
|
|
279
|
+
lines.forEach((line, index) => {
|
|
280
|
+
if (line.includes('TODO') || line.includes('FIXME')) {
|
|
281
|
+
issues.push({
|
|
282
|
+
file,
|
|
283
|
+
line: index + 1,
|
|
284
|
+
type: 'todo-comment',
|
|
285
|
+
message: 'TODO/FIXME comment found',
|
|
286
|
+
fix: null // 不自动修复
|
|
287
|
+
})
|
|
288
|
+
}
|
|
289
|
+
})
|
|
290
|
+
return issues
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* 应用修复
|
|
295
|
+
*/
|
|
296
|
+
async applyFixes(issues, files) {
|
|
297
|
+
const appliedFixes = []
|
|
298
|
+
const fileFixes = {}
|
|
299
|
+
|
|
300
|
+
// 按文件分组
|
|
301
|
+
for (const issue of issues) {
|
|
302
|
+
if (!issue.fix) continue // 无法自动修复的问题
|
|
303
|
+
|
|
304
|
+
if (!fileFixes[issue.file]) {
|
|
305
|
+
fileFixes[issue.file] = []
|
|
306
|
+
}
|
|
307
|
+
fileFixes[issue.file].push(issue)
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// 应用修复
|
|
311
|
+
for (const [file, fileIssues] of Object.entries(fileFixes)) {
|
|
312
|
+
try {
|
|
313
|
+
let content = await fs.readFile(file, 'utf8')
|
|
314
|
+
|
|
315
|
+
// 按行号倒序排列,避免行号偏移
|
|
316
|
+
fileIssues.sort((a, b) => b.line - a.line)
|
|
317
|
+
|
|
318
|
+
for (const issue of fileIssues) {
|
|
319
|
+
try {
|
|
320
|
+
const originalContent = content
|
|
321
|
+
content = issue.fix(content)
|
|
322
|
+
|
|
323
|
+
if (content !== originalContent) {
|
|
324
|
+
appliedFixes.push(issue)
|
|
325
|
+
}
|
|
326
|
+
} catch (e) {
|
|
327
|
+
// 修复失败,跳过
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// 写入文件
|
|
332
|
+
await fs.writeFile(file, content, 'utf8')
|
|
333
|
+
} catch (error) {
|
|
334
|
+
// 写入失败,跳过
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return {
|
|
339
|
+
success: true,
|
|
340
|
+
dryRun: false,
|
|
341
|
+
totalIssues: issues.length,
|
|
342
|
+
applied: appliedFixes.length,
|
|
343
|
+
fixes: appliedFixes,
|
|
344
|
+
skipped: issues.length - appliedFixes.length
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* 生成修复报告
|
|
350
|
+
*/
|
|
351
|
+
generateReport(result) {
|
|
352
|
+
const timestamp = new Date().toLocaleString()
|
|
353
|
+
|
|
354
|
+
let report = `# 自动修复报告\n\n`
|
|
355
|
+
report += `**生成时间**: ${timestamp}\n`
|
|
356
|
+
report += `**模式**: ${result.dryRun ? '预览' : '实际修复'}\n\n`
|
|
357
|
+
|
|
358
|
+
report += `## 统计\n\n`
|
|
359
|
+
report += `- 检测到问题: ${result.totalIssues}\n`
|
|
360
|
+
report += `- 已修复: ${result.applied}\n`
|
|
361
|
+
report += `- 跳过: ${result.skipped}\n\n`
|
|
362
|
+
|
|
363
|
+
if (result.fixes.length > 0) {
|
|
364
|
+
report += `## 修复详情\n\n`
|
|
365
|
+
result.fixes.forEach(fix => {
|
|
366
|
+
report += `- **${fix.file}:${fix.line}**\n`
|
|
367
|
+
report += ` - 类型: ${fix.type}\n`
|
|
368
|
+
report += ` - 问题: ${fix.message}\n`
|
|
369
|
+
report += ` - 状态: ✅ 已修复\n\n`
|
|
370
|
+
})
|
|
371
|
+
} else {
|
|
372
|
+
report += `## 修复详情\n\n`
|
|
373
|
+
report += `没有可自动修复的问题。\n\n`
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
report += `---\n*由 Code-Simplifier 自动修复生成*\n`
|
|
377
|
+
|
|
378
|
+
return report
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
module.exports = new AutoFix()
|