code-simplifier 1.0.0 → 1.2.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/ai-auto-fix.js +829 -0
- package/lib/ai-code-review.js +705 -0
- package/lib/ai-knowledge-base.js +757 -0
- package/lib/ai-quality-analyzer.js +1095 -0
- package/lib/ai-refactor-advisor.js +853 -0
- package/lib/ai-trend-analyzer.js +674 -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/lib/ralph-integration.js +541 -0
- package/package.json +87 -61
|
@@ -0,0 +1,397 @@
|
|
|
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
|
+
|
|
11
|
+
class MultiLanguageAnalyzer {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.languageConfigs = {
|
|
14
|
+
javascript: {
|
|
15
|
+
name: 'JavaScript',
|
|
16
|
+
extensions: ['.js', '.jsx'],
|
|
17
|
+
patterns: ['**/*.js', '**/*.jsx'],
|
|
18
|
+
complexityKeywords: ['if', 'for', 'while', 'switch', 'catch', 'try'],
|
|
19
|
+
fileIndicators: ['package.json', '.eslintrc.js']
|
|
20
|
+
},
|
|
21
|
+
typescript: {
|
|
22
|
+
name: 'TypeScript',
|
|
23
|
+
extensions: ['.ts', '.tsx'],
|
|
24
|
+
patterns: ['**/*.ts', '**/*.tsx'],
|
|
25
|
+
complexityKeywords: ['if', 'for', 'while', 'switch', 'catch', 'try'],
|
|
26
|
+
fileIndicators: ['tsconfig.json', '.eslintrc.js']
|
|
27
|
+
},
|
|
28
|
+
python: {
|
|
29
|
+
name: 'Python',
|
|
30
|
+
extensions: ['.py'],
|
|
31
|
+
patterns: ['**/*.py'],
|
|
32
|
+
complexityKeywords: ['if', 'for', 'while', 'try', 'except'],
|
|
33
|
+
fileIndicators: ['requirements.txt', 'setup.py', 'Pipfile']
|
|
34
|
+
},
|
|
35
|
+
java: {
|
|
36
|
+
name: 'Java',
|
|
37
|
+
extensions: ['.java'],
|
|
38
|
+
patterns: ['**/*.java'],
|
|
39
|
+
complexityKeywords: ['if', 'for', 'while', 'switch', 'try', 'catch'],
|
|
40
|
+
fileIndicators: ['pom.xml', 'build.gradle', 'build.xml']
|
|
41
|
+
},
|
|
42
|
+
csharp: {
|
|
43
|
+
name: 'C#',
|
|
44
|
+
extensions: ['.cs'],
|
|
45
|
+
patterns: ['**/*.cs'],
|
|
46
|
+
complexityKeywords: ['if', 'for', 'while', 'switch', 'try', 'catch'],
|
|
47
|
+
fileIndicators: ['*.csproj', '*.sln']
|
|
48
|
+
},
|
|
49
|
+
cpp: {
|
|
50
|
+
name: 'C++',
|
|
51
|
+
extensions: ['.cpp', '.cc', '.cxx', '.h', '.hpp'],
|
|
52
|
+
patterns: ['**/*.cpp', '**/*.cc', '**/*.cxx', '**/*.h', '**/*.hpp'],
|
|
53
|
+
complexityKeywords: ['if', 'for', 'while', 'switch', 'try', 'catch'],
|
|
54
|
+
fileIndicators: ['CMakeLists.txt', 'Makefile']
|
|
55
|
+
},
|
|
56
|
+
php: {
|
|
57
|
+
name: 'PHP',
|
|
58
|
+
extensions: ['.php'],
|
|
59
|
+
patterns: ['**/*.php'],
|
|
60
|
+
complexityKeywords: ['if', 'for', 'while', 'switch', 'try', 'catch'],
|
|
61
|
+
fileIndicators: ['composer.json', '*.phpproj']
|
|
62
|
+
},
|
|
63
|
+
ruby: {
|
|
64
|
+
name: 'Ruby',
|
|
65
|
+
extensions: ['.rb'],
|
|
66
|
+
patterns: ['**/*.rb'],
|
|
67
|
+
complexityKeywords: ['if', 'unless', 'while', 'for', 'case', 'begin'],
|
|
68
|
+
fileIndicators: ['Gemfile', '*.gemspec']
|
|
69
|
+
},
|
|
70
|
+
go: {
|
|
71
|
+
name: 'Go',
|
|
72
|
+
extensions: ['.go'],
|
|
73
|
+
patterns: ['**/*.go'],
|
|
74
|
+
complexityKeywords: ['if', 'for', 'switch', 'select'],
|
|
75
|
+
fileIndicators: ['go.mod', 'go.sum']
|
|
76
|
+
},
|
|
77
|
+
rust: {
|
|
78
|
+
name: 'Rust',
|
|
79
|
+
extensions: ['.rs'],
|
|
80
|
+
patterns: ['**/*.rs'],
|
|
81
|
+
complexityKeywords: ['if', 'for', 'while', 'match', 'try'],
|
|
82
|
+
fileIndicators: ['Cargo.toml', 'Cargo.lock']
|
|
83
|
+
},
|
|
84
|
+
kotlin: {
|
|
85
|
+
name: 'Kotlin',
|
|
86
|
+
extensions: ['.kt', '.kts'],
|
|
87
|
+
patterns: ['**/*.kt', '**/*.kts'],
|
|
88
|
+
complexityKeywords: ['if', 'for', 'while', 'when', 'try', 'catch'],
|
|
89
|
+
fileIndicators: ['build.gradle.kts', 'pom.xml']
|
|
90
|
+
},
|
|
91
|
+
swift: {
|
|
92
|
+
name: 'Swift',
|
|
93
|
+
extensions: ['.swift'],
|
|
94
|
+
patterns: ['**/*.swift'],
|
|
95
|
+
complexityKeywords: ['if', 'for', 'while', 'switch', 'guard'],
|
|
96
|
+
fileIndicators: ['Package.swift', '*.xcodeproj']
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* 检测项目语言
|
|
103
|
+
*/
|
|
104
|
+
async detectLanguage(projectDir) {
|
|
105
|
+
const detectedLanguages = []
|
|
106
|
+
|
|
107
|
+
for (const [langKey, config] of Object.entries(this.languageConfigs)) {
|
|
108
|
+
// 检查文件指示符
|
|
109
|
+
for (const indicator of config.fileIndicators) {
|
|
110
|
+
const matches = await this.findFiles(projectDir, indicator)
|
|
111
|
+
if (matches.length > 0) {
|
|
112
|
+
detectedLanguages.push({
|
|
113
|
+
key: langKey,
|
|
114
|
+
name: config.name,
|
|
115
|
+
confidence: 'high',
|
|
116
|
+
matches: matches.length,
|
|
117
|
+
indicator
|
|
118
|
+
})
|
|
119
|
+
break
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// 检查文件扩展名
|
|
125
|
+
for (const [langKey, config] of Object.entries(this.languageConfigs)) {
|
|
126
|
+
const files = await this.scanFiles(projectDir, config.patterns)
|
|
127
|
+
if (files.length > 0) {
|
|
128
|
+
// 检查是否已经检测到
|
|
129
|
+
const alreadyDetected = detectedLanguages.find(l => l.key === langKey)
|
|
130
|
+
if (!alreadyDetected) {
|
|
131
|
+
detectedLanguages.push({
|
|
132
|
+
key: langKey,
|
|
133
|
+
name: config.name,
|
|
134
|
+
confidence: files.length > 10 ? 'medium' : 'low',
|
|
135
|
+
files: files.length
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// 按置信度排序
|
|
142
|
+
return detectedLanguages.sort((a, b) => {
|
|
143
|
+
const confidenceOrder = { high: 3, medium: 2, low: 1 }
|
|
144
|
+
return confidenceOrder[b.confidence] - confidenceOrder[a.confidence]
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* 扫描文件
|
|
150
|
+
*/
|
|
151
|
+
async scanFiles(projectDir, patterns) {
|
|
152
|
+
const files = []
|
|
153
|
+
|
|
154
|
+
for (const pattern of patterns) {
|
|
155
|
+
const glob = require('glob')
|
|
156
|
+
const matches = await new Promise((resolve, reject) => {
|
|
157
|
+
try {
|
|
158
|
+
glob(pattern, {
|
|
159
|
+
cwd: projectDir,
|
|
160
|
+
ignore: ['node_modules/**', 'dist/**', 'build/**', 'vendor/**', '.git/**'],
|
|
161
|
+
nodir: true
|
|
162
|
+
}, (err, matches) => {
|
|
163
|
+
if (err) resolve([])
|
|
164
|
+
else resolve(matches)
|
|
165
|
+
})
|
|
166
|
+
} catch (e) {
|
|
167
|
+
resolve([])
|
|
168
|
+
}
|
|
169
|
+
})
|
|
170
|
+
files.push(...matches)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return files
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* 查找文件
|
|
178
|
+
*/
|
|
179
|
+
async findFiles(projectDir, pattern) {
|
|
180
|
+
const glob = require('glob')
|
|
181
|
+
return await new Promise((resolve) => {
|
|
182
|
+
try {
|
|
183
|
+
glob(pattern, {
|
|
184
|
+
cwd: projectDir,
|
|
185
|
+
ignore: ['node_modules/**', 'dist/**', 'build/**', 'vendor/**', '.git/**']
|
|
186
|
+
}, (err, matches) => {
|
|
187
|
+
resolve(err ? [] : matches)
|
|
188
|
+
})
|
|
189
|
+
} catch (e) {
|
|
190
|
+
resolve([])
|
|
191
|
+
}
|
|
192
|
+
})
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* 分析代码
|
|
197
|
+
*/
|
|
198
|
+
async analyze(projectDir, options = {}) {
|
|
199
|
+
console.log(chalk.blue.bold('\n🌐 多语言代码分析'))
|
|
200
|
+
console.log(chalk.gray('='.repeat(70)))
|
|
201
|
+
|
|
202
|
+
const detected = await this.detectLanguage(projectDir)
|
|
203
|
+
|
|
204
|
+
if (detected.length === 0) {
|
|
205
|
+
console.log(chalk.yellow('⚠️ 未检测到支持的编程语言'))
|
|
206
|
+
return {
|
|
207
|
+
success: false,
|
|
208
|
+
message: 'No supported languages detected',
|
|
209
|
+
languages: []
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
console.log(chalk.cyan(`\n📋 检测到 ${detected.length} 种语言:`))
|
|
214
|
+
detected.forEach(lang => {
|
|
215
|
+
const confidence = lang.confidence === 'high' ? chalk.green('✓') :
|
|
216
|
+
lang.confidence === 'medium' ? chalk.yellow('⚠') : chalk.gray('?')
|
|
217
|
+
console.log(` ${confidence} ${lang.name} (${lang.confidence})`)
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
// 分析每种语言
|
|
221
|
+
const results = []
|
|
222
|
+
for (const lang of detected) {
|
|
223
|
+
console.log(chalk.cyan(`\n分析 ${lang.name}...`))
|
|
224
|
+
const result = await this.analyzeLanguage(projectDir, lang)
|
|
225
|
+
results.push(result)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return {
|
|
229
|
+
success: true,
|
|
230
|
+
languages: detected,
|
|
231
|
+
results
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* 分析特定语言
|
|
237
|
+
*/
|
|
238
|
+
async analyzeLanguage(projectDir, language) {
|
|
239
|
+
const config = this.languageConfigs[language.key]
|
|
240
|
+
const files = await this.scanFiles(projectDir, config.patterns)
|
|
241
|
+
|
|
242
|
+
if (files.length === 0) {
|
|
243
|
+
return {
|
|
244
|
+
language: language.name,
|
|
245
|
+
files: 0,
|
|
246
|
+
lines: 0,
|
|
247
|
+
complexity: 0,
|
|
248
|
+
issues: []
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
let totalLines = 0
|
|
253
|
+
let totalComplexity = 0
|
|
254
|
+
let issues = []
|
|
255
|
+
|
|
256
|
+
// 分析前 100 个文件(避免性能问题)
|
|
257
|
+
const filesToAnalyze = files.slice(0, 100)
|
|
258
|
+
|
|
259
|
+
for (const file of filesToAnalyze) {
|
|
260
|
+
const filePath = path.join(projectDir, file)
|
|
261
|
+
|
|
262
|
+
try {
|
|
263
|
+
const content = await fs.readFile(filePath, 'utf8')
|
|
264
|
+
const lines = content.split('\n').length
|
|
265
|
+
totalLines += lines
|
|
266
|
+
|
|
267
|
+
// 计算复杂度
|
|
268
|
+
const complexity = this.calculateComplexity(content, config.complexityKeywords)
|
|
269
|
+
totalComplexity += complexity
|
|
270
|
+
|
|
271
|
+
// 检查代码问题
|
|
272
|
+
const fileIssues = this.checkCodeIssues(content, file, config)
|
|
273
|
+
issues.push(...fileIssues)
|
|
274
|
+
} catch (error) {
|
|
275
|
+
// 忽略无法读取的文件
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const avgComplexity = filesToAnalyze.length > 0 ? totalComplexity / filesToAnalyze.length : 0
|
|
280
|
+
|
|
281
|
+
return {
|
|
282
|
+
language: language.name,
|
|
283
|
+
files: filesToAnalyze.length,
|
|
284
|
+
lines: totalLines,
|
|
285
|
+
avgComplexity: Math.round(avgComplexity),
|
|
286
|
+
totalComplexity,
|
|
287
|
+
issues: issues.slice(0, 50) // 最多显示 50 个问题
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* 计算复杂度
|
|
293
|
+
*/
|
|
294
|
+
calculateComplexity(code, keywords) {
|
|
295
|
+
let complexity = 1
|
|
296
|
+
|
|
297
|
+
for (const keyword of keywords) {
|
|
298
|
+
const regex = new RegExp(`\\b${keyword}\\b`, 'g')
|
|
299
|
+
const matches = code.match(regex)
|
|
300
|
+
if (matches) {
|
|
301
|
+
complexity += matches.length
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return complexity
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* 检查代码问题
|
|
310
|
+
*/
|
|
311
|
+
checkCodeIssues(code, filePath, config) {
|
|
312
|
+
const issues = []
|
|
313
|
+
const lines = code.split('\n')
|
|
314
|
+
|
|
315
|
+
lines.forEach((line, index) => {
|
|
316
|
+
const lineNumber = index + 1
|
|
317
|
+
|
|
318
|
+
// 检查长函数(超过 100 行)
|
|
319
|
+
if (lineNumber === 100) {
|
|
320
|
+
issues.push({
|
|
321
|
+
file: filePath,
|
|
322
|
+
line: lineNumber,
|
|
323
|
+
severity: 'warning',
|
|
324
|
+
message: 'Function or file exceeds 100 lines',
|
|
325
|
+
type: 'complexity'
|
|
326
|
+
})
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// 检查 TODO 注释
|
|
330
|
+
if (line.includes('TODO') || line.includes('FIXME')) {
|
|
331
|
+
issues.push({
|
|
332
|
+
file: filePath,
|
|
333
|
+
line: lineNumber,
|
|
334
|
+
severity: 'warning',
|
|
335
|
+
message: 'Contains TODO/FIXME comment',
|
|
336
|
+
type: 'documentation'
|
|
337
|
+
})
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// 检查 console.log(JavaScript/TypeScript)
|
|
341
|
+
if (config.name.includes('JavaScript') || config.name.includes('TypeScript')) {
|
|
342
|
+
if (line.includes('console.log') || line.includes('console.error')) {
|
|
343
|
+
issues.push({
|
|
344
|
+
file: filePath,
|
|
345
|
+
line: lineNumber,
|
|
346
|
+
severity: 'warning',
|
|
347
|
+
message: 'Contains console statement',
|
|
348
|
+
type: 'debug'
|
|
349
|
+
})
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
return issues
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* 生成多语言报告
|
|
359
|
+
*/
|
|
360
|
+
generateReport(analysisResult) {
|
|
361
|
+
const timestamp = new Date().toLocaleString()
|
|
362
|
+
|
|
363
|
+
let report = `# 多语言代码质量分析报告\n\n`
|
|
364
|
+
report += `**生成时间**: ${timestamp}\n\n`
|
|
365
|
+
report += `## 检测到的语言\n\n`
|
|
366
|
+
|
|
367
|
+
analysisResult.results.forEach(result => {
|
|
368
|
+
report += `### ${result.language}\n\n`
|
|
369
|
+
report += `- 文件数: ${result.files}\n`
|
|
370
|
+
report += `- 代码行数: ${result.lines}\n`
|
|
371
|
+
report += `- 平均复杂度: ${result.avgComplexity}\n`
|
|
372
|
+
report += `- 问题数: ${result.issues.length}\n\n`
|
|
373
|
+
})
|
|
374
|
+
|
|
375
|
+
report += `## 问题详情\n\n`
|
|
376
|
+
|
|
377
|
+
let allIssues = []
|
|
378
|
+
analysisResult.results.forEach(result => {
|
|
379
|
+
allIssues = allIssues.concat(result.issues)
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
if (allIssues.length === 0) {
|
|
383
|
+
report += `✅ 没有发现问题\n\n`
|
|
384
|
+
} else {
|
|
385
|
+
allIssues.forEach(issue => {
|
|
386
|
+
report += `- **${issue.severity.toUpperCase()}**: ${issue.message} in ${issue.file}:${issue.line}\n`
|
|
387
|
+
})
|
|
388
|
+
report += `\n`
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
report += `---\n*由 Code-Simplifier 多语言分析器生成*\n`
|
|
392
|
+
|
|
393
|
+
return report
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
module.exports = new MultiLanguageAnalyzer()
|