@kridaydave/code-mapper 1.0.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.
- package/AGENTS.md +174 -0
- package/LICENSE.md +21 -0
- package/README.md +113 -0
- package/dist/graph/Graph.test.js +173 -0
- package/dist/graph/Graph.test.js.map +1 -0
- package/dist/graph/GraphAnalyzer.js +435 -0
- package/dist/graph/GraphAnalyzer.js.map +1 -0
- package/dist/graph/GraphBuilder.js +225 -0
- package/dist/graph/GraphBuilder.js.map +1 -0
- package/dist/graph/types.js +2 -0
- package/dist/graph/types.js.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/cache.js +72 -0
- package/dist/mcp/cache.js.map +1 -0
- package/dist/mcp/resources.js +119 -0
- package/dist/mcp/resources.js.map +1 -0
- package/dist/mcp/tools.js +444 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/mcp/tools.test.js +81 -0
- package/dist/mcp/tools.test.js.map +1 -0
- package/dist/parser/ComplexityAnalyzer.js +208 -0
- package/dist/parser/ComplexityAnalyzer.js.map +1 -0
- package/dist/parser/FileAnalyzer.js +191 -0
- package/dist/parser/FileAnalyzer.js.map +1 -0
- package/dist/parser/ProjectParser.js +134 -0
- package/dist/parser/ProjectParser.js.map +1 -0
- package/dist/parser/ProjectParser.test.js +77 -0
- package/dist/parser/ProjectParser.test.js.map +1 -0
- package/dist/parser/types.js +2 -0
- package/dist/parser/types.js.map +1 -0
- package/docs/PHASE2_PLAN.md +435 -0
- package/fixtures/test-project/calculator.ts +28 -0
- package/fixtures/test-project/index.ts +2 -0
- package/fixtures/test-project/math.ts +11 -0
- package/package.json +35 -0
- package/src/graph/Graph.test.ts +222 -0
- package/src/graph/GraphAnalyzer.ts +502 -0
- package/src/graph/GraphBuilder.ts +258 -0
- package/src/graph/types.ts +42 -0
- package/src/index.ts +38 -0
- package/src/mcp/cache.ts +89 -0
- package/src/mcp/resources.ts +137 -0
- package/src/mcp/tools.test.ts +104 -0
- package/src/mcp/tools.ts +529 -0
- package/src/parser/ComplexityAnalyzer.ts +275 -0
- package/src/parser/FileAnalyzer.ts +215 -0
- package/src/parser/ProjectParser.test.ts +96 -0
- package/src/parser/ProjectParser.ts +172 -0
- package/src/parser/types.ts +77 -0
- package/src/types/graphology-pagerank.d.ts +20 -0
- package/tsconfig.json +17 -0
- package/vitest.config.ts +15 -0
|
@@ -0,0 +1,222 @@
|
|
|
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
|
+
});
|