depgraph-core 1.5.1 → 1.8.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/.vscode/depgraph-output.json +7197 -2405
- package/README.md +50 -6
- package/depgraph-mcp.js +24282 -0
- package/depgraph-output.json +7138 -2482
- package/depgraph.js +1880 -5
- package/package.json +10 -3
- package/docs/README.md +0 -56
- package/docs/architecture.md +0 -89
- package/docs/data-types.md +0 -218
- package/docs/language-registry.md +0 -205
- package/docs/stage-collector.md +0 -109
- package/docs/stage-graph.md +0 -157
- package/docs/stage-impact.md +0 -154
- package/docs/stage-metrics.md +0 -113
- package/docs/stage-output.md +0 -144
- package/docs/stage-parser.md +0 -144
package/package.json
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "depgraph-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "Dependency mapping and impact simulation for JS/TS projects",
|
|
5
5
|
"main": "depgraph.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"depgraph": "depgraph.js"
|
|
7
|
+
"depgraph": "depgraph.js",
|
|
8
|
+
"depgraph-mcp": "depgraph-mcp.js"
|
|
8
9
|
},
|
|
9
10
|
"scripts": {
|
|
10
11
|
"build": "tsc",
|
|
11
12
|
"bundle": "esbuild dist/main.js --bundle --platform=node --outfile=depgraph.js",
|
|
13
|
+
"bundle:mcp": "esbuild src/mcp-server.ts --bundle --platform=node --outfile=depgraph-mcp.js",
|
|
12
14
|
"release": "npm run build && npm run bundle",
|
|
15
|
+
"release:mcp": "npm run build && npm run bundle:mcp",
|
|
16
|
+
"release:all": "npm run build && npm run bundle && npm run bundle:mcp",
|
|
17
|
+
"mcp:register": "claude mcp add depgraph -- node $(pwd)/depgraph-mcp.js",
|
|
13
18
|
"test": "vitest",
|
|
14
19
|
"test:run": "vitest run"
|
|
15
20
|
},
|
|
@@ -33,6 +38,8 @@
|
|
|
33
38
|
"vitest": "^4.1.10"
|
|
34
39
|
},
|
|
35
40
|
"dependencies": {
|
|
36
|
-
"
|
|
41
|
+
"@modelcontextprotocol/sdk": "^1.15.0",
|
|
42
|
+
"depgraph-core": "^1.0.3",
|
|
43
|
+
"zod": "^3.25.0"
|
|
37
44
|
}
|
|
38
45
|
}
|
package/docs/README.md
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
# DepGraph — Developer Documentation
|
|
2
|
-
|
|
3
|
-
Welcome to the internal documentation for **DepGraph Core**.
|
|
4
|
-
|
|
5
|
-
This folder explains how every moving part of the compiler works so you can contribute confidently, extend it with new languages, or debug an unexpected result.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Contents
|
|
10
|
-
|
|
11
|
-
| File | What it covers |
|
|
12
|
-
|---|---|
|
|
13
|
-
| [architecture.md](./architecture.md) | Big-picture overview — how the pipeline fits together |
|
|
14
|
-
| [stage-collector.md](./stage-collector.md) | Stage 1 — File collection (scanning the project directory) |
|
|
15
|
-
| [stage-parser.md](./stage-parser.md) | Stage 2 — Source file parsing (entities, imports, exports) |
|
|
16
|
-
| [stage-graph.md](./stage-graph.md) | Stage 3 — Dependency graph construction |
|
|
17
|
-
| [stage-metrics.md](./stage-metrics.md) | Stage 4 — Metrics computation (centrality, degrees) |
|
|
18
|
-
| [stage-impact.md](./stage-impact.md) | Stage 5 — Impact simulation (BFS + risk scoring) |
|
|
19
|
-
| [stage-output.md](./stage-output.md) | Stage 6 — JSON report generation |
|
|
20
|
-
| [language-registry.md](./language-registry.md) | The language plugin system — how to add a new language |
|
|
21
|
-
| [data-types.md](./data-types.md) | All shared TypeScript interfaces, explained |
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
## Quick Mental Model
|
|
26
|
-
|
|
27
|
-
```
|
|
28
|
-
Your project folder
|
|
29
|
-
│
|
|
30
|
-
▼
|
|
31
|
-
┌─────────────┐
|
|
32
|
-
│ Collector │ ← finds every eligible source file
|
|
33
|
-
└──────┬──────┘
|
|
34
|
-
▼
|
|
35
|
-
┌─────────────┐
|
|
36
|
-
│ Parser │ ← reads each file; extracts entities, imports, exports
|
|
37
|
-
└──────┬──────┘
|
|
38
|
-
▼
|
|
39
|
-
┌─────────────┐
|
|
40
|
-
│ Graph │ ← links entities together through their imports
|
|
41
|
-
└──────┬──────┘
|
|
42
|
-
▼
|
|
43
|
-
┌─────────────┐
|
|
44
|
-
│ Metrics │ ← computes in/out-degree and centrality for every node
|
|
45
|
-
└──────┬──────┘
|
|
46
|
-
▼
|
|
47
|
-
┌─────────────┐
|
|
48
|
-
│ Impact │ ← (optional) BFS from a target node → risk report
|
|
49
|
-
└──────┬──────┘
|
|
50
|
-
▼
|
|
51
|
-
┌─────────────┐
|
|
52
|
-
│ Output │ ← serialises everything to depgraph-output.json
|
|
53
|
-
└─────────────┘
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
Each stage is self-contained. Data flows **forward only** — no stage reaches back to an earlier one.
|
package/docs/architecture.md
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
# Architecture Overview
|
|
2
|
-
|
|
3
|
-
> **File**: `src/main.ts` — the orchestration entry point
|
|
4
|
-
> **Role**: Wires together all six stages in sequential order and handles CLI argument parsing.
|
|
5
|
-
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
## The Pipeline at a Glance
|
|
9
|
-
|
|
10
|
-
DepGraph is a **linear, stage-based compiler**. There is no framework magic — it's a plain chain of function calls where the output of each stage becomes the input of the next.
|
|
11
|
-
|
|
12
|
-
```
|
|
13
|
-
collectFiles()
|
|
14
|
-
│ string[] (file paths)
|
|
15
|
-
▼
|
|
16
|
-
parseFiles()
|
|
17
|
-
│ ParsedFile[] (entities, imports, exports per file)
|
|
18
|
-
▼
|
|
19
|
-
buildGraph()
|
|
20
|
-
│ DepGraph (nodes + edges map)
|
|
21
|
-
▼
|
|
22
|
-
computeMetrics()
|
|
23
|
-
│ DepGraph (same graph, nodes now have inDegree / outDegree / centralityScore)
|
|
24
|
-
▼
|
|
25
|
-
simulateImpact() ← only runs when --impact flag is provided
|
|
26
|
-
│ ImpactReport
|
|
27
|
-
▼
|
|
28
|
-
writeOutput()
|
|
29
|
-
depgraph-output.json
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
Every stage lives in its own file under `src/stages/`. They are pure functions — given the same input they always produce the same output, and they never touch the file system except for the collector (reading) and output (writing).
|
|
33
|
-
|
|
34
|
-
---
|
|
35
|
-
|
|
36
|
-
## Entry Point — `src/main.ts`
|
|
37
|
-
|
|
38
|
-
`main.ts` does four things and nothing else:
|
|
39
|
-
|
|
40
|
-
1. **Parses CLI flags** — `--output`, `--impact`, `--verbose`, `--no-color`, `--help`
|
|
41
|
-
2. **Runs the pipeline** inside a single `try/catch` block
|
|
42
|
-
3. **Prints progress** to stdout (banner, summary, impact table)
|
|
43
|
-
4. **Exits with code 1** on any unhandled error
|
|
44
|
-
|
|
45
|
-
### Key variables wired at startup
|
|
46
|
-
|
|
47
|
-
```ts
|
|
48
|
-
const projectDir = args[0]; // the path to scan
|
|
49
|
-
const outputPath = getFlag('--output'); // where to write the JSON
|
|
50
|
-
const impactTarget = getFlag('--impact'); // entity name to simulate
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### Execution order in the `try` block
|
|
54
|
-
|
|
55
|
-
```ts
|
|
56
|
-
const files = collectFiles(projectDir); // Stage 1
|
|
57
|
-
const parsed = parseFiles(files); // Stage 2
|
|
58
|
-
const graph = buildGraph(parsed); // Stage 3
|
|
59
|
-
const metrics = computeMetrics(graph); // Stage 4
|
|
60
|
-
// Stage 5 — optional
|
|
61
|
-
const impact = impactTarget
|
|
62
|
-
? simulateImpact(metrics, impactTarget, impactDesc)
|
|
63
|
-
: undefined;
|
|
64
|
-
writeOutput(metrics, parsed, outputPath, impact); // Stage 6
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
---
|
|
68
|
-
|
|
69
|
-
## Design Principles
|
|
70
|
-
|
|
71
|
-
| Principle | How it's applied |
|
|
72
|
-
|---|---|
|
|
73
|
-
| **Single responsibility** | Each stage file exports exactly one primary function |
|
|
74
|
-
| **No hidden state** | All data passed explicitly between stages |
|
|
75
|
-
| **Fail loudly** | `process.exit(1)` on unrecoverable errors — no silent swallowing |
|
|
76
|
-
| **Zero runtime dependencies** | Only Node.js built-ins (`fs`, `path`) plus TypeScript |
|
|
77
|
-
| **Plugin language support** | Languages register themselves via the registry (see [language-registry.md](./language-registry.md)) |
|
|
78
|
-
|
|
79
|
-
---
|
|
80
|
-
|
|
81
|
-
## Adding a New Stage
|
|
82
|
-
|
|
83
|
-
If you need to insert a new processing step (e.g. a linting stage), follow this pattern:
|
|
84
|
-
|
|
85
|
-
1. Create `src/stages/my-stage.ts` and export a pure function.
|
|
86
|
-
2. Import and call it in `main.ts` after the stage it depends on.
|
|
87
|
-
3. Thread its output into downstream stages.
|
|
88
|
-
|
|
89
|
-
No registration, no magic — just a function call.
|
package/docs/data-types.md
DELETED
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
# Data Types Reference
|
|
2
|
-
|
|
3
|
-
> **File**: `src/types.ts`
|
|
4
|
-
> All shared TypeScript interfaces used across every stage of the pipeline.
|
|
5
|
-
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
## Data Flow Summary
|
|
9
|
-
|
|
10
|
-
```
|
|
11
|
-
RawEntity ┐
|
|
12
|
-
RawImport ├──► ParsedFile ──► DepNode / DepEdge ──► DepGraph
|
|
13
|
-
┘ │
|
|
14
|
-
▼
|
|
15
|
-
AffectedNode ──► ImpactReport
|
|
16
|
-
│
|
|
17
|
-
▼
|
|
18
|
-
OutputJSON (written to disk)
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
## Stage 1–2 Types (Parser Output)
|
|
24
|
-
|
|
25
|
-
### `RawEntity`
|
|
26
|
-
|
|
27
|
-
A code construct extracted directly from source — before any cross-file resolution.
|
|
28
|
-
|
|
29
|
-
```ts
|
|
30
|
-
interface RawEntity {
|
|
31
|
-
name: string; // e.g. "getUserById"
|
|
32
|
-
type: string; // "function" | "class" | "component" | "hook" | "api" | "interface" | "type"
|
|
33
|
-
line: number; // 1-indexed line number in the source file
|
|
34
|
-
complexity: string; // "low" | "medium" | "high"
|
|
35
|
-
}
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
**Entity types by language:**
|
|
39
|
-
|
|
40
|
-
| Type | Languages | Example |
|
|
41
|
-
|---|---|---|
|
|
42
|
-
| `function` | JS/TS, Python | `function doSomething()` |
|
|
43
|
-
| `class` | JS/TS, Python | `class UserService` |
|
|
44
|
-
| `component` | JS/TS (React) | `const Button = () =>` |
|
|
45
|
-
| `hook` | JS/TS (React) | `const useAuth = () =>` |
|
|
46
|
-
| `interface` | TypeScript | `interface User` |
|
|
47
|
-
| `type` | TypeScript | `type UserId = string` |
|
|
48
|
-
| `api` | JS/TS (Express) | `app.get('/users', ...)` |
|
|
49
|
-
|
|
50
|
-
---
|
|
51
|
-
|
|
52
|
-
### `RawImport`
|
|
53
|
-
|
|
54
|
-
An import statement extracted from a source file.
|
|
55
|
-
|
|
56
|
-
```ts
|
|
57
|
-
interface RawImport {
|
|
58
|
-
source: string; // the import path, e.g. "./userService" or "express"
|
|
59
|
-
names: string[]; // the names imported, e.g. ["getUserById", "createUser"]
|
|
60
|
-
isLocal: boolean; // true if source starts with "." (relative path)
|
|
61
|
-
}
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
**Key distinction**: Only imports where `isLocal === true` become graph edges. External package imports (`"express"`, `"lodash"`, etc.) are ignored during graph construction.
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
### `ParsedFile`
|
|
69
|
-
|
|
70
|
-
The complete output for one source file after parsing.
|
|
71
|
-
|
|
72
|
-
```ts
|
|
73
|
-
interface ParsedFile {
|
|
74
|
-
filePath: string; // absolute or relative path on disk
|
|
75
|
-
lang: string; // parser's lang id, e.g. "js" or "py"
|
|
76
|
-
lines: number; // total line count
|
|
77
|
-
entities: RawEntity[]; // all found code constructs
|
|
78
|
-
imports: RawImport[]; // all import statements
|
|
79
|
-
exports: string[]; // names of exported entities
|
|
80
|
-
}
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
---
|
|
84
|
-
|
|
85
|
-
## Stage 3–4 Types (Graph)
|
|
86
|
-
|
|
87
|
-
### `DepNode`
|
|
88
|
-
|
|
89
|
-
A fully resolved node in the dependency graph. Built from a `RawEntity`, enriched with graph metrics.
|
|
90
|
-
|
|
91
|
-
```ts
|
|
92
|
-
interface DepNode {
|
|
93
|
-
id: string; // composite ID: "entityName__fileBase"
|
|
94
|
-
name: string; // entity name, e.g. "getUserById"
|
|
95
|
-
type: string; // same as RawEntity.type
|
|
96
|
-
file: string; // source file path
|
|
97
|
-
line: number; // source line number
|
|
98
|
-
lang: string; // language id
|
|
99
|
-
complexity: string; // "low" | "medium" | "high"
|
|
100
|
-
inDegree: number; // how many other nodes import this one
|
|
101
|
-
outDegree: number; // how many nodes this one imports
|
|
102
|
-
centralityScore: number; // inDegree * 2 + outDegree
|
|
103
|
-
connections: string[]; // IDs of directly connected nodes (both directions)
|
|
104
|
-
|
|
105
|
-
// Optional — only present for specific entity types:
|
|
106
|
-
extends?: string; // parent class/interface name
|
|
107
|
-
method?: string; // HTTP method for API routes, e.g. "GET"
|
|
108
|
-
route?: string; // URL path for API routes, e.g. "/api/users"
|
|
109
|
-
}
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
**Node ID format**: `getUserById__userService`
|
|
113
|
-
(entity name + `__` + file basename without extension, with non-alphanumeric chars replaced by `_`)
|
|
114
|
-
|
|
115
|
-
---
|
|
116
|
-
|
|
117
|
-
### `DepEdge`
|
|
118
|
-
|
|
119
|
-
A directed link from one node to another, representing an import relationship.
|
|
120
|
-
|
|
121
|
-
```ts
|
|
122
|
-
interface DepEdge {
|
|
123
|
-
from: string; // source node ID (the importer)
|
|
124
|
-
to: string; // target node ID (the imported entity)
|
|
125
|
-
type: string; // currently always "imports"
|
|
126
|
-
description: string; // human-readable, e.g. "UserRouter imports getUserById from userService"
|
|
127
|
-
}
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
**Direction convention**: `from → to` means "`from` depends on `to`".
|
|
131
|
-
|
|
132
|
-
---
|
|
133
|
-
|
|
134
|
-
### `DepGraph`
|
|
135
|
-
|
|
136
|
-
The complete graph.
|
|
137
|
-
|
|
138
|
-
```ts
|
|
139
|
-
interface DepGraph {
|
|
140
|
-
nodes: Map<string, DepNode>; // keyed by node ID
|
|
141
|
-
edges: DepEdge[];
|
|
142
|
-
}
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
The `nodes` Map is converted to an Array when writing to JSON output.
|
|
146
|
-
|
|
147
|
-
---
|
|
148
|
-
|
|
149
|
-
## Stage 5 Types (Impact)
|
|
150
|
-
|
|
151
|
-
### `AffectedNode`
|
|
152
|
-
|
|
153
|
-
A downstream node discovered during BFS traversal from the impact target.
|
|
154
|
-
|
|
155
|
-
```ts
|
|
156
|
-
interface AffectedNode {
|
|
157
|
-
nodeId: string; // graph node ID
|
|
158
|
-
name: string; // entity name
|
|
159
|
-
file: string; // source file path
|
|
160
|
-
depth: number; // BFS depth from target (1 = direct dependent)
|
|
161
|
-
impact: string; // "critical" | "high" | "medium" | "low"
|
|
162
|
-
reason: string; // why this node is affected
|
|
163
|
-
changeRequired: string; // what action needs to be taken
|
|
164
|
-
breakingChange: boolean; // true if depth <= 2
|
|
165
|
-
}
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
---
|
|
169
|
-
|
|
170
|
-
### `ImpactReport`
|
|
171
|
-
|
|
172
|
-
The full simulation result returned by `simulateImpact`.
|
|
173
|
-
|
|
174
|
-
```ts
|
|
175
|
-
interface ImpactReport {
|
|
176
|
-
targetNode: string; // the node ID of the changed entity
|
|
177
|
-
changeDescription: string; // description from --impact flag
|
|
178
|
-
riskScore: number; // 0–100
|
|
179
|
-
riskLevel: string; // "LOW" | "MEDIUM" | "HIGH" | "CRITICAL"
|
|
180
|
-
affectedNodes: AffectedNode[]; // all downstream nodes
|
|
181
|
-
breakingChanges: AffectedNode[]; // subset where breakingChange === true
|
|
182
|
-
testingPlan: string[]; // recommended test actions
|
|
183
|
-
recommendations: string[]; // code review / deployment guidance
|
|
184
|
-
}
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
**Risk score formula**: `C×30 + H×15 + M×7 + L×2 + targetInDegree×3` (capped at 100)
|
|
188
|
-
|
|
189
|
-
---
|
|
190
|
-
|
|
191
|
-
## Output Type
|
|
192
|
-
|
|
193
|
-
### `OutputJSON`
|
|
194
|
-
|
|
195
|
-
The root schema of the generated `depgraph-output.json` file.
|
|
196
|
-
|
|
197
|
-
```ts
|
|
198
|
-
interface OutputJSON {
|
|
199
|
-
meta: {
|
|
200
|
-
version: string; // tool version, e.g. "1.0.0"
|
|
201
|
-
timestamp: string; // ISO 8601, e.g. "2024-01-15T09:30:00.000Z"
|
|
202
|
-
totalFiles: number;
|
|
203
|
-
totalLines: number;
|
|
204
|
-
};
|
|
205
|
-
summary: {
|
|
206
|
-
totalNodes: number;
|
|
207
|
-
totalEdges: number;
|
|
208
|
-
entryPoints: string[]; // node IDs with inDegree=0 & outDegree>0
|
|
209
|
-
leafNodes: string[]; // node IDs with outDegree=0 & inDegree>0
|
|
210
|
-
isolatedNodes: string[]; // node IDs with both degrees = 0 (dead code)
|
|
211
|
-
criticalNodes: string[]; // node IDs with centralityScore > 20
|
|
212
|
-
};
|
|
213
|
-
nodes: DepNode[];
|
|
214
|
-
edges: DepEdge[];
|
|
215
|
-
files: ParsedFile[];
|
|
216
|
-
impact?: ImpactReport; // only present when --impact was used
|
|
217
|
-
}
|
|
218
|
-
```
|
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
# Language Registry
|
|
2
|
-
|
|
3
|
-
> **File**: `src/languages/registry.ts`
|
|
4
|
-
> **Exports**: `LanguageParser` (interface), `registerParser`, `getLanguageParser`
|
|
5
|
-
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
## Overview
|
|
9
|
-
|
|
10
|
-
The language registry is a **simple plugin system**. It solves one problem: the parser stage needs to know *how* to parse a `.py` file differently from a `.ts` file, without hardcoding language logic into the stage itself.
|
|
11
|
-
|
|
12
|
-
Any file that imports `registerParser` and calls it at module load time becomes a **language plugin**. The parser stage just calls `getLanguageParser(ext)` and gets back the right handler — it doesn't know or care which language it's dealing with.
|
|
13
|
-
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
## The `LanguageParser` Interface
|
|
17
|
-
|
|
18
|
-
This is the contract every language plugin must fulfil:
|
|
19
|
-
|
|
20
|
-
```ts
|
|
21
|
-
export interface LanguageParser {
|
|
22
|
-
/** A short unique identifier for the language, e.g. "js", "py". */
|
|
23
|
-
lang: string;
|
|
24
|
-
|
|
25
|
-
/** File extensions this parser handles, e.g. [".js", ".ts", ".tsx"]. */
|
|
26
|
-
extensions: string[];
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Extract named code entities (functions, classes, routes, etc.)
|
|
30
|
-
* from clean source code (single-line comments already stripped).
|
|
31
|
-
*/
|
|
32
|
-
extractEntities: (code: string, filePath: string) => RawEntity[];
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Extract import statements from clean source code.
|
|
36
|
-
*/
|
|
37
|
-
extractImports: (code: string) => RawImport[];
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Extract the names of exported entities from clean source code.
|
|
41
|
-
*/
|
|
42
|
-
extractExports: (code: string) => string[];
|
|
43
|
-
}
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
---
|
|
47
|
-
|
|
48
|
-
## How Registration Works
|
|
49
|
-
|
|
50
|
-
The registry stores parsers in a private module-level array:
|
|
51
|
-
|
|
52
|
-
```ts
|
|
53
|
-
const parsers: LanguageParser[] = [];
|
|
54
|
-
|
|
55
|
-
export function registerParser(parser: LanguageParser): void {
|
|
56
|
-
parsers.push(parser);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function getLanguageParser(ext: string): LanguageParser | null {
|
|
60
|
-
return parsers.find(p => p.extensions.includes(ext)) ?? null;
|
|
61
|
-
}
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
That's the entire registry — an array and two functions.
|
|
65
|
-
|
|
66
|
-
### Lookup
|
|
67
|
-
|
|
68
|
-
`getLanguageParser(".ts")` returns the first registered parser whose `extensions` array includes `".ts"`. If none match, it returns `null` and the file is skipped by the parser stage.
|
|
69
|
-
|
|
70
|
-
---
|
|
71
|
-
|
|
72
|
-
## How Plugins Self-Register
|
|
73
|
-
|
|
74
|
-
Each language file (e.g. `src/languages/javascript.ts`) defines a parser object and calls `registerParser` at the **bottom of the file**, at module scope:
|
|
75
|
-
|
|
76
|
-
```ts
|
|
77
|
-
const JavaScriptParser: LanguageParser = {
|
|
78
|
-
lang: 'js',
|
|
79
|
-
extensions: ['.js', '.jsx', '.mjs', '.cjs', '.ts', '.tsx'],
|
|
80
|
-
extractEntities,
|
|
81
|
-
extractImports,
|
|
82
|
-
extractExports,
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
registerParser(JavaScriptParser);
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
This code runs **once**, when the module is first imported.
|
|
89
|
-
|
|
90
|
-
### Triggering registration
|
|
91
|
-
|
|
92
|
-
Plugins must be imported somewhere in the application for their side-effect (`registerParser(...)`) to run. In `src/main.ts`:
|
|
93
|
-
|
|
94
|
-
```ts
|
|
95
|
-
import './languages/javascript';
|
|
96
|
-
import './languages/python';
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
These are **side-effect imports** (no named export is consumed). They exist purely to execute the registration call.
|
|
100
|
-
|
|
101
|
-
> If you add a new language parser file but forget to import it in `main.ts`, it will never be registered and its extensions will never be recognised.
|
|
102
|
-
|
|
103
|
-
---
|
|
104
|
-
|
|
105
|
-
## Currently Registered Languages
|
|
106
|
-
|
|
107
|
-
| File | `lang` | Extensions |
|
|
108
|
-
|---|---|---|
|
|
109
|
-
| `javascript.ts` | `js` | `.js` `.jsx` `.mjs` `.cjs` `.ts` `.tsx` |
|
|
110
|
-
| `python.ts` | `py` | `.py` |
|
|
111
|
-
|
|
112
|
-
The following language files exist as stubs (imported but with minimal or no implementation yet):
|
|
113
|
-
|
|
114
|
-
- `java.ts`
|
|
115
|
-
- `csharp.ts`
|
|
116
|
-
- `go.ts`
|
|
117
|
-
- `kotlin.ts`
|
|
118
|
-
- `php.ts`
|
|
119
|
-
- `ruby.ts`
|
|
120
|
-
- `swift.ts`
|
|
121
|
-
|
|
122
|
-
---
|
|
123
|
-
|
|
124
|
-
## How to Add a New Language
|
|
125
|
-
|
|
126
|
-
Follow these steps to add full support for a new language.
|
|
127
|
-
|
|
128
|
-
### 1. Create the parser file
|
|
129
|
-
|
|
130
|
-
Create `src/languages/mylang.ts`:
|
|
131
|
-
|
|
132
|
-
```ts
|
|
133
|
-
import { RawEntity, RawImport } from '../types';
|
|
134
|
-
import { LanguageParser, registerParser } from './registry';
|
|
135
|
-
|
|
136
|
-
function extractEntities(code: string, filePath: string): RawEntity[] {
|
|
137
|
-
const entities: RawEntity[] = [];
|
|
138
|
-
// Use regex or any other technique to find functions, classes, etc.
|
|
139
|
-
// Populate and return the entities array.
|
|
140
|
-
return entities;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function extractImports(code: string): RawImport[] {
|
|
144
|
-
const imports: RawImport[] = [];
|
|
145
|
-
// Parse import/require/use statements.
|
|
146
|
-
// Set isLocal: true for relative imports ('./something').
|
|
147
|
-
return imports;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function extractExports(code: string): string[] {
|
|
151
|
-
// Return an array of exported entity names.
|
|
152
|
-
return [];
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const MyLangParser: LanguageParser = {
|
|
156
|
-
lang: 'mylang',
|
|
157
|
-
extensions: ['.ml', '.mls'],
|
|
158
|
-
extractEntities,
|
|
159
|
-
extractImports,
|
|
160
|
-
extractExports,
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
registerParser(MyLangParser);
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
### 2. Import it in `main.ts`
|
|
167
|
-
|
|
168
|
-
```ts
|
|
169
|
-
import './languages/mylang';
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
Add this **before** the pipeline starts (with the other language imports at the top of the file).
|
|
173
|
-
|
|
174
|
-
### 3. Ensure the extension is in `SUPPORTED_EXTS`
|
|
175
|
-
|
|
176
|
-
In `src/constants.ts`, add your extension to the set:
|
|
177
|
-
|
|
178
|
-
```ts
|
|
179
|
-
export const SUPPORTED_EXTS = new Set([
|
|
180
|
-
// ... existing extensions
|
|
181
|
-
'.ml', '.mls',
|
|
182
|
-
]);
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
Without this, the collector will never hand files with that extension to the parser in the first place.
|
|
186
|
-
|
|
187
|
-
### 4. Test it
|
|
188
|
-
|
|
189
|
-
```bash
|
|
190
|
-
node depgraph.js ./path/to/mylang-project --verbose
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
Look for your files appearing in the verbose output and entities showing up in the summary.
|
|
194
|
-
|
|
195
|
-
---
|
|
196
|
-
|
|
197
|
-
## Rules for Writing a Good Parser
|
|
198
|
-
|
|
199
|
-
| Rule | Why |
|
|
200
|
-
|---|---|
|
|
201
|
-
| Only use `code` (the cleaned version) — never read files | The stage already strips comments and passes clean code |
|
|
202
|
-
| Reset `regex.lastIndex = 0` before each `exec` loop | Global regexes maintain state; forgetting this causes skipped matches |
|
|
203
|
-
| Guard against duplicates (`entities.some(e => e.name === name)`) | The graph stage skips duplicates, but clean data is better upstream |
|
|
204
|
-
| Mark relative imports with `isLocal: true` | Only local imports become graph edges; external packages are ignored |
|
|
205
|
-
| Keep extraction logic regex-based or simple string parsing | The project has zero AST dependencies on purpose — keep it that way |
|