@kridaydave/code-mapper 1.0.0 → 1.0.1
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/CHANGELOG.md +31 -0
- package/README.md +1 -0
- package/bin/code-mapper.mjs +86 -0
- package/dist/graph/GraphAnalyzer.js +32 -65
- package/dist/graph/GraphAnalyzer.js.map +1 -1
- package/dist/graph/GraphBuilder.js +18 -45
- package/dist/graph/GraphBuilder.js.map +1 -1
- package/dist/index.js +100 -23
- package/dist/index.js.map +1 -1
- package/dist/mcp/cache.js +8 -17
- package/dist/mcp/cache.js.map +1 -1
- package/dist/mcp/resources.js +5 -1
- package/dist/mcp/resources.js.map +1 -1
- package/dist/mcp/tools.js +190 -35
- package/dist/mcp/tools.js.map +1 -1
- package/dist/parser/ComplexityAnalyzer.js +19 -2
- package/dist/parser/ComplexityAnalyzer.js.map +1 -1
- package/dist/parser/FileAnalyzer.js +8 -30
- package/dist/parser/FileAnalyzer.js.map +1 -1
- package/dist/parser/ProjectParser.js +8 -5
- package/dist/parser/ProjectParser.js.map +1 -1
- package/dist/parser/ProjectParser.test.js +1 -17
- package/dist/parser/ProjectParser.test.js.map +1 -1
- package/dist/tui/index.js +239 -0
- package/dist/tui/index.js.map +1 -0
- package/package.json +82 -35
- package/AGENTS.md +0 -174
- package/docs/PHASE2_PLAN.md +0 -435
- package/fixtures/test-project/calculator.ts +0 -28
- package/fixtures/test-project/index.ts +0 -2
- package/fixtures/test-project/math.ts +0 -11
- package/src/graph/Graph.test.ts +0 -222
- package/src/graph/GraphAnalyzer.ts +0 -502
- package/src/graph/GraphBuilder.ts +0 -258
- package/src/graph/types.ts +0 -42
- package/src/index.ts +0 -38
- package/src/mcp/cache.ts +0 -89
- package/src/mcp/resources.ts +0 -137
- package/src/mcp/tools.test.ts +0 -104
- package/src/mcp/tools.ts +0 -529
- package/src/parser/ComplexityAnalyzer.ts +0 -275
- package/src/parser/FileAnalyzer.ts +0 -215
- package/src/parser/ProjectParser.test.ts +0 -96
- package/src/parser/ProjectParser.ts +0 -172
- package/src/parser/types.ts +0 -77
- package/src/types/graphology-pagerank.d.ts +0 -20
- package/tsconfig.json +0 -17
- package/vitest.config.ts +0 -15
package/docs/PHASE2_PLAN.md
DELETED
|
@@ -1,435 +0,0 @@
|
|
|
1
|
-
# CodeGraph Phase 2 Implementation Plan
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
This document outlines the implementation plan for Phase 2 of CodeGraph, focusing on new features and improvements to existing functionality.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## 1. Export Format Enhancements
|
|
10
|
-
|
|
11
|
-
### 1.1 DOT (Graphviz) Export
|
|
12
|
-
|
|
13
|
-
**Objective**: Add support for exporting dependency graphs in DOT format, enabling visualization with Graphviz, Webgraphviz, and other DOT-compatible tools.
|
|
14
|
-
|
|
15
|
-
**Implementation Details**:
|
|
16
|
-
|
|
17
|
-
- **Location**: `src/graph/GraphAnalyzer.ts`
|
|
18
|
-
- **New Method**: `toDot(targetFile?: string): string`
|
|
19
|
-
- **DOT Format Specification**:
|
|
20
|
-
```dot
|
|
21
|
-
digraph code_graph {
|
|
22
|
-
rankdir=LR;
|
|
23
|
-
node [shape=box];
|
|
24
|
-
|
|
25
|
-
"file:src/index.ts" [label="index.ts", style=filled, fillcolor=lightblue];
|
|
26
|
-
"file:src/utils.ts" [label="utils.ts"];
|
|
27
|
-
|
|
28
|
-
"file:src/index.ts" -> "file:src/utils.ts" [label="imports"];
|
|
29
|
-
}
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
**Node Styling by Type**:
|
|
33
|
-
| Kind | Shape | Color |
|
|
34
|
-
|------|-------|-------|
|
|
35
|
-
| file | box | lightblue |
|
|
36
|
-
| function | ellipse | lightgreen |
|
|
37
|
-
| class | box3d | lightyellow |
|
|
38
|
-
|
|
39
|
-
**Edge Styling by Type**:
|
|
40
|
-
| Kind | Style | Color |
|
|
41
|
-
|------|-------|-------|
|
|
42
|
-
| imports | solid | black |
|
|
43
|
-
| contains | dotted | gray |
|
|
44
|
-
| extends | dashed | blue |
|
|
45
|
-
| implements | dashed | green |
|
|
46
|
-
|
|
47
|
-
**Deliverables**:
|
|
48
|
-
- `toDot()` method in GraphAnalyzer
|
|
49
|
-
- Support for filtered graphs (targetFile parameter)
|
|
50
|
-
- Node/edge styling based on kind
|
|
51
|
-
|
|
52
|
-
### 1.2 PlantUML Export
|
|
53
|
-
|
|
54
|
-
**Objective**: Add support for exporting dependency graphs in PlantUML format, enabling visualization with PlantUML server and IDE plugins.
|
|
55
|
-
|
|
56
|
-
**Implementation Details**:
|
|
57
|
-
|
|
58
|
-
- **Location**: `src/graph/GraphAnalyzer.ts`
|
|
59
|
-
- **New Method**: `toPlantUML(targetFile?: string): string`
|
|
60
|
-
- **PlantUML Format Specification**:
|
|
61
|
-
```plantuml
|
|
62
|
-
@startuml
|
|
63
|
-
skinparam linetype ortho
|
|
64
|
-
|
|
65
|
-
component "index.ts" as index
|
|
66
|
-
component "utils.ts" as utils
|
|
67
|
-
|
|
68
|
-
index -down-> utils : imports
|
|
69
|
-
@enduml
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
**Deliverables**:
|
|
73
|
-
- `toPlantUML()` method in GraphAnalyzer
|
|
74
|
-
- Support for component diagrams
|
|
75
|
-
- Proper escaping of special characters
|
|
76
|
-
|
|
77
|
-
---
|
|
78
|
-
|
|
79
|
-
## 2. Graph Metrics Enhancement
|
|
80
|
-
|
|
81
|
-
### 2.1 PageRank Metric
|
|
82
|
-
|
|
83
|
-
**Objective**: Add PageRank as a centrality metric to identify influential files in the dependency graph.
|
|
84
|
-
|
|
85
|
-
**Implementation Details**:
|
|
86
|
-
|
|
87
|
-
- **Dependency**: Install `graphology-pagerank`
|
|
88
|
-
- **Location**: `src/graph/GraphAnalyzer.ts`
|
|
89
|
-
- **Method Modification**: Extend `rankImpact()` to support `metric: "pagerank"`
|
|
90
|
-
|
|
91
|
-
**PageRank Algorithm**:
|
|
92
|
-
- Iterative algorithm that ranks nodes based on incoming links
|
|
93
|
-
- Accounts for indirect dependencies through random walks
|
|
94
|
-
- Useful for identifying "hub" files that connect many modules
|
|
95
|
-
|
|
96
|
-
**Command**:
|
|
97
|
-
```bash
|
|
98
|
-
npm install graphology-pagerank
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
**Code Changes**:
|
|
102
|
-
```typescript
|
|
103
|
-
import pagerank from "graphology-pagerank";
|
|
104
|
-
|
|
105
|
-
// In rankImpact method:
|
|
106
|
-
if (metric === "pagerank") {
|
|
107
|
-
const ranks = pagerank(this.graph);
|
|
108
|
-
for (const [node, score] of Object.entries(ranks)) {
|
|
109
|
-
scores.set(node, score);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
**Deliverables**:
|
|
115
|
-
- Install graphology-pagerank dependency
|
|
116
|
-
- Extend rankImpact to support pagerank metric
|
|
117
|
-
- Update MCP tool schema
|
|
118
|
-
|
|
119
|
-
---
|
|
120
|
-
|
|
121
|
-
## 3. Code Complexity Analysis
|
|
122
|
-
|
|
123
|
-
### 3.1 ComplexityAnalyzer
|
|
124
|
-
|
|
125
|
-
**Objective**: Analyze code complexity metrics for each file, enabling identification of files that may need refactoring.
|
|
126
|
-
|
|
127
|
-
**Implementation Details**:
|
|
128
|
-
|
|
129
|
-
- **New File**: `src/parser/ComplexityAnalyzer.ts`
|
|
130
|
-
- **Metrics Calculated**:
|
|
131
|
-
|
|
132
|
-
| Metric | Description | Threshold |
|
|
133
|
-
|--------|-------------|-----------|
|
|
134
|
-
| Cyclomatic Complexity | Number of linearly independent paths through code | > 10 is concerning |
|
|
135
|
-
| Cognitive Complexity | How hard code is to understand | > 15 is concerning |
|
|
136
|
-
| Nesting Depth | Maximum nesting level of control structures | > 4 is concerning |
|
|
137
|
-
| Lines of Code | Total lines in file | > 500 is large |
|
|
138
|
-
| Function Count | Number of functions in file | > 20 is many |
|
|
139
|
-
| Class Count | Number of classes in file | > 10 is many |
|
|
140
|
-
|
|
141
|
-
**Class Structure**:
|
|
142
|
-
```typescript
|
|
143
|
-
export interface ComplexityResult {
|
|
144
|
-
filePath: string;
|
|
145
|
-
relativePath: string;
|
|
146
|
-
cyclomaticComplexity: number;
|
|
147
|
-
cognitiveComplexity: number;
|
|
148
|
-
nestingDepth: number;
|
|
149
|
-
linesOfCode: number;
|
|
150
|
-
functionCount: number;
|
|
151
|
-
classCount: number;
|
|
152
|
-
overallScore: number; // Normalized 0-100
|
|
153
|
-
issues: string[]; // List of identified issues
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
export class ComplexityAnalyzer {
|
|
157
|
-
analyze(sourceFile: SourceFile): ComplexityResult;
|
|
158
|
-
analyzeProject(parseResult: ParseResult): ComplexityResult[];
|
|
159
|
-
getTopComplexFiles(n: number): ComplexityResult[];
|
|
160
|
-
}
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
**Cyclomatic Complexity Calculation**:
|
|
164
|
-
```
|
|
165
|
-
CC = E - N + 2P
|
|
166
|
-
|
|
167
|
-
Where:
|
|
168
|
-
- E = number of edges (control flow paths)
|
|
169
|
-
- N = number of nodes
|
|
170
|
-
- P = number of connected components
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
Simplified approach: Count decision points (if, for, while, switch, catch, &&, ||)
|
|
174
|
-
|
|
175
|
-
**Cognitive Complexity Calculation**:
|
|
176
|
-
- Increments for:
|
|
177
|
-
- Nesting level (+1 per level)
|
|
178
|
-
- Control flow keywords (+1 each)
|
|
179
|
-
- Short-circuit logic (+1)
|
|
180
|
-
- Recursion (+1)
|
|
181
|
-
- No increments for:
|
|
182
|
-
- Simple method calls
|
|
183
|
-
- Property access
|
|
184
|
-
|
|
185
|
-
**Deliverables**:
|
|
186
|
-
- New `ComplexityAnalyzer` class
|
|
187
|
-
- Integration with existing parser
|
|
188
|
-
- New MCP tool: `analyze_complexity`
|
|
189
|
-
|
|
190
|
-
---
|
|
191
|
-
|
|
192
|
-
## 4. Progress Indicators
|
|
193
|
-
|
|
194
|
-
### 4.1 Progress Reporting
|
|
195
|
-
|
|
196
|
-
**Objective**: Add progress reporting for large codebase scans, improving user experience.
|
|
197
|
-
|
|
198
|
-
**Implementation Details**:
|
|
199
|
-
|
|
200
|
-
- **Location**: `src/parser/ProjectParser.ts`
|
|
201
|
-
- **Mechanism**: Progress callback function
|
|
202
|
-
|
|
203
|
-
**Interface**:
|
|
204
|
-
```typescript
|
|
205
|
-
interface ProgressCallback {
|
|
206
|
-
(progress: ProgressInfo): void;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
interface ProgressInfo {
|
|
210
|
-
phase: "scanning" | "parsing" | "analyzing";
|
|
211
|
-
current: number;
|
|
212
|
-
total: number;
|
|
213
|
-
currentFile?: string;
|
|
214
|
-
percentComplete: number;
|
|
215
|
-
}
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
**Usage in ProjectParser**:
|
|
219
|
-
```typescript
|
|
220
|
-
interface ParseOptions {
|
|
221
|
-
directory: string;
|
|
222
|
-
onProgress?: ProgressCallback;
|
|
223
|
-
maxFiles?: number;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
async parse(options: ParseOptions): Promise<ParseResult> {
|
|
227
|
-
const files = this.findFiles(directory);
|
|
228
|
-
const total = files.length;
|
|
229
|
-
|
|
230
|
-
for (let i = 0; i < files.length; i++) {
|
|
231
|
-
// ... parse file
|
|
232
|
-
|
|
233
|
-
if (options.onProgress) {
|
|
234
|
-
options.onProgress({
|
|
235
|
-
phase: "parsing",
|
|
236
|
-
current: i + 1,
|
|
237
|
-
total,
|
|
238
|
-
currentFile: files[i],
|
|
239
|
-
percentComplete: Math.round(((i + 1) / total) * 100)
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
**Deliverables**:
|
|
247
|
-
- Progress callback option in ProjectParser
|
|
248
|
-
- Progress information in MCP responses (via structuredContent)
|
|
249
|
-
- Console logging fallback
|
|
250
|
-
|
|
251
|
-
---
|
|
252
|
-
|
|
253
|
-
## 5. Error Message Improvements
|
|
254
|
-
|
|
255
|
-
### 5.1 Enhanced Error Messages
|
|
256
|
-
|
|
257
|
-
**Objective**: Provide more helpful error messages with suggestions for common issues.
|
|
258
|
-
|
|
259
|
-
**Implementation Details**:
|
|
260
|
-
|
|
261
|
-
- **Location**: `src/mcp/tools.ts`
|
|
262
|
-
- **Pattern**: Error messages should include:
|
|
263
|
-
- What went wrong
|
|
264
|
-
- Why it happened
|
|
265
|
-
- How to fix it
|
|
266
|
-
|
|
267
|
-
**Error Categories**:
|
|
268
|
-
|
|
269
|
-
| Error | Suggestion |
|
|
270
|
-
|-------|------------|
|
|
271
|
-
| Directory not found | "Did you mean: ./src"? |
|
|
272
|
-
| No TypeScript files | "Ensure your directory contains .ts/.tsx files" |
|
|
273
|
-
| Too many files | "Consider scanning a subdirectory" |
|
|
274
|
-
| Permission denied | "Check file permissions" |
|
|
275
|
-
| Empty result | "Directory may be empty or all files are ignored" |
|
|
276
|
-
|
|
277
|
-
**Implementation**:
|
|
278
|
-
```typescript
|
|
279
|
-
function getErrorMessage(error: Error, context: Record<string, unknown>): string {
|
|
280
|
-
const baseMessage = error.message;
|
|
281
|
-
|
|
282
|
-
if (error.message.includes("does not exist")) {
|
|
283
|
-
return `${baseMessage}. Did you mean to use an absolute path?`;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
if (error.message.includes("Too many files")) {
|
|
287
|
-
return `${baseMessage}. Try scanning a specific subdirectory like ./src/components`;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
return baseMessage;
|
|
291
|
-
}
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
**Deliverables**:
|
|
295
|
-
- Enhanced error handling in safeHandler
|
|
296
|
-
- Context-aware error suggestions
|
|
297
|
-
- Structured error codes
|
|
298
|
-
|
|
299
|
-
---
|
|
300
|
-
|
|
301
|
-
## 6. MCP Tool Updates
|
|
302
|
-
|
|
303
|
-
### 6.1 Updated analyze_dependencies
|
|
304
|
-
|
|
305
|
-
**Changes**:
|
|
306
|
-
```typescript
|
|
307
|
-
inputSchema: z.object({
|
|
308
|
-
directory: z.string(),
|
|
309
|
-
targetFile: z.string().optional(),
|
|
310
|
-
format: z.enum(["json", "mermaid", "dot", "plantuml"]).default("json"),
|
|
311
|
-
}));
|
|
312
|
-
|
|
313
|
-
outputSchema: z.object({
|
|
314
|
-
format: z.string(),
|
|
315
|
-
nodeCount: z.number(),
|
|
316
|
-
edgeCount: z.number(),
|
|
317
|
-
nodes: z.array(...),
|
|
318
|
-
edges: z.array(...),
|
|
319
|
-
cycles: z.array(...),
|
|
320
|
-
mermaid: z.string().optional(),
|
|
321
|
-
dot: z.string().optional(), // NEW
|
|
322
|
-
plantuml: z.string().optional(), // NEW
|
|
323
|
-
}));
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
### 6.2 Updated rank_impact
|
|
327
|
-
|
|
328
|
-
**Changes**:
|
|
329
|
-
```typescript
|
|
330
|
-
inputSchema: z.object({
|
|
331
|
-
directory: z.string(),
|
|
332
|
-
metric: z.enum(["inDegree", "outDegree", "betweenness", "pagerank"]).default("inDegree"),
|
|
333
|
-
topN: z.number().default(10),
|
|
334
|
-
}));
|
|
335
|
-
```
|
|
336
|
-
|
|
337
|
-
### 6.3 New Tool: analyze_complexity
|
|
338
|
-
|
|
339
|
-
**New Tool Registration**:
|
|
340
|
-
```typescript
|
|
341
|
-
server.registerTool(
|
|
342
|
-
"analyze_complexity",
|
|
343
|
-
{
|
|
344
|
-
title: "Analyze Code Complexity",
|
|
345
|
-
description: "Analyze code complexity metrics for each file in the codebase. Identifies files that may need refactoring based on cyclomatic complexity, cognitive complexity, nesting depth, and size.",
|
|
346
|
-
inputSchema: z.object({
|
|
347
|
-
directory: z.string().describe("Path to the codebase directory"),
|
|
348
|
-
threshold: z.number().optional().describe("Minimum complexity score to report"),
|
|
349
|
-
topN: z.number().default(10).describe("Number of most complex files to return"),
|
|
350
|
-
}),
|
|
351
|
-
outputSchema: z.object({
|
|
352
|
-
totalFiles: z.number(),
|
|
353
|
-
files: z.array(z.object({
|
|
354
|
-
relativePath: z.string(),
|
|
355
|
-
cyclomaticComplexity: z.number(),
|
|
356
|
-
cognitiveComplexity: z.number(),
|
|
357
|
-
nestingDepth: z.number(),
|
|
358
|
-
linesOfCode: z.number(),
|
|
359
|
-
functionCount: z.number(),
|
|
360
|
-
classCount: z.number(),
|
|
361
|
-
overallScore: z.number(),
|
|
362
|
-
issues: z.array(z.string()),
|
|
363
|
-
})),
|
|
364
|
-
summary: z.object({
|
|
365
|
-
avgComplexity: z.number(),
|
|
366
|
-
maxComplexity: z.number(),
|
|
367
|
-
filesNeedingRefactoring: z.number(),
|
|
368
|
-
}),
|
|
369
|
-
}),
|
|
370
|
-
},
|
|
371
|
-
async ({ directory, threshold, topN }) => { ... }
|
|
372
|
-
);
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
---
|
|
376
|
-
|
|
377
|
-
## 7. Implementation Order
|
|
378
|
-
|
|
379
|
-
### Phase 2A: Export Formats (Priority: High)
|
|
380
|
-
1. Install graphology-pagerank dependency
|
|
381
|
-
2. Add toMermaid() improvements (if needed)
|
|
382
|
-
3. Implement toDot() method
|
|
383
|
-
4. Implement toPlantUML() method
|
|
384
|
-
5. Update analyze_dependencies tool
|
|
385
|
-
|
|
386
|
-
### Phase 2B: Graph Metrics (Priority: High)
|
|
387
|
-
1. Add PageRank support to rankImpact
|
|
388
|
-
2. Update rank_impact tool schema
|
|
389
|
-
|
|
390
|
-
### Phase 2C: Complexity Analysis (Priority: Medium)
|
|
391
|
-
1. Create ComplexityAnalyzer class
|
|
392
|
-
2. Implement cyclomatic complexity calculation
|
|
393
|
-
3. Implement cognitive complexity calculation
|
|
394
|
-
4. Add analyze_complexity tool
|
|
395
|
-
|
|
396
|
-
### Phase 2D: Improvements (Priority: Medium)
|
|
397
|
-
1. Add progress callback to ProjectParser
|
|
398
|
-
2. Enhance error messages
|
|
399
|
-
3. Add suggestions to error responses
|
|
400
|
-
|
|
401
|
-
---
|
|
402
|
-
|
|
403
|
-
## 8. Testing Plan
|
|
404
|
-
|
|
405
|
-
### Unit Tests
|
|
406
|
-
- Test toDot() output format
|
|
407
|
-
- Test toPlantUML() output format
|
|
408
|
-
- Test PageRank metric calculation
|
|
409
|
-
- Test ComplexityAnalyzer accuracy
|
|
410
|
-
|
|
411
|
-
### Integration Tests
|
|
412
|
-
- Test full flow with sample project
|
|
413
|
-
- Test MCP tool responses
|
|
414
|
-
|
|
415
|
-
---
|
|
416
|
-
|
|
417
|
-
## 9. Backward Compatibility
|
|
418
|
-
|
|
419
|
-
All changes must maintain backward compatibility:
|
|
420
|
-
- Existing MCP tool names unchanged
|
|
421
|
-
- Existing output formats unchanged
|
|
422
|
-
- New fields optional
|
|
423
|
-
|
|
424
|
-
---
|
|
425
|
-
|
|
426
|
-
## 10. Success Criteria
|
|
427
|
-
|
|
428
|
-
1. ✅ DOT export produces valid Graphviz output
|
|
429
|
-
2. ✅ PlantUML export produces valid PlantUML output
|
|
430
|
-
3. ✅ PageRank metric provides meaningful rankings
|
|
431
|
-
4. ✅ Complexity analysis identifies complex files correctly
|
|
432
|
-
5. ✅ Progress reporting works for large projects
|
|
433
|
-
6. ✅ Error messages include helpful suggestions
|
|
434
|
-
7. ✅ All existing tests continue to pass
|
|
435
|
-
8. ✅ TypeScript type checking succeeds
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { add, multiply } from "./math.js";
|
|
2
|
-
|
|
3
|
-
export class Calculator {
|
|
4
|
-
private value: number = 0;
|
|
5
|
-
|
|
6
|
-
constructor(initial: number = 0) {
|
|
7
|
-
this.value = initial;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
public add(n: number): number {
|
|
11
|
-
this.value = add(this.value, n);
|
|
12
|
-
return this.value;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
public multiply(n: number): number {
|
|
16
|
-
this.value = multiply(this.value, n);
|
|
17
|
-
return this.value;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
public getValue(): number {
|
|
21
|
-
return this.value;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface Config {
|
|
26
|
-
apiUrl: string;
|
|
27
|
-
timeout: number;
|
|
28
|
-
}
|
package/src/graph/Graph.test.ts
DELETED
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
-
import { ProjectParser } from "../parser/ProjectParser.js";
|
|
3
|
-
import { GraphBuilder } from "../graph/GraphBuilder.js";
|
|
4
|
-
import { GraphAnalyzer } from "../graph/GraphAnalyzer.js";
|
|
5
|
-
import path from "node:path";
|
|
6
|
-
|
|
7
|
-
describe("GraphBuilder", () => {
|
|
8
|
-
let parser: ProjectParser;
|
|
9
|
-
let builder: GraphBuilder;
|
|
10
|
-
let parseResult: Awaited<ReturnType<ProjectParser["parse"]>>;
|
|
11
|
-
|
|
12
|
-
beforeEach(async () => {
|
|
13
|
-
parser = new ProjectParser();
|
|
14
|
-
builder = new GraphBuilder();
|
|
15
|
-
const testDir = path.resolve("./fixtures/test-project");
|
|
16
|
-
parseResult = await parser.parse(testDir);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
describe("build", () => {
|
|
20
|
-
it("should build a graph from parse result", () => {
|
|
21
|
-
const { graph, nodes, edges } = builder.build(parseResult);
|
|
22
|
-
|
|
23
|
-
expect(graph.order).toBeGreaterThan(0);
|
|
24
|
-
expect(nodes.length).toBeGreaterThan(0);
|
|
25
|
-
expect(edges.length).toBeGreaterThan(0);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it("should create file nodes", () => {
|
|
29
|
-
const { nodes } = builder.build(parseResult);
|
|
30
|
-
|
|
31
|
-
const fileNodes = nodes.filter(n => n.kind === "file");
|
|
32
|
-
expect(fileNodes.length).toBe(3);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it("should create function nodes", () => {
|
|
36
|
-
const { nodes } = builder.build(parseResult);
|
|
37
|
-
|
|
38
|
-
const fnNodes = nodes.filter(n => n.kind === "function");
|
|
39
|
-
expect(fnNodes.length).toBe(3);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it("should create class nodes", () => {
|
|
43
|
-
const { nodes } = builder.build(parseResult);
|
|
44
|
-
|
|
45
|
-
const classNodes = nodes.filter(n => n.kind === "class");
|
|
46
|
-
expect(classNodes.length).toBe(1);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it("should create import edges between files", () => {
|
|
50
|
-
const { edges } = builder.build(parseResult);
|
|
51
|
-
|
|
52
|
-
const importEdges = edges.filter(e => e.kind === "imports");
|
|
53
|
-
expect(importEdges.length).toBeGreaterThan(0);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it("should create containment edges", () => {
|
|
57
|
-
const { edges } = builder.build(parseResult);
|
|
58
|
-
|
|
59
|
-
const containsEdges = edges.filter(e => e.kind === "contains");
|
|
60
|
-
expect(containsEdges.length).toBeGreaterThan(0);
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
describe("GraphAnalyzer", () => {
|
|
66
|
-
let parser: ProjectParser;
|
|
67
|
-
let builder: GraphBuilder;
|
|
68
|
-
let analyzer: GraphAnalyzer;
|
|
69
|
-
let parseResult: Awaited<ReturnType<ProjectParser["parse"]>>;
|
|
70
|
-
|
|
71
|
-
beforeEach(async () => {
|
|
72
|
-
parser = new ProjectParser();
|
|
73
|
-
builder = new GraphBuilder();
|
|
74
|
-
const testDir = path.resolve("./fixtures/test-project");
|
|
75
|
-
parseResult = await parser.parse(testDir);
|
|
76
|
-
const { graph, nodes, edges } = builder.build(parseResult);
|
|
77
|
-
analyzer = new GraphAnalyzer(graph, parseResult, nodes, edges);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
describe("rankImpact", () => {
|
|
81
|
-
it("should rank files by in-degree", () => {
|
|
82
|
-
const ranked = analyzer.rankImpact("inDegree");
|
|
83
|
-
|
|
84
|
-
expect(ranked.length).toBeGreaterThan(0);
|
|
85
|
-
expect(ranked[0]).toHaveProperty("relativePath");
|
|
86
|
-
expect(ranked[0]).toHaveProperty("score");
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it("should rank files by out-degree", () => {
|
|
90
|
-
const ranked = analyzer.rankImpact("outDegree");
|
|
91
|
-
|
|
92
|
-
expect(ranked.length).toBeGreaterThan(0);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it("should rank files by betweenness", () => {
|
|
96
|
-
const ranked = analyzer.rankImpact("betweenness");
|
|
97
|
-
|
|
98
|
-
expect(ranked.length).toBeGreaterThan(0);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it("should include file metrics in ranking", () => {
|
|
102
|
-
const ranked = analyzer.rankImpact("inDegree");
|
|
103
|
-
|
|
104
|
-
expect(ranked[0]).toHaveProperty("functionCount");
|
|
105
|
-
expect(ranked[0]).toHaveProperty("classCount");
|
|
106
|
-
expect(ranked[0]).toHaveProperty("importCount");
|
|
107
|
-
expect(ranked[0]).toHaveProperty("exportCount");
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
describe("findFunction", () => {
|
|
112
|
-
it("should find functions by name", () => {
|
|
113
|
-
const matches = analyzer.findFunction("add", "function");
|
|
114
|
-
|
|
115
|
-
expect(matches.length).toBeGreaterThan(0);
|
|
116
|
-
expect(matches[0].name).toBe("add");
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it("should find classes by name", () => {
|
|
120
|
-
const matches = analyzer.findFunction("Calculator", "class");
|
|
121
|
-
|
|
122
|
-
expect(matches.length).toBe(1);
|
|
123
|
-
expect(matches[0].name).toBe("Calculator");
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it("should find both functions and classes when type is any", () => {
|
|
127
|
-
const matches = analyzer.findFunction("Calculator", "any");
|
|
128
|
-
|
|
129
|
-
expect(matches.length).toBe(1);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it("should return empty for non-existent symbol", () => {
|
|
133
|
-
const matches = analyzer.findFunction("NonExistentSymbol", "any");
|
|
134
|
-
|
|
135
|
-
expect(matches).toEqual([]);
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
describe("getCallers and getCallees", () => {
|
|
140
|
-
it("should get callers for a node", () => {
|
|
141
|
-
const nodes = analyzer.getNodes();
|
|
142
|
-
const fileNode = nodes.find(n => n.kind === "file" && n.label.includes("calculator"));
|
|
143
|
-
|
|
144
|
-
if (fileNode) {
|
|
145
|
-
const callers = analyzer.getCallers(fileNode.id);
|
|
146
|
-
expect(callers).toBeDefined();
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it("should get callees for a node", () => {
|
|
151
|
-
const nodes = analyzer.getNodes();
|
|
152
|
-
const fileNode = nodes.find(n => n.kind === "file" && n.label.includes("calculator"));
|
|
153
|
-
|
|
154
|
-
if (fileNode) {
|
|
155
|
-
const callees = analyzer.getCallees(fileNode.id);
|
|
156
|
-
expect(callees).toBeDefined();
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
describe("traceCallChain", () => {
|
|
162
|
-
it("should find paths between nodes", () => {
|
|
163
|
-
const result = analyzer.traceCallChain("add", "Calculator");
|
|
164
|
-
|
|
165
|
-
expect(result).toHaveProperty("found");
|
|
166
|
-
expect(result).toHaveProperty("paths");
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
it("should return empty for non-existent nodes", () => {
|
|
170
|
-
const result = analyzer.traceCallChain("NonExistent", "AlsoNotHere");
|
|
171
|
-
|
|
172
|
-
expect(result.found).toBe(false);
|
|
173
|
-
expect(result.paths).toEqual([]);
|
|
174
|
-
});
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
describe("toMermaid", () => {
|
|
178
|
-
it("should generate mermaid diagram", () => {
|
|
179
|
-
const mermaid = analyzer.toMermaid();
|
|
180
|
-
|
|
181
|
-
expect(mermaid).toContain("graph TD");
|
|
182
|
-
expect(mermaid).toContain("-->");
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
it("should filter by target file", () => {
|
|
186
|
-
const mermaid = analyzer.toMermaid("calculator");
|
|
187
|
-
|
|
188
|
-
expect(mermaid).toContain("graph TD");
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
describe("detectCycles", () => {
|
|
193
|
-
it("should detect cycles in the graph", () => {
|
|
194
|
-
const cycles = analyzer.detectCycles();
|
|
195
|
-
|
|
196
|
-
expect(cycles).toBeDefined();
|
|
197
|
-
expect(Array.isArray(cycles)).toBe(true);
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
describe("getGraph, getNodes, getEdges, getParseResult", () => {
|
|
202
|
-
it("should return the graph", () => {
|
|
203
|
-
const graph = analyzer.getGraph();
|
|
204
|
-
expect(graph).toBeDefined();
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
it("should return nodes", () => {
|
|
208
|
-
const nodes = analyzer.getNodes();
|
|
209
|
-
expect(nodes.length).toBeGreaterThan(0);
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
it("should return edges", () => {
|
|
213
|
-
const edges = analyzer.getEdges();
|
|
214
|
-
expect(edges.length).toBeGreaterThan(0);
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
it("should return parse result", () => {
|
|
218
|
-
const result = analyzer.getParseResult();
|
|
219
|
-
expect(result.totalFiles).toBe(3);
|
|
220
|
-
});
|
|
221
|
-
});
|
|
222
|
-
});
|