@sun-asterisk/sunlint 1.3.34 → 1.3.35
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/core/architecture-integration.js +16 -7
- package/core/auto-performance-manager.js +1 -1
- package/core/cli-action-handler.js +92 -2
- package/core/cli-program.js +96 -138
- package/core/file-targeting-service.js +62 -4
- package/core/git-utils.js +19 -12
- package/core/github-annotate-service.js +326 -11
- package/core/html-report-generator.js +326 -731
- package/core/impact-integration.js +433 -0
- package/core/output-service.js +293 -21
- package/core/scoring-service.js +3 -2
- package/engines/arch-detect/core/analyzer.js +413 -0
- package/engines/arch-detect/core/index.js +22 -0
- package/engines/arch-detect/engine/hybrid-detector.js +176 -0
- package/engines/arch-detect/engine/index.js +24 -0
- package/engines/arch-detect/engine/rule-executor.js +228 -0
- package/engines/arch-detect/engine/score-calculator.js +214 -0
- package/engines/arch-detect/engine/violation-detector.js +616 -0
- package/engines/arch-detect/index.js +50 -0
- package/engines/arch-detect/rules/base-rule.js +187 -0
- package/engines/arch-detect/rules/index.js +35 -0
- package/engines/arch-detect/rules/layered/index.js +28 -0
- package/engines/arch-detect/rules/layered/l001-presentation-layer.js +237 -0
- package/engines/arch-detect/rules/layered/l002-business-layer.js +215 -0
- package/engines/arch-detect/rules/layered/l003-data-layer.js +229 -0
- package/engines/arch-detect/rules/layered/l004-model-layer.js +204 -0
- package/engines/arch-detect/rules/layered/l005-layer-separation.js +215 -0
- package/engines/arch-detect/rules/layered/l006-dependency-direction.js +221 -0
- package/engines/arch-detect/rules/layered/layered-rules-collection.js +445 -0
- package/engines/arch-detect/rules/modular/index.js +27 -0
- package/engines/arch-detect/rules/modular/m001-feature-modules.js +238 -0
- package/engines/arch-detect/rules/modular/m002-core-module.js +169 -0
- package/engines/arch-detect/rules/modular/m003-module-declaration.js +186 -0
- package/engines/arch-detect/rules/modular/m004-public-api.js +171 -0
- package/engines/arch-detect/rules/modular/m005-no-deep-imports.js +220 -0
- package/engines/arch-detect/rules/modular/modular-rules-collection.js +357 -0
- package/engines/arch-detect/rules/presentation/index.js +27 -0
- package/engines/arch-detect/rules/presentation/pr001-view-layer.js +221 -0
- package/engines/arch-detect/rules/presentation/pr002-presentation-logic.js +192 -0
- package/engines/arch-detect/rules/presentation/pr004-data-binding.js +187 -0
- package/engines/arch-detect/rules/presentation/pr006-router-layer.js +185 -0
- package/engines/arch-detect/rules/presentation/pr007-interactor-layer.js +181 -0
- package/engines/arch-detect/rules/presentation/presentation-rules-collection.js +507 -0
- package/engines/arch-detect/rules/project-scanner/index.js +31 -0
- package/engines/arch-detect/rules/project-scanner/ps001-project-root.js +213 -0
- package/engines/arch-detect/rules/project-scanner/ps002-language-detection.js +192 -0
- package/engines/arch-detect/rules/project-scanner/ps003-framework-detection.js +339 -0
- package/engines/arch-detect/rules/project-scanner/ps004-build-system.js +171 -0
- package/engines/arch-detect/rules/project-scanner/ps005-source-directory.js +163 -0
- package/engines/arch-detect/rules/project-scanner/ps006-test-directory.js +184 -0
- package/engines/arch-detect/rules/project-scanner/ps007-documentation.js +149 -0
- package/engines/arch-detect/rules/project-scanner/ps008-cicd-detection.js +163 -0
- package/engines/arch-detect/rules/project-scanner/ps009-code-quality.js +152 -0
- package/engines/arch-detect/rules/project-scanner/ps010-statistics.js +180 -0
- package/engines/arch-detect/rules/rule-registry.js +111 -0
- package/engines/arch-detect/types/context.types.js +60 -0
- package/engines/arch-detect/types/enums.js +161 -0
- package/engines/arch-detect/types/index.js +25 -0
- package/engines/arch-detect/types/result.types.js +7 -0
- package/engines/arch-detect/types/rule.types.js +7 -0
- package/engines/arch-detect/utils/file-scanner.js +411 -0
- package/engines/arch-detect/utils/index.js +23 -0
- package/engines/arch-detect/utils/pattern-matcher.js +328 -0
- package/engines/impact/cli.js +106 -0
- package/engines/impact/config/default-config.js +54 -0
- package/engines/impact/core/change-detector.js +258 -0
- package/engines/impact/core/detectors/database-detector.js +1317 -0
- package/engines/impact/core/detectors/endpoint-detector.js +55 -0
- package/engines/impact/core/impact-analyzer.js +124 -0
- package/engines/impact/core/report-generator.js +462 -0
- package/engines/impact/core/utils/ast-parser.js +241 -0
- package/engines/impact/core/utils/dependency-graph.js +159 -0
- package/engines/impact/core/utils/file-utils.js +116 -0
- package/engines/impact/core/utils/git-utils.js +203 -0
- package/engines/impact/core/utils/logger.js +13 -0
- package/engines/impact/core/utils/method-call-graph.js +1192 -0
- package/engines/impact/index.js +135 -0
- package/engines/impact/package.json +29 -0
- package/package.json +18 -43
- package/scripts/build-release.sh +0 -0
- package/scripts/copy-impact-analyzer.js +135 -0
- package/scripts/install.sh +0 -0
- package/scripts/manual-release.sh +0 -0
- package/scripts/pre-release-test.sh +0 -0
- package/scripts/prepare-release.sh +0 -0
- package/scripts/quick-performance-test.js +0 -0
- package/scripts/setup-github-registry.sh +0 -0
- package/scripts/trigger-release.sh +0 -0
- package/scripts/verify-install.sh +0 -0
- package/templates/combined-report.html +1418 -0
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Pattern Matcher Utility
|
|
4
|
+
* Hỗ trợ matching các patterns trong code và config
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.CommonPatterns = exports.PatternMatcher = void 0;
|
|
8
|
+
const enums_1 = require("../types/enums");
|
|
9
|
+
const file_scanner_1 = require("./file-scanner");
|
|
10
|
+
/**
|
|
11
|
+
* Pattern Matcher class
|
|
12
|
+
*/
|
|
13
|
+
class PatternMatcher {
|
|
14
|
+
constructor(_projectRoot) {
|
|
15
|
+
this.fileContentCache = new Map();
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Match folder patterns
|
|
19
|
+
*/
|
|
20
|
+
async matchFolderPatterns(patterns, directoryTree) {
|
|
21
|
+
const matches = [];
|
|
22
|
+
for (const pattern of patterns) {
|
|
23
|
+
const regex = this.globToRegex(pattern);
|
|
24
|
+
const matchedFolders = file_scanner_1.FileScanner.findFoldersByPattern(directoryTree, regex);
|
|
25
|
+
for (const folder of matchedFolders) {
|
|
26
|
+
matches.push({
|
|
27
|
+
type: enums_1.MatchType.FOLDER,
|
|
28
|
+
path: folder.relativePath,
|
|
29
|
+
matchedPattern: pattern,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
matched: matches.length > 0,
|
|
35
|
+
matches,
|
|
36
|
+
score: Math.min(matches.length / patterns.length, 1),
|
|
37
|
+
occurrences: matches.length,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Match file patterns
|
|
42
|
+
*/
|
|
43
|
+
async matchFilePatterns(patterns, files) {
|
|
44
|
+
const matches = [];
|
|
45
|
+
for (const pattern of patterns) {
|
|
46
|
+
const regex = this.globToRegex(pattern);
|
|
47
|
+
const matchedFiles = file_scanner_1.FileScanner.findFilesByPattern(files, regex);
|
|
48
|
+
for (const file of matchedFiles) {
|
|
49
|
+
matches.push({
|
|
50
|
+
type: enums_1.MatchType.FILE,
|
|
51
|
+
path: file.relativePath,
|
|
52
|
+
matchedPattern: pattern,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
matched: matches.length > 0,
|
|
58
|
+
matches,
|
|
59
|
+
score: Math.min(matches.length / patterns.length, 1),
|
|
60
|
+
occurrences: matches.length,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Match code patterns trong files
|
|
65
|
+
*/
|
|
66
|
+
async matchCodePatterns(codePatterns, files) {
|
|
67
|
+
const matches = [];
|
|
68
|
+
for (const codePattern of codePatterns) {
|
|
69
|
+
const filteredFiles = this.filterFilesByLanguage(files, codePattern.language);
|
|
70
|
+
const regex = this.ensureRegex(codePattern.pattern);
|
|
71
|
+
for (const file of filteredFiles) {
|
|
72
|
+
try {
|
|
73
|
+
const content = await this.getFileContent(file.absolutePath);
|
|
74
|
+
const lines = content.split('\n');
|
|
75
|
+
for (let i = 0; i < lines.length; i++) {
|
|
76
|
+
const line = lines[i];
|
|
77
|
+
const match = line.match(regex);
|
|
78
|
+
if (match) {
|
|
79
|
+
matches.push({
|
|
80
|
+
type: enums_1.MatchType.CODE,
|
|
81
|
+
path: file.relativePath,
|
|
82
|
+
line: i + 1,
|
|
83
|
+
column: match.index || 0,
|
|
84
|
+
snippet: line.trim(),
|
|
85
|
+
matchedPattern: codePattern.pattern.toString(),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// Skip files that cannot be read
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Check minimum occurrences
|
|
96
|
+
const minOccurrences = codePatterns[0]?.minOccurrences || 1;
|
|
97
|
+
const passed = matches.length >= minOccurrences;
|
|
98
|
+
return {
|
|
99
|
+
matched: passed,
|
|
100
|
+
matches,
|
|
101
|
+
score: passed ? Math.min(matches.length / (minOccurrences * 2), 1) : 0,
|
|
102
|
+
occurrences: matches.length,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Match config patterns
|
|
107
|
+
*/
|
|
108
|
+
async matchConfigPatterns(configPatterns, files) {
|
|
109
|
+
const matches = [];
|
|
110
|
+
for (const configPattern of configPatterns) {
|
|
111
|
+
const regex = this.globToRegex(configPattern.filePattern);
|
|
112
|
+
const configFiles = files.filter((f) => regex.test(f.relativePath) || regex.test(f.fileName));
|
|
113
|
+
for (const file of configFiles) {
|
|
114
|
+
try {
|
|
115
|
+
const content = await this.getFileContent(file.absolutePath);
|
|
116
|
+
let matched = false;
|
|
117
|
+
// Check contains
|
|
118
|
+
if (configPattern.contains) {
|
|
119
|
+
if (content.includes(configPattern.contains)) {
|
|
120
|
+
matched = true;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Check regex
|
|
124
|
+
if (configPattern.regex) {
|
|
125
|
+
const patternRegex = this.ensureRegex(configPattern.regex);
|
|
126
|
+
if (patternRegex.test(content)) {
|
|
127
|
+
matched = true;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// Check JSON path
|
|
131
|
+
if (configPattern.jsonPath && file.extension === '.json') {
|
|
132
|
+
try {
|
|
133
|
+
const json = JSON.parse(content);
|
|
134
|
+
if (this.checkJsonPath(json, configPattern.jsonPath)) {
|
|
135
|
+
matched = true;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
// Invalid JSON, skip
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// If no specific check, just match file existence
|
|
143
|
+
if (!configPattern.contains && !configPattern.regex && !configPattern.jsonPath) {
|
|
144
|
+
matched = true;
|
|
145
|
+
}
|
|
146
|
+
if (matched) {
|
|
147
|
+
matches.push({
|
|
148
|
+
type: enums_1.MatchType.CONFIG,
|
|
149
|
+
path: file.relativePath,
|
|
150
|
+
matchedPattern: configPattern.filePattern,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
// Skip files that cannot be read
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
matched: matches.length > 0,
|
|
161
|
+
matches,
|
|
162
|
+
score: Math.min(matches.length / configPatterns.length, 1),
|
|
163
|
+
occurrences: matches.length,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Check JSON path exists
|
|
168
|
+
*/
|
|
169
|
+
checkJsonPath(obj, jsonPath) {
|
|
170
|
+
// Simple JSON path like $.dependencies.express
|
|
171
|
+
const parts = jsonPath.replace(/^\$\.?/, '').split('.');
|
|
172
|
+
let current = obj;
|
|
173
|
+
for (const part of parts) {
|
|
174
|
+
if (current === null || current === undefined) {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
if (typeof current === 'object' && part in current) {
|
|
178
|
+
current = current[part];
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return current !== undefined;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Filter files by language
|
|
188
|
+
*/
|
|
189
|
+
filterFilesByLanguage(files, language) {
|
|
190
|
+
if (language === 'all') {
|
|
191
|
+
return files;
|
|
192
|
+
}
|
|
193
|
+
return files.filter((f) => f.language === language);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Get file content with caching
|
|
197
|
+
*/
|
|
198
|
+
async getFileContent(filePath) {
|
|
199
|
+
if (this.fileContentCache.has(filePath)) {
|
|
200
|
+
return this.fileContentCache.get(filePath);
|
|
201
|
+
}
|
|
202
|
+
const content = await file_scanner_1.FileScanner.readFileContent(filePath);
|
|
203
|
+
this.fileContentCache.set(filePath, content);
|
|
204
|
+
return content;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Convert glob to regex
|
|
208
|
+
*/
|
|
209
|
+
globToRegex(pattern) {
|
|
210
|
+
const regexStr = pattern
|
|
211
|
+
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
212
|
+
.replace(/\*\*/g, '{{GLOBSTAR}}')
|
|
213
|
+
.replace(/\*/g, '[^/]*')
|
|
214
|
+
.replace(/\?/g, '.')
|
|
215
|
+
.replace(/\{\{GLOBSTAR\}\}/g, '.*');
|
|
216
|
+
return new RegExp(regexStr, 'i');
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Ensure pattern is a RegExp
|
|
220
|
+
*/
|
|
221
|
+
ensureRegex(pattern) {
|
|
222
|
+
return typeof pattern === 'string' ? new RegExp(pattern) : pattern;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Clear content cache
|
|
226
|
+
*/
|
|
227
|
+
clearCache() {
|
|
228
|
+
this.fileContentCache.clear();
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
exports.PatternMatcher = PatternMatcher;
|
|
232
|
+
/**
|
|
233
|
+
* Common code patterns for various languages
|
|
234
|
+
*/
|
|
235
|
+
exports.CommonPatterns = {
|
|
236
|
+
// Java patterns
|
|
237
|
+
java: {
|
|
238
|
+
controller: [
|
|
239
|
+
/@Controller\b/,
|
|
240
|
+
/@RestController\b/,
|
|
241
|
+
/@RequestMapping\b/,
|
|
242
|
+
/@GetMapping\b/,
|
|
243
|
+
/@PostMapping\b/,
|
|
244
|
+
/@PutMapping\b/,
|
|
245
|
+
/@DeleteMapping\b/,
|
|
246
|
+
],
|
|
247
|
+
service: [/@Service\b/, /@Component\b/, /@Transactional\b/],
|
|
248
|
+
repository: [/@Repository\b/, /extends\s+(Jpa|Crud|Mongo)Repository\b/, /@Query\b/],
|
|
249
|
+
entity: [/@Entity\b/, /@Table\b/, /@Data\b/, /@Getter\b/, /@Setter\b/],
|
|
250
|
+
},
|
|
251
|
+
// TypeScript/JavaScript patterns
|
|
252
|
+
typescript: {
|
|
253
|
+
controller: [
|
|
254
|
+
/@Controller\s*\(/,
|
|
255
|
+
/@Get\s*\(/,
|
|
256
|
+
/@Post\s*\(/,
|
|
257
|
+
/@Put\s*\(/,
|
|
258
|
+
/@Delete\s*\(/,
|
|
259
|
+
/router\.(get|post|put|delete)\s*\(/,
|
|
260
|
+
/app\.(get|post|put|delete)\s*\(/,
|
|
261
|
+
],
|
|
262
|
+
service: [/@Injectable\s*\(/, /\.service\.ts$/, /class\s+\w+Service\b/],
|
|
263
|
+
repository: [/@EntityRepository\s*\(/, /\.repository\.ts$/, /class\s+\w+Repository\b/],
|
|
264
|
+
entity: [/@Entity\s*\(/, /@Column\s*\(/, /interface\s+\w+\s*\{/],
|
|
265
|
+
module: [/@Module\s*\(/, /@NgModule\s*\(/],
|
|
266
|
+
viewModel: [
|
|
267
|
+
/useState\s*[<(]/,
|
|
268
|
+
/useReducer\s*\(/,
|
|
269
|
+
/ref\s*[<(]/,
|
|
270
|
+
/reactive\s*[<(]/,
|
|
271
|
+
/computed\s*\(/,
|
|
272
|
+
],
|
|
273
|
+
},
|
|
274
|
+
// Swift patterns
|
|
275
|
+
swift: {
|
|
276
|
+
viewController: [
|
|
277
|
+
/class\s+\w+ViewController\s*:\s*UIViewController/,
|
|
278
|
+
/struct\s+\w+View\s*:\s*View/,
|
|
279
|
+
],
|
|
280
|
+
viewModel: [
|
|
281
|
+
/class\s+\w+ViewModel\s*:\s*ObservableObject/,
|
|
282
|
+
/@Published\b/,
|
|
283
|
+
/@StateObject\b/,
|
|
284
|
+
/@ObservedObject\b/,
|
|
285
|
+
],
|
|
286
|
+
interactor: [/protocol\s+\w+Interactor/, /class\s+\w+Interactor/],
|
|
287
|
+
router: [/protocol\s+\w+Router/, /class\s+\w+Router/, /class\s+\w+Coordinator/],
|
|
288
|
+
},
|
|
289
|
+
// Dart/Flutter patterns
|
|
290
|
+
dart: {
|
|
291
|
+
widget: [/class\s+\w+\s+extends\s+StatefulWidget/, /class\s+\w+\s+extends\s+StatelessWidget/],
|
|
292
|
+
viewModel: [/class\s+\w+\s+extends\s+ChangeNotifier/, /notifyListeners\s*\(/],
|
|
293
|
+
bloc: [/class\s+\w+Bloc\s+extends\s+Bloc/, /@freezed\b/],
|
|
294
|
+
},
|
|
295
|
+
// C# patterns
|
|
296
|
+
csharp: {
|
|
297
|
+
controller: [
|
|
298
|
+
/\[ApiController\]/,
|
|
299
|
+
/\[HttpGet\]/,
|
|
300
|
+
/\[HttpPost\]/,
|
|
301
|
+
/:\s*ControllerBase\b/,
|
|
302
|
+
/:\s*Controller\b/,
|
|
303
|
+
],
|
|
304
|
+
service: [/class\s+\w+Service\b/, /interface\s+I\w+Service\b/],
|
|
305
|
+
repository: [/DbContext\b/, /DbSet\s*</, /interface\s+I\w+Repository\b/],
|
|
306
|
+
viewModel: [/:\s*INotifyPropertyChanged\b/, /PropertyChanged\?\s*\.Invoke/],
|
|
307
|
+
},
|
|
308
|
+
// PHP patterns
|
|
309
|
+
php: {
|
|
310
|
+
controller: [/extends\s+Controller\b/, /Route::get\s*\(/, /Route::post\s*\(/],
|
|
311
|
+
service: [/class\s+\w+Service\b/],
|
|
312
|
+
repository: [/extends\s+Model\b/, /\$fillable\s*=/, /DB::table\s*\(/],
|
|
313
|
+
},
|
|
314
|
+
// Python patterns
|
|
315
|
+
python: {
|
|
316
|
+
view: [/@app\.route\s*\(/, /@api_view\s*\(/, /class\s+\w+View\s*\(\s*APIView\s*\)/],
|
|
317
|
+
service: [/class\s+\w+Service\b/],
|
|
318
|
+
model: [/@dataclass\b/, /class\s+\w+\s*\(\s*models\.Model\s*\)/],
|
|
319
|
+
},
|
|
320
|
+
// Go patterns
|
|
321
|
+
go: {
|
|
322
|
+
handler: [/func\s+\(\w+\s+\*?\w*Handler\)/, /r\.HandleFunc\s*\(/, /gin\.Context/],
|
|
323
|
+
service: [/type\s+\w+Service\s+struct/],
|
|
324
|
+
repository: [/type\s+\w+Repository\s+struct/],
|
|
325
|
+
},
|
|
326
|
+
};
|
|
327
|
+
exports.default = PatternMatcher;
|
|
328
|
+
//# sourceMappingURL=pattern-matcher.js.map
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI - Command Line Interface Parser
|
|
3
|
+
* Handles argument parsing and validation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export class CLI {
|
|
7
|
+
constructor(argv) {
|
|
8
|
+
this.args = new Map();
|
|
9
|
+
this.parseArgs(argv);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
parseArgs(argv) {
|
|
13
|
+
for (let i = 2; i < argv.length; i++) {
|
|
14
|
+
const arg = argv[i];
|
|
15
|
+
|
|
16
|
+
if (arg.startsWith('--')) {
|
|
17
|
+
const [key, value] = arg.substring(2).split('=');
|
|
18
|
+
this.args.set(key, value || 'true');
|
|
19
|
+
} else if (arg.startsWith('-')) {
|
|
20
|
+
// Short flags
|
|
21
|
+
const key = arg.substring(1);
|
|
22
|
+
this.args.set(key, 'true');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
getArg(key, defaultValue = '') {
|
|
28
|
+
return this.args.get(key) || defaultValue;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
hasArg(key) {
|
|
32
|
+
return this.args.has(key);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
getAllArgs() {
|
|
36
|
+
return Object.fromEntries(this.args);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
showHelp() {
|
|
40
|
+
console.log(`
|
|
41
|
+
╔═══════════════════════════════════════════════════════════════════╗
|
|
42
|
+
║ 🔍 Impact Analyzer CLI - Help Guide ║
|
|
43
|
+
╚═══════════════════════════════════════════════════════════════════╝
|
|
44
|
+
|
|
45
|
+
USAGE:
|
|
46
|
+
impact-analyzer [OPTIONS]
|
|
47
|
+
|
|
48
|
+
DESCRIPTION:
|
|
49
|
+
Analyzes code changes and their impact on endpoints, database, and
|
|
50
|
+
components in TypeScript/JavaScript projects.
|
|
51
|
+
|
|
52
|
+
OPTIONS:
|
|
53
|
+
--input=<path> Source directory to analyze
|
|
54
|
+
Default: src
|
|
55
|
+
|
|
56
|
+
--base=<ref> Base git reference for comparison
|
|
57
|
+
Default: HEAD~1 (previous commit)
|
|
58
|
+
Examples: origin/main, HEAD~5, abc123
|
|
59
|
+
|
|
60
|
+
--head=<ref> Head git reference (optional)
|
|
61
|
+
Default: (working directory)
|
|
62
|
+
|
|
63
|
+
--exclude=<paths> Comma-separated paths to exclude
|
|
64
|
+
Default: node_modules,dist,build,specs,coverage
|
|
65
|
+
|
|
66
|
+
--output=<file> Output markdown report file
|
|
67
|
+
Default: impact-report.md
|
|
68
|
+
|
|
69
|
+
--json=<file> Output JSON report file (optional)
|
|
70
|
+
Example: --json=impact-report.json
|
|
71
|
+
|
|
72
|
+
--max-depth=<n> Maximum call graph depth
|
|
73
|
+
Default: 3
|
|
74
|
+
|
|
75
|
+
--include-tests Include test files in analysis
|
|
76
|
+
|
|
77
|
+
--verbose Show verbose output and stack traces
|
|
78
|
+
|
|
79
|
+
--no-fail Don't exit with error on critical impact
|
|
80
|
+
|
|
81
|
+
--help, -h Show this help message
|
|
82
|
+
|
|
83
|
+
EXAMPLES:
|
|
84
|
+
# Analyze changes in last commit (default)
|
|
85
|
+
impact-analyzer
|
|
86
|
+
|
|
87
|
+
# Analyze changes between main branch and current
|
|
88
|
+
impact-analyzer --base=origin/main
|
|
89
|
+
|
|
90
|
+
# Analyze specific directory with JSON output
|
|
91
|
+
impact-analyzer --input=backend/src --json=report.json
|
|
92
|
+
|
|
93
|
+
# Analyze last 3 commits with verbose output
|
|
94
|
+
impact-analyzer --base=HEAD~3 --verbose
|
|
95
|
+
|
|
96
|
+
# Compare two specific commits
|
|
97
|
+
impact-analyzer --base=abc123 --head=def456
|
|
98
|
+
|
|
99
|
+
REFERENCE:
|
|
100
|
+
Architecture: .specify/plans/architecture.md
|
|
101
|
+
Coding Rules: .github/copilot-instructions.md
|
|
102
|
+
|
|
103
|
+
For more information, visit: https://github.com/sun-asterisk/impact-analyzer
|
|
104
|
+
`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Loader
|
|
3
|
+
* Loads and validates configuration from CLI args and defaults
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export function loadConfig(cli) {
|
|
7
|
+
const config = {
|
|
8
|
+
// Source directory to analyze
|
|
9
|
+
sourceDir: cli.getArg('input', 'src'),
|
|
10
|
+
|
|
11
|
+
// Paths to exclude from analysis
|
|
12
|
+
excludePaths: cli.getArg('exclude', 'node_modules,dist,build,specs,coverage')
|
|
13
|
+
.split(',')
|
|
14
|
+
.map(p => p.trim()),
|
|
15
|
+
|
|
16
|
+
// Git references - defaults to HEAD~1 (previous commit)
|
|
17
|
+
baseRef: cli.getArg('base', 'HEAD~1'),
|
|
18
|
+
headRef: cli.getArg('head', ''), // Empty means current working directory
|
|
19
|
+
|
|
20
|
+
// Analysis options
|
|
21
|
+
maxDepth: parseInt(cli.getArg('max-depth', '3')),
|
|
22
|
+
includeTests: cli.hasArg('include-tests'),
|
|
23
|
+
verbose: cli.hasArg('verbose'),
|
|
24
|
+
|
|
25
|
+
// Report options
|
|
26
|
+
outputFormat: cli.getArg('format', 'markdown'),
|
|
27
|
+
outputFile: cli.getArg('output', 'impact-report.md'),
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Validate configuration
|
|
31
|
+
validateConfig(config);
|
|
32
|
+
|
|
33
|
+
return config;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function validateConfig(config) {
|
|
37
|
+
if (!config.sourceDir) {
|
|
38
|
+
throw new Error('Source directory (--input) is required');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// baseRef and headRef are optional, have sensible defaults
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const DEFAULT_CONFIG = {
|
|
45
|
+
sourceDir: 'src',
|
|
46
|
+
excludePaths: ['node_modules', 'dist', 'build', 'specs', 'coverage'],
|
|
47
|
+
baseRef: 'HEAD~1', // Previous commit
|
|
48
|
+
headRef: 'HEAD', // Current commit
|
|
49
|
+
maxDepth: 3,
|
|
50
|
+
includeTests: false,
|
|
51
|
+
verbose: false,
|
|
52
|
+
outputFormat: 'markdown',
|
|
53
|
+
outputFile: 'impact-report.md',
|
|
54
|
+
};
|