@codeflow-map/core 0.1.0 → 0.1.2
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 +118 -69
- package/package.json +8 -7
package/README.md
CHANGED
|
@@ -1,157 +1,206 @@
|
|
|
1
|
-
# @
|
|
1
|
+
# @codeflow-map/core
|
|
2
2
|
|
|
3
|
-
Language-agnostic call-graph
|
|
3
|
+
Language-agnostic call-graph engine powered by [Tree-sitter](https://tree-sitter.github.io/tree-sitter/). Feed it source files, get back a structured graph of every function, every call relationship, and every execution flow — deterministic, fully local, no LLM, no cloud.
|
|
4
4
|
|
|
5
|
-
Used by the [
|
|
5
|
+
Used by the [CallSight VS Code extension](https://github.com/devricky-codes/callsight-vscode-extension) to render interactive call-flow diagrams for any codebase.
|
|
6
|
+
|
|
7
|
+
---
|
|
6
8
|
|
|
7
9
|
## Supported Languages
|
|
8
10
|
|
|
9
|
-
| Language | Functions | Calls | Components/Hooks |
|
|
10
|
-
|
|
11
|
-
| TypeScript | ✅ | ✅
|
|
12
|
-
| JavaScript | ✅ | ✅
|
|
13
|
-
| TSX | ✅ | ✅
|
|
14
|
-
| JSX | ✅ | ✅
|
|
15
|
-
| Python | ✅ | ✅
|
|
16
|
-
| Go | ✅ | ✅
|
|
11
|
+
| Language | Functions | Calls | Components / Hooks |
|
|
12
|
+
|------------|:---------:|:-----:|:-----------------:|
|
|
13
|
+
| TypeScript | ✅ | ✅ | ✅ |
|
|
14
|
+
| JavaScript | ✅ | ✅ | ✅ |
|
|
15
|
+
| TSX | ✅ | ✅ | ✅ |
|
|
16
|
+
| JSX | ✅ | ✅ | ✅ |
|
|
17
|
+
| Python | ✅ | ✅ | - |
|
|
18
|
+
| Go | ✅ | ✅ | - |
|
|
19
|
+
|
|
20
|
+
---
|
|
17
21
|
|
|
18
22
|
## Installation
|
|
19
23
|
|
|
20
24
|
```bash
|
|
21
|
-
npm install @
|
|
25
|
+
npm install @codeflow-map/core
|
|
22
26
|
# or
|
|
23
|
-
pnpm add @
|
|
27
|
+
pnpm add @codeflow-map/core
|
|
24
28
|
```
|
|
25
29
|
|
|
26
|
-
> **
|
|
30
|
+
> **Runtime requirement:** `web-tree-sitter` parses source files using WebAssembly grammar files.
|
|
31
|
+
> You must supply a `wasmDirectory` that contains one `.wasm` file per language you want to analyse
|
|
32
|
+
> (e.g. `tree-sitter-typescript.wasm`, `tree-sitter-python.wasm`).
|
|
33
|
+
> Pre-built grammars are available from the official
|
|
34
|
+
> [tree-sitter grammar repositories](https://github.com/tree-sitter).
|
|
35
|
+
|
|
36
|
+
---
|
|
27
37
|
|
|
28
|
-
##
|
|
38
|
+
## Quick Start
|
|
29
39
|
|
|
30
|
-
###
|
|
40
|
+
### Analyse an entire directory
|
|
31
41
|
|
|
32
42
|
```typescript
|
|
33
|
-
import { analyzeDirectory } from '@
|
|
43
|
+
import { analyzeDirectory } from '@codeflow-map/core';
|
|
34
44
|
|
|
35
45
|
const graph = await analyzeDirectory({
|
|
36
|
-
rootPath:
|
|
46
|
+
rootPath: '/absolute/path/to/project',
|
|
37
47
|
wasmDirectory: '/path/to/wasm/grammars',
|
|
38
|
-
include:
|
|
39
|
-
exclude:
|
|
48
|
+
include: ['**/*.ts', '**/*.tsx'],
|
|
49
|
+
exclude: ['**/node_modules/**', '**/dist/**'],
|
|
40
50
|
});
|
|
41
51
|
|
|
42
52
|
console.log(`Scanned ${graph.scannedFiles} files in ${graph.durationMs}ms`);
|
|
43
|
-
console.log(
|
|
44
|
-
console.log(`Partitioned into ${graph.flows.length} flows`);
|
|
53
|
+
console.log(`${graph.nodes.length} functions · ${graph.edges.length} call edges · ${graph.flows.length} flows`);
|
|
45
54
|
```
|
|
46
55
|
|
|
47
|
-
###
|
|
56
|
+
### Analyse a single file
|
|
48
57
|
|
|
49
58
|
```typescript
|
|
50
|
-
import { parseFile } from '@
|
|
59
|
+
import { parseFile } from '@codeflow-map/core';
|
|
51
60
|
|
|
52
61
|
const { functions, calls } = await parseFile(
|
|
53
|
-
'src/index.ts',
|
|
54
|
-
'/absolute/src/index.ts', // absolute path
|
|
62
|
+
'src/index.ts', // relative path — used as node ID prefix
|
|
63
|
+
'/absolute/src/index.ts', // absolute path — used to read the file
|
|
55
64
|
'/path/to/wasm', // wasmDirectory
|
|
56
|
-
'typescript'
|
|
65
|
+
'typescript', // SupportedLanguage
|
|
57
66
|
);
|
|
58
67
|
```
|
|
59
68
|
|
|
60
|
-
### Build a call graph
|
|
69
|
+
### Build a call graph from raw parse results
|
|
61
70
|
|
|
62
71
|
```typescript
|
|
63
|
-
import { buildCallGraph, detectEntryPoints, partitionFlows } from '@
|
|
72
|
+
import { buildCallGraph, detectEntryPoints, partitionFlows } from '@codeflow-map/core';
|
|
64
73
|
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
const { flows, orphans } = partitionFlows(nodesWithEntries, edges);
|
|
74
|
+
const edges = buildCallGraph(functions, calls);
|
|
75
|
+
const nodesWithEntries = detectEntryPoints(functions, edges);
|
|
76
|
+
const { flows, orphans } = partitionFlows(nodesWithEntries, edges);
|
|
69
77
|
```
|
|
70
78
|
|
|
79
|
+
---
|
|
80
|
+
|
|
71
81
|
## Data Model
|
|
72
82
|
|
|
73
83
|
### `FunctionNode`
|
|
74
84
|
|
|
85
|
+
Represents a single function, method, component, or hook extracted from source.
|
|
86
|
+
|
|
75
87
|
```typescript
|
|
76
88
|
interface FunctionNode {
|
|
77
|
-
id:
|
|
78
|
-
name:
|
|
79
|
-
filePath:
|
|
80
|
-
startLine:
|
|
81
|
-
endLine:
|
|
82
|
-
params:
|
|
83
|
-
returnType:
|
|
84
|
-
isAsync:
|
|
85
|
-
isExported:
|
|
86
|
-
isEntryPoint:
|
|
87
|
-
language:
|
|
88
|
-
kind?:
|
|
89
|
-
parentFunctionId?: string;
|
|
89
|
+
id: string; // "relative/path.ts::functionName::startLine"
|
|
90
|
+
name: string; // human-readable, e.g. "UserService.getUser"
|
|
91
|
+
filePath: string; // relative to workspace root
|
|
92
|
+
startLine: number; // 0-indexed
|
|
93
|
+
endLine: number;
|
|
94
|
+
params: Parameter[]; // name + type (type is null for untyped languages)
|
|
95
|
+
returnType: string | null;
|
|
96
|
+
isAsync: boolean;
|
|
97
|
+
isExported: boolean;
|
|
98
|
+
isEntryPoint: boolean; // true when in-degree = 0 and out-degree > 0
|
|
99
|
+
language: SupportedLanguage;
|
|
100
|
+
kind?: 'function' | 'component' | 'hook' | 'method' | 'class' | 'iife';
|
|
101
|
+
parentFunctionId?: string; // set for inner / nested functions
|
|
90
102
|
}
|
|
91
103
|
```
|
|
92
104
|
|
|
93
105
|
### `CallEdge`
|
|
94
106
|
|
|
107
|
+
Represents a directed call relationship between two functions.
|
|
108
|
+
|
|
95
109
|
```typescript
|
|
96
110
|
interface CallEdge {
|
|
97
|
-
from:
|
|
98
|
-
to:
|
|
99
|
-
line:
|
|
111
|
+
from: string; // caller FunctionNode.id
|
|
112
|
+
to: string; // callee FunctionNode.id
|
|
113
|
+
line: number; // source line where the call occurs
|
|
100
114
|
callType?: 'direct' | 'ref' | 'concurrent' | 'goroutine';
|
|
101
115
|
}
|
|
102
116
|
```
|
|
103
117
|
|
|
118
|
+
`callType` distinguishes how the call happens:
|
|
119
|
+
- `direct` — standard synchronous call
|
|
120
|
+
- `ref` — function passed as a reference (e.g. event handler, callback)
|
|
121
|
+
- `concurrent` — call inside `Promise.all`, `asyncio.gather`, etc.
|
|
122
|
+
- `goroutine` — Go `go fn()` launch
|
|
123
|
+
|
|
104
124
|
### `Flow`
|
|
105
125
|
|
|
126
|
+
A connected subgraph reachable from a single entry point.
|
|
127
|
+
|
|
106
128
|
```typescript
|
|
107
129
|
interface Flow {
|
|
108
|
-
id:
|
|
109
|
-
entryPoint:
|
|
110
|
-
nodeIds:
|
|
130
|
+
id: string; // derived from the entry point's FunctionNode.id
|
|
131
|
+
entryPoint: string; // FunctionNode.id of the root function
|
|
132
|
+
nodeIds: string[]; // all function IDs reachable from this entry point
|
|
111
133
|
}
|
|
112
134
|
```
|
|
113
135
|
|
|
114
136
|
### `Graph`
|
|
115
137
|
|
|
138
|
+
The complete analysis result returned by `analyzeDirectory`.
|
|
139
|
+
|
|
116
140
|
```typescript
|
|
117
141
|
interface Graph {
|
|
118
|
-
nodes:
|
|
119
|
-
edges:
|
|
120
|
-
flows:
|
|
121
|
-
orphans:
|
|
142
|
+
nodes: FunctionNode[];
|
|
143
|
+
edges: CallEdge[];
|
|
144
|
+
flows: Flow[];
|
|
145
|
+
orphans: string[]; // FunctionNode IDs unreachable from any entry point
|
|
122
146
|
scannedFiles: number;
|
|
123
|
-
durationMs:
|
|
147
|
+
durationMs: number;
|
|
124
148
|
}
|
|
125
149
|
```
|
|
126
150
|
|
|
151
|
+
---
|
|
152
|
+
|
|
127
153
|
## API Reference
|
|
128
154
|
|
|
129
155
|
### `analyzeDirectory(options): Promise<Graph>`
|
|
130
156
|
|
|
131
|
-
Scans a directory, parses all matched source files, and
|
|
157
|
+
Scans a directory, parses all matched source files, builds the call graph, detects entry points, and partitions execution flows in a single call.
|
|
158
|
+
|
|
159
|
+
| Option | Type | Required | Description |
|
|
160
|
+
|-----------------|------------|:--------:|--------------------------------------------------|
|
|
161
|
+
| `rootPath` | `string` | ✅ | Absolute path to the project root |
|
|
162
|
+
| `wasmDirectory` | `string` | ✅ | Directory containing `.wasm` Tree-sitter grammars |
|
|
163
|
+
| `include` | `string[]` | ✅ | Glob patterns for files to include |
|
|
164
|
+
| `exclude` | `string[]` | | Glob patterns for files to exclude |
|
|
132
165
|
|
|
133
|
-
|
|
134
|
-
|----------------|------------|--------------------------------------------------|
|
|
135
|
-
| `rootPath` | `string` | Absolute path to the project root |
|
|
136
|
-
| `wasmDirectory`| `string` | Directory containing `.wasm` grammar files |
|
|
137
|
-
| `include` | `string[]` | Glob patterns for files to include |
|
|
138
|
-
| `exclude` | `string[]` | Glob patterns for files to exclude |
|
|
166
|
+
---
|
|
139
167
|
|
|
140
|
-
### `parseFile(relativePath, absolutePath, wasmDirectory, language): Promise<{functions, calls}>`
|
|
168
|
+
### `parseFile(relativePath, absolutePath, wasmDirectory, language): Promise<{ functions, calls }>`
|
|
141
169
|
|
|
142
|
-
Parses a single source file
|
|
170
|
+
Parses a single source file and returns the raw extracted functions and call sites. Use this when you want fine-grained control over the analysis pipeline.
|
|
171
|
+
|
|
172
|
+
---
|
|
143
173
|
|
|
144
174
|
### `buildCallGraph(nodes, rawCalls): CallEdge[]`
|
|
145
175
|
|
|
146
|
-
Resolves raw call references into typed `CallEdge` objects.
|
|
176
|
+
Resolves raw call references into typed `CallEdge` objects by matching callee names against the indexed function nodes.
|
|
177
|
+
|
|
178
|
+
---
|
|
147
179
|
|
|
148
180
|
### `detectEntryPoints(nodes, edges): FunctionNode[]`
|
|
149
181
|
|
|
150
|
-
Marks
|
|
182
|
+
Marks nodes as entry points using graph topology — a node is an entry point when nothing calls it (in-degree = 0) but it calls at least one other function (out-degree > 0). No language-specific heuristics, no name matching.
|
|
183
|
+
|
|
184
|
+
---
|
|
151
185
|
|
|
152
186
|
### `partitionFlows(nodes, edges): { flows: Flow[], orphans: string[] }`
|
|
153
187
|
|
|
154
|
-
|
|
188
|
+
Runs BFS from each entry point to group the call graph into independent execution flows. Functions unreachable from any entry point are collected separately as `orphans` — useful for dead code detection.
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## How Entry Points Are Detected
|
|
193
|
+
|
|
194
|
+
Entry point detection is purely graph-based — no hardcoded names, no language-specific rules. A function is an entry point if and only if:
|
|
195
|
+
|
|
196
|
+
1. No other function in the graph calls it (in-degree = 0)
|
|
197
|
+
2. It calls at least one other function (out-degree > 0)
|
|
198
|
+
|
|
199
|
+
This means `main()` in Go, route handlers in Flask, React root components, and CLI entry functions are all detected automatically and consistently across all supported languages.
|
|
200
|
+
|
|
201
|
+
> **Single-file caveat:** When analysing a single file in isolation, functions that only call external code will appear as orphans because their callees are not in scope. Run `analyzeDirectory` across the full workspace for accurate entry point detection.
|
|
202
|
+
|
|
203
|
+
---
|
|
155
204
|
|
|
156
205
|
## License
|
|
157
206
|
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codeflow-map/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Language-agnostic call-graph analysis engine powered by Tree-sitter. Parses TypeScript, JavaScript, TSX, JSX, Python, and Go source files into a structured call graph with flows.",
|
|
5
5
|
"keywords": [
|
|
6
|
-
"
|
|
6
|
+
"callsight",
|
|
7
7
|
"call-graph",
|
|
8
8
|
"tree-sitter",
|
|
9
9
|
"static-analysis",
|
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
"author": "devricky-codes",
|
|
19
19
|
"repository": {
|
|
20
20
|
"type": "git",
|
|
21
|
-
"url": "https://github.com/devricky-codes/
|
|
21
|
+
"url": "https://github.com/devricky-codes/callsight-vscode-extension.git",
|
|
22
22
|
"directory": "packages/core"
|
|
23
23
|
},
|
|
24
|
-
"homepage": "https://github.com/devricky-codes/
|
|
24
|
+
"homepage": "https://github.com/devricky-codes/callsight-vscode-extension#readme",
|
|
25
25
|
"main": "dist/index.js",
|
|
26
26
|
"types": "dist/index.d.ts",
|
|
27
27
|
"files": [
|
|
@@ -35,14 +35,15 @@
|
|
|
35
35
|
"access": "public"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
|
-
"build": "
|
|
39
|
-
"watch": "
|
|
38
|
+
"build": "tsc",
|
|
39
|
+
"watch": "tsc --watch"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"web-tree-sitter": "^0.24.4",
|
|
43
43
|
"minimatch": "^9.0.3"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@types/node": "^20.10.6"
|
|
46
|
+
"@types/node": "^20.10.6",
|
|
47
|
+
"typescript": "^5.3.3"
|
|
47
48
|
}
|
|
48
49
|
}
|