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 CHANGED
@@ -11,7 +11,10 @@
11
11
  - **质量监控** - 实时监控代码质量指标
12
12
  - **知识库管理** - 积累最佳实践和经验教训
13
13
  - **自动更新** - 保持工具始终最新
14
- - **多语言支持** - Node.jsJava、Python、Rust、Go
14
+ - **多语言支持** - JavaScriptTypeScript、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`:
@@ -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
+ }
@@ -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()