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.
@@ -0,0 +1,328 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Code-Simplifier ESLint 集成
5
+ */
6
+
7
+ const { exec } = require('child_process')
8
+ const util = require('util')
9
+ const execPromise = util.promisify(exec)
10
+ const fs = require('fs-extra')
11
+ const path = require('path')
12
+ const chalk = require('chalk')
13
+ const ora = require('ora')
14
+
15
+ class ESLintIntegration {
16
+ constructor() {
17
+ this.eslintConfig = null
18
+ this.eslintResults = []
19
+ }
20
+
21
+ /**
22
+ * 检查是否支持 ESLint
23
+ */
24
+ async isSupported(projectDir) {
25
+ try {
26
+ // 检查是否有 eslint 配置文件
27
+ const configFiles = [
28
+ '.eslintrc.js',
29
+ '.eslintrc.yaml',
30
+ '.eslintrc.yml',
31
+ '.eslintrc.json',
32
+ '.eslintrc',
33
+ 'package.json'
34
+ ]
35
+
36
+ for (const configFile of configFiles) {
37
+ const configPath = path.join(projectDir, configFile)
38
+ if (await fs.pathExists(configPath)) {
39
+ if (configFile === 'package.json') {
40
+ const pkg = await fs.readJson(configPath)
41
+ if (pkg.eslintConfig) {
42
+ return true
43
+ }
44
+ } else {
45
+ return true
46
+ }
47
+ }
48
+ }
49
+
50
+ // 检查是否安装了 eslint
51
+ try {
52
+ await execPromise('npx eslint --version', { cwd: projectDir })
53
+ return true
54
+ } catch (e) {
55
+ return false
56
+ }
57
+ } catch (error) {
58
+ return false
59
+ }
60
+ }
61
+
62
+ /**
63
+ * 运行 ESLint 分析
64
+ */
65
+ async runAnalysis(projectDir, options = {}) {
66
+ console.log(chalk.blue.bold('\n🔍 ESLint 代码分析'))
67
+ console.log(chalk.gray('='.repeat(70)))
68
+
69
+ const spinner = ora(chalk.blue('正在运行 ESLint...')).start()
70
+
71
+ try {
72
+ const {
73
+ format = 'json',
74
+ fix = false,
75
+ files = '**/*.{js,jsx,ts,tsx,vue}',
76
+ ignorePath = '.gitignore'
77
+ } = options
78
+
79
+ // 构建 eslint 命令
80
+ let cmd = `npx eslint --format ${format} `
81
+ if (fix) cmd += '--fix '
82
+ cmd += `--ignore-pattern "${ignorePath}" `
83
+ cmd += `"${files}"`
84
+
85
+ const { stdout } = await execPromise(cmd, { cwd: projectDir })
86
+
87
+ spinner.succeed(chalk.green('ESLint 分析完成'))
88
+
89
+ // 解析结果
90
+ let results = []
91
+ if (format === 'json') {
92
+ results = JSON.parse(stdout)
93
+ } else {
94
+ results = this.parseTextOutput(stdout)
95
+ }
96
+
97
+ this.eslintResults = results
98
+
99
+ // 生成分析报告
100
+ const report = this.generateReport(results)
101
+ this.printSummary(results)
102
+
103
+ return {
104
+ success: true,
105
+ results,
106
+ report,
107
+ summary: this.getSummary(results)
108
+ }
109
+ } catch (error) {
110
+ if (error.stdout) {
111
+ // ESLint 返回非零退出码但有输出
112
+ try {
113
+ const results = JSON.parse(error.stdout)
114
+ spinner.succeed(chalk.green('ESLint 分析完成'))
115
+ this.eslintResults = results
116
+ this.printSummary(results)
117
+ return {
118
+ success: true,
119
+ results,
120
+ report: this.generateReport(results),
121
+ summary: this.getSummary(results)
122
+ }
123
+ } catch (e) {
124
+ // 解析失败
125
+ }
126
+ }
127
+
128
+ spinner.fail(chalk.red('ESLint 分析失败'))
129
+ console.error(chalk.gray(error.message))
130
+ return {
131
+ success: false,
132
+ error: error.message,
133
+ results: []
134
+ }
135
+ }
136
+ }
137
+
138
+ /**
139
+ * 自动修复问题
140
+ */
141
+ async autoFix(projectDir, options = {}) {
142
+ console.log(chalk.blue.bold('\n🔧 自动修复 ESLint 问题'))
143
+ console.log(chalk.gray('='.repeat(70)))
144
+
145
+ const spinner = ora(chalk.blue('正在自动修复...')).start()
146
+
147
+ try {
148
+ const { files = '**/*.{js,jsx,ts,tsx,vue}' } = options
149
+
150
+ const cmd = `npx eslint --fix "${files}"`
151
+ const { stdout } = await execPromise(cmd, { cwd: projectDir })
152
+
153
+ spinner.succeed(chalk.green('自动修复完成'))
154
+
155
+ console.log(chalk.gray(stdout))
156
+ console.log(chalk.green('\n✅ 修复完成,请检查修改'))
157
+
158
+ return { success: true, output: stdout }
159
+ } catch (error) {
160
+ spinner.fail(chalk.red('自动修复失败'))
161
+ console.error(chalk.red(error.message))
162
+ return { success: false, error: error.message }
163
+ }
164
+ }
165
+
166
+ /**
167
+ * 生成报告
168
+ */
169
+ generateReport(results) {
170
+ const timestamp = new Date().toLocaleString()
171
+
172
+ let totalErrors = 0
173
+ let totalWarnings = 0
174
+ let fixedCount = 0
175
+
176
+ results.forEach(file => {
177
+ totalErrors += file.errorCount || 0
178
+ totalWarnings += file.warningCount || 0
179
+ fixedCount += file.fixedCount || 0
180
+ })
181
+
182
+ return `# ESLint 分析报告
183
+
184
+ **生成时间**: ${timestamp}
185
+
186
+ ## 概览
187
+
188
+ - **文件总数**: ${results.length}
189
+ - **错误数**: ${totalErrors}
190
+ - **警告数**: ${totalWarnings}
191
+ - **已修复**: ${fixedCount}
192
+
193
+ ## 文件详情
194
+
195
+ ${results.map(file => {
196
+ return `### ${file.filePath}
197
+
198
+ - 错误: ${file.errorCount}
199
+ - 警告: ${file.warningCount}
200
+ - 修复: ${file.fixedCount}
201
+
202
+ ${file.messages.map(msg => {
203
+ const severity = msg.severity === 2 ? '❌ 错误' : '⚠️ 警告'
204
+ return `- ${severity}: ${msg.message} (${msg.ruleId}) at line ${msg.line}`
205
+ }).join('\n')}
206
+ `
207
+ }).join('\n')}
208
+
209
+ ---
210
+ *由 Code-Simplifier ESLint 集成生成*
211
+ `
212
+ }
213
+
214
+ /**
215
+ * 打印摘要
216
+ */
217
+ printSummary(results) {
218
+ const summary = this.getSummary(results)
219
+
220
+ console.log(chalk.cyan('\n📊 ESLint 分析结果:'))
221
+ console.log(chalk.cyan(` 文件总数: ${summary.totalFiles}`))
222
+ console.log(chalk.red(` 错误: ${summary.totalErrors}`))
223
+ console.log(chalk.yellow(` 警告: ${summary.totalWarnings}`))
224
+ console.log(chalk.green(` 已修复: ${summary.fixedCount}`))
225
+ }
226
+
227
+ /**
228
+ * 获取摘要
229
+ */
230
+ getSummary(results) {
231
+ return {
232
+ totalFiles: results.length,
233
+ totalErrors: results.reduce((sum, file) => sum + (file.errorCount || 0), 0),
234
+ totalWarnings: results.reduce((sum, file) => sum + (file.warningCount || 0), 0),
235
+ fixedCount: results.reduce((sum, file) => sum + (file.fixedCount || 0), 0)
236
+ }
237
+ }
238
+
239
+ /**
240
+ * 解析文本输出
241
+ */
242
+ parseTextOutput(stdout) {
243
+ // 简单的文本解析逻辑
244
+ const lines = stdout.split('\n')
245
+ const results = []
246
+
247
+ let currentFile = null
248
+ let currentMessages = []
249
+
250
+ for (const line of lines) {
251
+ const match = line.match(/^(.+?):\s+(\d+):(\d+)\s+(error|warning)\s+(.+?)(?:\s+\((.+?)\))?$/)
252
+ if (match) {
253
+ const [_, filePath, line, col, severity, message, ruleId] = match
254
+
255
+ if (currentFile !== filePath) {
256
+ if (currentFile) {
257
+ results.push({
258
+ filePath: currentFile,
259
+ messages: currentMessages,
260
+ errorCount: currentMessages.filter(m => m.severity === 2).length,
261
+ warningCount: currentMessages.filter(m => m.severity === 1).length
262
+ })
263
+ }
264
+ currentFile = filePath
265
+ currentMessages = []
266
+ }
267
+
268
+ currentMessages.push({
269
+ line: parseInt(line),
270
+ column: parseInt(col),
271
+ severity: severity === 'error' ? 2 : 1,
272
+ message,
273
+ ruleId
274
+ })
275
+ }
276
+ }
277
+
278
+ if (currentFile) {
279
+ results.push({
280
+ filePath: currentFile,
281
+ messages: currentMessages,
282
+ errorCount: currentMessages.filter(m => m.severity === 2).length,
283
+ warningCount: currentMessages.filter(m => m.severity === 1).length
284
+ })
285
+ }
286
+
287
+ return results
288
+ }
289
+
290
+ /**
291
+ * 集成到质量分析
292
+ */
293
+ integrateWithQualityAnalysis(eslintResult, qualityResult) {
294
+ if (!eslintResult || !eslintResult.success) {
295
+ return qualityResult
296
+ }
297
+
298
+ const eslintSummary = eslintResult.summary
299
+
300
+ // 根据 ESLint 结果调整质量评分
301
+ let adjustedScore = qualityResult.score
302
+
303
+ // 错误扣分(每个错误扣 2 分)
304
+ adjustedScore -= eslintSummary.totalErrors * 2
305
+
306
+ // 警告扣分(每个警告扣 0.5 分)
307
+ adjustedScore -= eslintSummary.totalWarnings * 0.5
308
+
309
+ // 限制分数范围
310
+ adjustedScore = Math.max(0, Math.min(100, adjustedScore))
311
+
312
+ // 更新结果
313
+ return {
314
+ ...qualityResult,
315
+ score: Math.round(adjustedScore),
316
+ eslint: {
317
+ totalFiles: eslintSummary.totalFiles,
318
+ totalErrors: eslintSummary.totalErrors,
319
+ totalWarnings: eslintSummary.totalWarnings,
320
+ fixedCount: eslintSummary.fixedCount,
321
+ results: eslintResult.results
322
+ },
323
+ totalIssues: qualityResult.totalIssues + eslintSummary.totalErrors + eslintSummary.totalWarnings
324
+ }
325
+ }
326
+ }
327
+
328
+ module.exports = new ESLintIntegration()
@@ -0,0 +1,353 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Code-Simplifier Git 钩子集成
5
+ */
6
+
7
+ const fs = require('fs-extra')
8
+ const path = require('path')
9
+ const chalk = require('chalk')
10
+ const inquirer = require('inquirer')
11
+ const { exec } = require('child_process')
12
+ const util = require('util')
13
+ const execPromise = util.promisify(exec)
14
+
15
+ class GitHooks {
16
+ constructor() {
17
+ this.gitDir = '.git'
18
+ this.hooksDir = '.git/hooks'
19
+ }
20
+
21
+ /**
22
+ * 运行 Git 钩子管理
23
+ */
24
+ async run(options = {}) {
25
+ if (options.install) {
26
+ await this.install(options)
27
+ } else if (options.uninstall) {
28
+ await this.uninstall(options)
29
+ } else if (options.list) {
30
+ await this.list()
31
+ } else {
32
+ await this.interactive()
33
+ }
34
+ }
35
+
36
+ /**
37
+ * 交互式安装
38
+ */
39
+ async interactive() {
40
+ console.log(chalk.cyan.bold('\n🔧 Git 钩子管理'))
41
+ console.log(chalk.gray('='.repeat(50)))
42
+
43
+ const answers = await inquirer.prompt([
44
+ {
45
+ type: 'confirm',
46
+ name: 'preCommit',
47
+ message: '安装 pre-commit 钩子 (提交前检查代码质量)?',
48
+ default: true
49
+ },
50
+ {
51
+ type: 'confirm',
52
+ name: 'prePush',
53
+ message: '安装 pre-push 钩子 (推送前检查)?',
54
+ default: true
55
+ },
56
+ {
57
+ type: 'number',
58
+ name: 'threshold',
59
+ message: '质量评分阈值 (0-100):',
60
+ default: 70,
61
+ validate: val => val >= 0 && val <= 100
62
+ }
63
+ ])
64
+
65
+ await this.install({
66
+ preCommit: answers.preCommit,
67
+ prePush: answers.prePush,
68
+ threshold: answers.threshold
69
+ })
70
+ }
71
+
72
+ /**
73
+ * 安装钩子
74
+ */
75
+ async install(options = {}) {
76
+ console.log(chalk.blue.bold('\n📦 安装 Git 钩子'))
77
+ console.log(chalk.gray('='.repeat(50)))
78
+
79
+ if (!(await fs.pathExists(this.gitDir))) {
80
+ console.log(chalk.red('❌ 当前目录不是 Git 仓库'))
81
+ return { success: false, error: 'Not a git repository' }
82
+ }
83
+
84
+ const config = {
85
+ threshold: options.threshold || 70,
86
+ eslint: options.eslint !== false,
87
+ autoFix: options.autoFix || false
88
+ }
89
+
90
+ // 写入配置文件
91
+ await fs.writeJson('.code-simplifier/hooks-config.json', config, { spaces: 2 })
92
+
93
+ // 安装 pre-commit 钩子
94
+ if (options.preCommit !== false) {
95
+ await this.installPreCommit(config)
96
+ }
97
+
98
+ // 安装 pre-push 钩子
99
+ if (options.prePush !== false) {
100
+ await this.installPrePush(config)
101
+ }
102
+
103
+ console.log(chalk.green('\n✅ Git 钩子安装完成'))
104
+ return { success: true }
105
+ }
106
+
107
+ /**
108
+ * 安装 pre-commit 钩子
109
+ */
110
+ async installPreCommit(config) {
111
+ console.log(chalk.gray('正在安装 pre-commit 钩子...'))
112
+
113
+ const hookContent = this.generatePreCommitHook(config)
114
+ const hookPath = path.join(this.hooksDir, 'pre-commit')
115
+
116
+ await fs.writeFile(hookPath, hookContent)
117
+ await fs.chmod(hookPath, '755')
118
+
119
+ console.log(chalk.green('✓ pre-commit 钩子已安装'))
120
+ }
121
+
122
+ /**
123
+ * 安装 pre-push 钩子
124
+ */
125
+ async installPrePush(config) {
126
+ console.log(chalk.gray('正在安装 pre-push 钩子...'))
127
+
128
+ const hookContent = this.generatePrePushHook(config)
129
+ const hookPath = path.join(this.hooksDir, 'pre-push')
130
+
131
+ await fs.writeFile(hookPath, hookContent)
132
+ await fs.chmod(hookPath, '755')
133
+
134
+ console.log(chalk.green('✓ pre-push 钩子已安装'))
135
+ }
136
+
137
+ /**
138
+ * 生成 pre-commit 钩子内容
139
+ */
140
+ generatePreCommitHook(config) {
141
+ return `#!/bin/bash
142
+
143
+ # Code-Simplifier pre-commit hook
144
+ # 由 Code-Simplifier 自动生成
145
+
146
+ echo "🔍 正在运行代码质量检查..."
147
+
148
+ # 检查是否安装了 code-simplifier
149
+ if ! command -v code-simplifier &> /dev/null && ! command -v npx &> /dev/null; then
150
+ echo "❌ 未找到 code-simplifier,请先安装:"
151
+ echo " npm install -g code-simplifier"
152
+ exit 1
153
+ fi
154
+
155
+ # 获取待提交的文件
156
+ STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\\.(js|jsx|ts|tsx|vue)$' || true)
157
+
158
+ if [ -z "$STAGED_FILES" ]; then
159
+ echo "✓ 没有需要检查的代码文件"
160
+ exit 0
161
+ fi
162
+
163
+ echo "📝 检查文件: $STAGED_FILES"
164
+
165
+ # 运行质量检查
166
+ if command -v code-simplifier &> /dev/null; then
167
+ npx code-simplifier quality --threshold ${config.threshold} --dir .
168
+ else
169
+ npx code-simplifier quality --threshold ${config.threshold} --dir .
170
+ fi
171
+
172
+ QUALITY_EXIT_CODE=$?
173
+
174
+ if [ $QUALITY_EXIT_CODE -ne 0 ]; then
175
+ echo ""
176
+ echo "❌ 代码质量检查失败!"
177
+ echo "请修复问题后再提交,或使用 --no-verify 跳过检查"
178
+ exit 1
179
+ fi
180
+
181
+ # 运行 ESLint(如果配置了)
182
+ ${config.eslint ? `
183
+ if [ -f .eslintrc.js ] || [ -f .eslintrc.json ] || [ -f package.json ]; then
184
+ echo "🔍 运行 ESLint 检查..."
185
+ npx eslint --ext .js,.jsx,.ts,.tsx,.vue $STAGED_FILES
186
+ ESLINT_EXIT_CODE=$?
187
+
188
+ if [ $ESLINT_EXIT_CODE -ne 0 ]; then
189
+ echo ""
190
+ echo "❌ ESLint 检查失败!"
191
+ echo "请修复问题后再提交,或使用 --no-verify 跳过检查"
192
+ exit 1
193
+ fi
194
+ fi
195
+ ` : ''}
196
+
197
+ # 自动修复(如果配置了)
198
+ ${config.autoFix ? `
199
+ if [ -f .eslintrc.js ] || [ -f .eslintrc.json ] || [ -f package.json ]; then
200
+ echo "🔧 尝试自动修复..."
201
+ npx eslint --fix $STAGED_FILES 2>/dev/null || true
202
+
203
+ # 如果有修改,重新添加到暂存区
204
+ MODIFIED_FILES=$(git diff --name-only | grep -E '\\.(js|jsx|ts|tsx|vue)$' || true)
205
+
206
+ if [ -n "$MODIFIED_FILES" ]; then
207
+ echo "📝 添加自动修复的文件..."
208
+ git add $MODIFIED_FILES
209
+ fi
210
+ fi
211
+ ` : ''}
212
+
213
+ echo "✅ 代码质量检查通过"
214
+ exit 0
215
+ `
216
+ }
217
+
218
+ /**
219
+ * 生成 pre-push 钩子内容
220
+ */
221
+ generatePrePushHook(config) {
222
+ return `#!/bin/bash
223
+
224
+ # Code-Simplifier pre-push hook
225
+ # 由 Code-Simplifier 自动生成
226
+
227
+ echo "🔍 正在运行推送前检查..."
228
+
229
+ # 获取要推送的分支和远程
230
+ read -r LOCAL_REF LOCAL_SHA REMOTE_REF REMOTE_SHA
231
+
232
+ if [ "$REMOTE_SHA" = "0000000000000000000000000000000000000000" ]; then
233
+ # 新分支,检查所有文件
234
+ BRANCH_FILES=$(git diff $LOCAL_SHA --name-only | grep -E '\\.(js|jsx|ts|tsx|vue)$' || true)
235
+ else
236
+ # 更新分支
237
+ BRANCH_FILES=$(git diff $REMOTE_SHA..$LOCAL_SHA --name-only | grep -E '\\.(js|jsx|ts|tsx|vue)$' || true)
238
+ fi
239
+
240
+ if [ -z "$BRANCH_FILES" ]; then
241
+ echo "✓ 没有需要检查的代码文件"
242
+ exit 0
243
+ fi
244
+
245
+ echo "📝 检查分支: $(git rev-parse --abbrev-ref HEAD)"
246
+
247
+ # 运行全面质量检查
248
+ if command -v code-simplifier &> /dev/null; then
249
+ npx code-simplifier quality --threshold ${config.threshold}
250
+ else
251
+ npx code-simplifier quality --threshold ${config.threshold}
252
+ fi
253
+
254
+ QUALITY_EXIT_CODE=$?
255
+
256
+ if [ $QUALITY_EXIT_CODE -ne 0 ]; then
257
+ echo ""
258
+ echo "❌ 代码质量检查失败!"
259
+ echo "请修复问题后再推送,或使用 --no-verify 跳过检查"
260
+ exit 1
261
+ fi
262
+
263
+ # 运行完整工作流(可选)
264
+ if [ "$1" = "--full-check" ]; then
265
+ echo "🔄 运行完整质量检查工作流..."
266
+ npx code-simplifier workflow --all
267
+ WORKFLOW_EXIT_CODE=$?
268
+
269
+ if [ $WORKFLOW_EXIT_CODE -ne 0 ]; then
270
+ echo ""
271
+ echo "❌ 完整工作流检查失败!"
272
+ exit 1
273
+ fi
274
+ fi
275
+
276
+ echo "✅ 推送前检查通过"
277
+ exit 0
278
+ `
279
+ }
280
+
281
+ /**
282
+ * 卸载钩子
283
+ */
284
+ async uninstall(options = {}) {
285
+ console.log(chalk.blue.bold('\n🗑️ 卸载 Git 钩子'))
286
+
287
+ const hooks = [
288
+ { name: 'pre-commit', path: path.join(this.hooksDir, 'pre-commit') },
289
+ { name: 'pre-push', path: path.join(this.hooksDir, 'pre-push') }
290
+ ]
291
+
292
+ for (const hook of hooks) {
293
+ if (await fs.pathExists(hook.path)) {
294
+ await fs.remove(hook.path)
295
+ console.log(chalk.green(`✓ ${hook.name} 钩子已卸载`))
296
+ }
297
+ }
298
+
299
+ // 删除配置文件
300
+ if (await fs.pathExists('.code-simplifier/hooks-config.json')) {
301
+ await fs.remove('.code-simplifier/hooks-config.json')
302
+ }
303
+
304
+ console.log(chalk.green('\n✅ Git 钩子卸载完成'))
305
+ }
306
+
307
+ /**
308
+ * 列出钩子
309
+ */
310
+ async list() {
311
+ console.log(chalk.cyan.bold('\n📋 已安装的 Git 钩子'))
312
+ console.log(chalk.gray('='.repeat(50)))
313
+
314
+ const hooks = [
315
+ { name: 'pre-commit', path: path.join(this.hooksDir, 'pre-commit') },
316
+ { name: 'pre-push', path: path.join(this.hooksDir, 'pre-push') }
317
+ ]
318
+
319
+ let installedCount = 0
320
+ for (const hook of hooks) {
321
+ if (await fs.pathExists(hook.path)) {
322
+ console.log(chalk.green(`✓ ${hook.name} - 已安装`))
323
+ installedCount++
324
+ } else {
325
+ console.log(chalk.gray(`- ${hook.name} - 未安装`))
326
+ }
327
+ }
328
+
329
+ if (installedCount > 0) {
330
+ console.log(chalk.cyan(`\n总计: ${installedCount}/${hooks.length} 个钩子已安装`))
331
+ } else {
332
+ console.log(chalk.gray('\n没有安装任何钩子'))
333
+ }
334
+
335
+ // 显示配置
336
+ if (await fs.pathExists('.code-simplifier/hooks-config.json')) {
337
+ const config = await fs.readJson('.code-simplifier/hooks-config.json')
338
+ console.log(chalk.cyan('\n配置:'))
339
+ console.log(chalk.gray(` 质量阈值: ${config.threshold}`))
340
+ console.log(chalk.gray(` ESLint: ${config.eslint ? '启用' : '禁用'}`))
341
+ console.log(chalk.gray(` 自动修复: ${config.autoFix ? '启用' : '禁用'}`))
342
+ }
343
+ }
344
+
345
+ /**
346
+ * 检查是否为 Git 仓库
347
+ */
348
+ async isGitRepository() {
349
+ return await fs.pathExists(this.gitDir)
350
+ }
351
+ }
352
+
353
+ module.exports = new GitHooks()