@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.
Files changed (48) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +1 -0
  3. package/bin/code-mapper.mjs +86 -0
  4. package/dist/graph/GraphAnalyzer.js +32 -65
  5. package/dist/graph/GraphAnalyzer.js.map +1 -1
  6. package/dist/graph/GraphBuilder.js +18 -45
  7. package/dist/graph/GraphBuilder.js.map +1 -1
  8. package/dist/index.js +100 -23
  9. package/dist/index.js.map +1 -1
  10. package/dist/mcp/cache.js +8 -17
  11. package/dist/mcp/cache.js.map +1 -1
  12. package/dist/mcp/resources.js +5 -1
  13. package/dist/mcp/resources.js.map +1 -1
  14. package/dist/mcp/tools.js +190 -35
  15. package/dist/mcp/tools.js.map +1 -1
  16. package/dist/parser/ComplexityAnalyzer.js +19 -2
  17. package/dist/parser/ComplexityAnalyzer.js.map +1 -1
  18. package/dist/parser/FileAnalyzer.js +8 -30
  19. package/dist/parser/FileAnalyzer.js.map +1 -1
  20. package/dist/parser/ProjectParser.js +8 -5
  21. package/dist/parser/ProjectParser.js.map +1 -1
  22. package/dist/parser/ProjectParser.test.js +1 -17
  23. package/dist/parser/ProjectParser.test.js.map +1 -1
  24. package/dist/tui/index.js +239 -0
  25. package/dist/tui/index.js.map +1 -0
  26. package/package.json +82 -35
  27. package/AGENTS.md +0 -174
  28. package/docs/PHASE2_PLAN.md +0 -435
  29. package/fixtures/test-project/calculator.ts +0 -28
  30. package/fixtures/test-project/index.ts +0 -2
  31. package/fixtures/test-project/math.ts +0 -11
  32. package/src/graph/Graph.test.ts +0 -222
  33. package/src/graph/GraphAnalyzer.ts +0 -502
  34. package/src/graph/GraphBuilder.ts +0 -258
  35. package/src/graph/types.ts +0 -42
  36. package/src/index.ts +0 -38
  37. package/src/mcp/cache.ts +0 -89
  38. package/src/mcp/resources.ts +0 -137
  39. package/src/mcp/tools.test.ts +0 -104
  40. package/src/mcp/tools.ts +0 -529
  41. package/src/parser/ComplexityAnalyzer.ts +0 -275
  42. package/src/parser/FileAnalyzer.ts +0 -215
  43. package/src/parser/ProjectParser.test.ts +0 -96
  44. package/src/parser/ProjectParser.ts +0 -172
  45. package/src/parser/types.ts +0 -77
  46. package/src/types/graphology-pagerank.d.ts +0 -20
  47. package/tsconfig.json +0 -17
  48. package/vitest.config.ts +0 -15
@@ -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
- }
@@ -1,2 +0,0 @@
1
- export { add, multiply } from "./math.js";
2
- export { Calculator, type Config } from "./calculator.js";
@@ -1,11 +0,0 @@
1
- export function add(a: number, b: number): number {
2
- return a + b;
3
- }
4
-
5
- export function multiply(a: number, b: number): number {
6
- return a * b;
7
- }
8
-
9
- export default function subtract(a: number, b: number): number {
10
- return a - b;
11
- }
@@ -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
- });