@sun-asterisk/impact-analyzer 1.0.1 → 1.0.3
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 +4 -4
- package/config/default-config.js +5 -3
- package/modules/utils/git-utils.js +14 -9
- package/modules/utils/method-call-graph.js +38 -63
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -163,9 +163,9 @@ jobs:
|
|
|
163
163
|
analyze:
|
|
164
164
|
runs-on: ubuntu-latest
|
|
165
165
|
steps:
|
|
166
|
-
- uses: actions/checkout@
|
|
166
|
+
- uses: actions/checkout@v4
|
|
167
167
|
with:
|
|
168
|
-
fetch-depth: 0
|
|
168
|
+
fetch-depth: 0 # Fetch full history
|
|
169
169
|
|
|
170
170
|
- uses: actions/setup-node@v3
|
|
171
171
|
with:
|
|
@@ -176,7 +176,6 @@ jobs:
|
|
|
176
176
|
npx @sun-asterisk/impact-analyzer \
|
|
177
177
|
--input=src \
|
|
178
178
|
--base=origin/${{ github.base_ref }} \
|
|
179
|
-
--head=${{ github.sha }} \
|
|
180
179
|
--output=impact-report.md \
|
|
181
180
|
--json=impact-report.json
|
|
182
181
|
|
|
@@ -200,11 +199,12 @@ jobs:
|
|
|
200
199
|
impact-analysis:
|
|
201
200
|
stage: test
|
|
202
201
|
image: node:18
|
|
202
|
+
variables:
|
|
203
|
+
GIT_DEPTH: 0 # Fetch full history
|
|
203
204
|
script:
|
|
204
205
|
- npx @sun-asterisk/impact-analyzer
|
|
205
206
|
--input=src
|
|
206
207
|
--base=origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME
|
|
207
|
-
--head=$CI_COMMIT_SHA
|
|
208
208
|
--output=impact-report.md
|
|
209
209
|
--json=impact-report.json
|
|
210
210
|
artifacts:
|
package/config/default-config.js
CHANGED
|
@@ -15,7 +15,7 @@ export function loadConfig(cli) {
|
|
|
15
15
|
|
|
16
16
|
// Git references
|
|
17
17
|
baseRef: cli.getArg('base', 'origin/main'),
|
|
18
|
-
headRef: cli.getArg('head', '
|
|
18
|
+
headRef: cli.getArg('head', ''), // Empty means current working directory
|
|
19
19
|
|
|
20
20
|
// Analysis options
|
|
21
21
|
maxDepth: parseInt(cli.getArg('max-depth', '3')),
|
|
@@ -38,9 +38,11 @@ function validateConfig(config) {
|
|
|
38
38
|
throw new Error('Source directory (--input) is required');
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
if (!config.baseRef
|
|
42
|
-
throw new Error('
|
|
41
|
+
if (!config.baseRef) {
|
|
42
|
+
throw new Error('Base git reference (--base) is required');
|
|
43
43
|
}
|
|
44
|
+
|
|
45
|
+
// headRef is optional, defaults to working directory comparison
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
export const DEFAULT_CONFIG = {
|
|
@@ -11,7 +11,7 @@ export class GitUtils {
|
|
|
11
11
|
/**
|
|
12
12
|
* Get list of changed files between two refs
|
|
13
13
|
* @param {string} baseRef - Base git reference
|
|
14
|
-
* @param {string} headRef - Head git reference
|
|
14
|
+
* @param {string} headRef - Head git reference (empty = working directory)
|
|
15
15
|
* @param {string} workDir - Working directory (optional, defaults to cwd)
|
|
16
16
|
*/
|
|
17
17
|
static getChangedFiles(baseRef, headRef, workDir = null) {
|
|
@@ -23,10 +23,14 @@ export class GitUtils {
|
|
|
23
23
|
throw new Error(`Directory does not exist: ${cwd}`);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
// If headRef is empty, compare baseRef with working directory
|
|
27
|
+
const diffCommand = headRef
|
|
28
|
+
? `git diff --name-status ${baseRef}...${headRef}`
|
|
29
|
+
: `git diff --name-status ${baseRef}`;
|
|
30
|
+
|
|
27
31
|
const diffOutput = execSync(diffCommand, {
|
|
28
32
|
encoding: 'utf-8',
|
|
29
|
-
cwd: cwd
|
|
33
|
+
cwd: cwd
|
|
30
34
|
});
|
|
31
35
|
|
|
32
36
|
const changedFiles = [];
|
|
@@ -48,7 +52,7 @@ export class GitUtils {
|
|
|
48
52
|
console.error('Error getting changed files:', error.message);
|
|
49
53
|
console.error(' Working directory:', workDir || process.cwd());
|
|
50
54
|
console.error(' Base ref:', baseRef);
|
|
51
|
-
console.error(' Head ref:', headRef);
|
|
55
|
+
console.error(' Head ref:', headRef || '(working directory)');
|
|
52
56
|
return [];
|
|
53
57
|
}
|
|
54
58
|
}
|
|
@@ -77,7 +81,7 @@ export class GitUtils {
|
|
|
77
81
|
* Get diff between two versions of a file
|
|
78
82
|
* @param {string} filePath - File path relative to git root (e.g., 'src/modules/file.ts')
|
|
79
83
|
* @param {string} baseRef - Base git reference
|
|
80
|
-
* @param {string} headRef - Head git reference
|
|
84
|
+
* @param {string} headRef - Head git reference (empty = working directory)
|
|
81
85
|
* @param {string} workDir - Working directory to run git from (optional)
|
|
82
86
|
*/
|
|
83
87
|
static getFileDiff(filePath, baseRef, headRef, workDir = null) {
|
|
@@ -93,9 +97,10 @@ export class GitUtils {
|
|
|
93
97
|
return '';
|
|
94
98
|
}
|
|
95
99
|
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
|
|
100
|
+
// If headRef is empty, compare with working directory
|
|
101
|
+
const diffCommand = headRef
|
|
102
|
+
? `git diff ${baseRef}...${headRef} -- ${filePath}`
|
|
103
|
+
: `git diff ${baseRef} -- ${filePath}`;
|
|
99
104
|
|
|
100
105
|
const result = execSync(diffCommand, {
|
|
101
106
|
encoding: 'utf-8',
|
|
@@ -108,7 +113,7 @@ export class GitUtils {
|
|
|
108
113
|
console.error('Error getting file diff:', error.message);
|
|
109
114
|
console.error(' File:', filePath);
|
|
110
115
|
console.error(' Base:', baseRef);
|
|
111
|
-
console.error(' Head:', headRef);
|
|
116
|
+
console.error(' Head:', headRef || '(working directory)');
|
|
112
117
|
console.error(' Working dir:', workDir);
|
|
113
118
|
return '';
|
|
114
119
|
}
|
|
@@ -448,89 +448,64 @@ export class MethodCallGraph {
|
|
|
448
448
|
|
|
449
449
|
/**
|
|
450
450
|
* Get changed methods from git diff
|
|
451
|
-
*
|
|
452
|
-
* 1. Methods that contain changed lines
|
|
453
|
-
* 2. Method calls that were added/removed
|
|
451
|
+
* Uses ts-morph AST to detect which specific methods contain changes
|
|
454
452
|
*/
|
|
455
453
|
getChangedMethods(diff, filePath) {
|
|
456
|
-
const changedMethods = new Set();
|
|
457
|
-
const changedMethodCalls = new Set();
|
|
458
|
-
|
|
459
454
|
if (!diff || !filePath) return [];
|
|
460
455
|
|
|
461
456
|
const sourceFile = this.project.getSourceFile(filePath);
|
|
462
457
|
if (!sourceFile) return [];
|
|
463
458
|
|
|
464
|
-
|
|
465
|
-
if (classes.length === 0) return [];
|
|
466
|
-
|
|
467
|
-
// Parse diff to extract changed line numbers
|
|
459
|
+
// Extract changed line numbers from diff
|
|
468
460
|
const changedLines = this.extractChangedLineNumbers(diff);
|
|
469
|
-
|
|
470
461
|
if (changedLines.length === 0) return [];
|
|
471
462
|
|
|
472
|
-
|
|
463
|
+
const changedMethods = [];
|
|
464
|
+
|
|
465
|
+
// Get all classes in the file
|
|
466
|
+
const classes = sourceFile.getClasses();
|
|
467
|
+
|
|
473
468
|
for (const classDecl of classes) {
|
|
474
469
|
const className = classDecl.getName();
|
|
470
|
+
if (!className) continue;
|
|
471
|
+
|
|
475
472
|
const methods = classDecl.getMethods();
|
|
476
|
-
|
|
473
|
+
|
|
477
474
|
for (const method of methods) {
|
|
478
475
|
const methodName = method.getName();
|
|
479
|
-
const startLine = method.getStartLineNumber();
|
|
480
|
-
const endLine = method.getEndLineNumber();
|
|
481
|
-
|
|
482
|
-
// Check if any changed line falls within this method's range
|
|
483
|
-
const hasChangedLine = changedLines.some(
|
|
484
|
-
lineNum => lineNum >= startLine && lineNum <= endLine
|
|
485
|
-
);
|
|
486
|
-
|
|
487
|
-
if (hasChangedLine) {
|
|
488
|
-
const fullName = `${className}.${methodName}`;
|
|
489
|
-
changedMethods.add(fullName);
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
// 2. Detect method calls that were modified in the diff
|
|
495
|
-
const diffLines = diff.split('\n');
|
|
496
|
-
|
|
497
|
-
for (const line of diffLines) {
|
|
498
|
-
// Only look at added/removed lines
|
|
499
|
-
if (!line.startsWith('+') && !line.startsWith('-')) continue;
|
|
500
|
-
|
|
501
|
-
const codeLine = line.substring(1).trim();
|
|
502
|
-
|
|
503
|
-
// Pattern: object.methodName(...) or await object.methodName(...)
|
|
504
|
-
const methodCallPattern = /(?:await\s+)?(\w+)\.(\w+)\s*\(/g;
|
|
505
|
-
let match;
|
|
506
|
-
|
|
507
|
-
while ((match = methodCallPattern.exec(codeLine)) !== null) {
|
|
508
|
-
const objectName = match[1];
|
|
509
|
-
const methodName = match[2];
|
|
510
476
|
|
|
511
|
-
//
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
477
|
+
// Check if this specific method contains changes
|
|
478
|
+
if (this.methodContainsChanges(method, changedLines)) {
|
|
479
|
+
changedMethods.push({
|
|
480
|
+
className: className,
|
|
481
|
+
methodName: methodName,
|
|
482
|
+
file: filePath,
|
|
483
|
+
fullName: `${className}.${methodName}`
|
|
484
|
+
});
|
|
517
485
|
}
|
|
518
486
|
}
|
|
519
487
|
}
|
|
520
|
-
|
|
521
|
-
// Combine both: methods containing changes + method calls that changed
|
|
522
|
-
const allChangedMethods = [...changedMethods, ...changedMethodCalls];
|
|
523
488
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
489
|
+
return changedMethods;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Check if a method contains actual code changes using ts-morph AST
|
|
494
|
+
*/
|
|
495
|
+
methodContainsChanges(method, changedLines) {
|
|
496
|
+
const body = method.getBody();
|
|
497
|
+
if (!body) return false;
|
|
498
|
+
|
|
499
|
+
// Get the method body block (excluding signature and decorators)
|
|
500
|
+
const bodyStartLine = body.getStartLineNumber();
|
|
501
|
+
const bodyEndLine = body.getEndLineNumber();
|
|
502
|
+
|
|
503
|
+
// Check if any changed line is within the method body
|
|
504
|
+
const hasChangesInBody = changedLines.some(
|
|
505
|
+
line => line >= bodyStartLine && line <= bodyEndLine
|
|
506
|
+
);
|
|
507
|
+
|
|
508
|
+
return hasChangesInBody;
|
|
534
509
|
}
|
|
535
510
|
|
|
536
511
|
/**
|