@kodus/kodus-graph 0.2.8 → 0.2.10
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/LICENSE +21 -0
- package/README.md +252 -0
- package/dist/analysis/blast-radius.d.ts +2 -0
- package/dist/analysis/blast-radius.js +55 -0
- package/dist/analysis/communities.d.ts +28 -0
- package/dist/analysis/communities.js +100 -0
- package/dist/analysis/context-builder.d.ts +34 -0
- package/dist/analysis/context-builder.js +92 -0
- package/dist/analysis/diff.d.ts +41 -0
- package/dist/analysis/diff.js +155 -0
- package/dist/analysis/enrich.d.ts +5 -0
- package/dist/analysis/enrich.js +126 -0
- package/dist/analysis/flows.d.ts +27 -0
- package/dist/analysis/flows.js +86 -0
- package/dist/analysis/inheritance.d.ts +3 -0
- package/dist/analysis/inheritance.js +31 -0
- package/dist/analysis/prompt-formatter.d.ts +2 -0
- package/dist/analysis/prompt-formatter.js +173 -0
- package/dist/analysis/risk-score.d.ts +4 -0
- package/dist/analysis/risk-score.js +51 -0
- package/dist/analysis/search.d.ts +11 -0
- package/dist/analysis/search.js +64 -0
- package/dist/analysis/test-gaps.d.ts +2 -0
- package/dist/analysis/test-gaps.js +14 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +210 -0
- package/dist/commands/analyze.d.ts +9 -0
- package/dist/commands/analyze.js +116 -0
- package/dist/commands/communities.d.ts +8 -0
- package/dist/commands/communities.js +9 -0
- package/dist/commands/context.d.ts +12 -0
- package/dist/commands/context.js +130 -0
- package/dist/commands/diff.d.ts +9 -0
- package/dist/commands/diff.js +89 -0
- package/dist/commands/flows.d.ts +8 -0
- package/dist/commands/flows.js +9 -0
- package/dist/commands/parse.d.ts +11 -0
- package/dist/commands/parse.js +101 -0
- package/dist/commands/search.d.ts +12 -0
- package/dist/commands/search.js +27 -0
- package/dist/commands/update.d.ts +7 -0
- package/dist/commands/update.js +154 -0
- package/dist/graph/builder.d.ts +6 -0
- package/dist/graph/builder.js +248 -0
- package/dist/graph/edges.d.ts +23 -0
- package/dist/graph/edges.js +159 -0
- package/dist/graph/json-writer.d.ts +9 -0
- package/dist/graph/json-writer.js +38 -0
- package/dist/graph/loader.d.ts +13 -0
- package/dist/graph/loader.js +101 -0
- package/dist/graph/merger.d.ts +7 -0
- package/dist/graph/merger.js +18 -0
- package/dist/graph/types.d.ts +252 -0
- package/dist/graph/types.js +1 -0
- package/dist/parser/batch.d.ts +5 -0
- package/dist/parser/batch.js +93 -0
- package/dist/parser/discovery.d.ts +7 -0
- package/dist/parser/discovery.js +61 -0
- package/dist/parser/extractor.d.ts +4 -0
- package/dist/parser/extractor.js +33 -0
- package/dist/parser/extractors/generic.d.ts +8 -0
- package/dist/parser/extractors/generic.js +471 -0
- package/dist/parser/extractors/python.d.ts +8 -0
- package/dist/parser/extractors/python.js +133 -0
- package/dist/parser/extractors/ruby.d.ts +8 -0
- package/dist/parser/extractors/ruby.js +153 -0
- package/dist/parser/extractors/typescript.d.ts +10 -0
- package/dist/parser/extractors/typescript.js +365 -0
- package/dist/parser/languages.d.ts +32 -0
- package/dist/parser/languages.js +304 -0
- package/dist/resolver/call-resolver.d.ts +36 -0
- package/dist/resolver/call-resolver.js +178 -0
- package/dist/resolver/external-detector.d.ts +11 -0
- package/dist/resolver/external-detector.js +820 -0
- package/dist/resolver/fs-cache.d.ts +8 -0
- package/dist/resolver/fs-cache.js +36 -0
- package/dist/resolver/import-map.d.ts +12 -0
- package/dist/resolver/import-map.js +21 -0
- package/dist/resolver/import-resolver.d.ts +19 -0
- package/dist/resolver/import-resolver.js +310 -0
- package/dist/resolver/languages/csharp.d.ts +3 -0
- package/dist/resolver/languages/csharp.js +94 -0
- package/dist/resolver/languages/go.d.ts +3 -0
- package/dist/resolver/languages/go.js +197 -0
- package/dist/resolver/languages/java.d.ts +1 -0
- package/dist/resolver/languages/java.js +193 -0
- package/dist/resolver/languages/php.d.ts +3 -0
- package/dist/resolver/languages/php.js +75 -0
- package/dist/resolver/languages/python.d.ts +11 -0
- package/dist/resolver/languages/python.js +127 -0
- package/dist/resolver/languages/ruby.d.ts +24 -0
- package/dist/resolver/languages/ruby.js +110 -0
- package/dist/resolver/languages/rust.d.ts +1 -0
- package/dist/resolver/languages/rust.js +197 -0
- package/dist/resolver/languages/typescript.d.ts +35 -0
- package/dist/resolver/languages/typescript.js +416 -0
- package/dist/resolver/re-export-resolver.d.ts +24 -0
- package/dist/resolver/re-export-resolver.js +57 -0
- package/dist/resolver/symbol-table.d.ts +17 -0
- package/dist/resolver/symbol-table.js +60 -0
- package/dist/shared/extract-calls.d.ts +26 -0
- package/dist/shared/extract-calls.js +57 -0
- package/dist/shared/file-hash.d.ts +3 -0
- package/dist/shared/file-hash.js +10 -0
- package/dist/shared/filters.d.ts +3 -0
- package/dist/shared/filters.js +240 -0
- package/dist/shared/logger.d.ts +6 -0
- package/dist/shared/logger.js +17 -0
- package/dist/shared/qualified-name.d.ts +1 -0
- package/dist/shared/qualified-name.js +9 -0
- package/dist/shared/safe-path.d.ts +6 -0
- package/dist/shared/safe-path.js +29 -0
- package/dist/shared/schemas.d.ts +43 -0
- package/dist/shared/schemas.js +30 -0
- package/dist/shared/temp.d.ts +11 -0
- package/{src/shared/temp.ts → dist/shared/temp.js} +4 -5
- package/package.json +20 -6
- package/src/analysis/blast-radius.ts +0 -54
- package/src/analysis/communities.ts +0 -135
- package/src/analysis/context-builder.ts +0 -130
- package/src/analysis/diff.ts +0 -169
- package/src/analysis/enrich.ts +0 -110
- package/src/analysis/flows.ts +0 -112
- package/src/analysis/inheritance.ts +0 -34
- package/src/analysis/prompt-formatter.ts +0 -175
- package/src/analysis/risk-score.ts +0 -62
- package/src/analysis/search.ts +0 -76
- package/src/analysis/test-gaps.ts +0 -21
- package/src/cli.ts +0 -210
- package/src/commands/analyze.ts +0 -128
- package/src/commands/communities.ts +0 -19
- package/src/commands/context.ts +0 -182
- package/src/commands/diff.ts +0 -96
- package/src/commands/flows.ts +0 -19
- package/src/commands/parse.ts +0 -124
- package/src/commands/search.ts +0 -41
- package/src/commands/update.ts +0 -166
- package/src/graph/builder.ts +0 -209
- package/src/graph/edges.ts +0 -101
- package/src/graph/json-writer.ts +0 -43
- package/src/graph/loader.ts +0 -113
- package/src/graph/merger.ts +0 -25
- package/src/graph/types.ts +0 -283
- package/src/parser/batch.ts +0 -82
- package/src/parser/discovery.ts +0 -75
- package/src/parser/extractor.ts +0 -37
- package/src/parser/extractors/generic.ts +0 -132
- package/src/parser/extractors/python.ts +0 -133
- package/src/parser/extractors/ruby.ts +0 -147
- package/src/parser/extractors/typescript.ts +0 -350
- package/src/parser/languages.ts +0 -122
- package/src/resolver/call-resolver.ts +0 -244
- package/src/resolver/import-map.ts +0 -27
- package/src/resolver/import-resolver.ts +0 -72
- package/src/resolver/languages/csharp.ts +0 -7
- package/src/resolver/languages/go.ts +0 -7
- package/src/resolver/languages/java.ts +0 -7
- package/src/resolver/languages/php.ts +0 -7
- package/src/resolver/languages/python.ts +0 -35
- package/src/resolver/languages/ruby.ts +0 -21
- package/src/resolver/languages/rust.ts +0 -7
- package/src/resolver/languages/typescript.ts +0 -168
- package/src/resolver/re-export-resolver.ts +0 -66
- package/src/resolver/symbol-table.ts +0 -67
- package/src/shared/extract-calls.ts +0 -75
- package/src/shared/file-hash.ts +0 -12
- package/src/shared/filters.ts +0 -243
- package/src/shared/logger.ts +0 -17
- package/src/shared/qualified-name.ts +0 -5
- package/src/shared/safe-path.ts +0 -31
- package/src/shared/schemas.ts +0 -32
package/src/graph/types.ts
DELETED
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
// ── Node kinds (aligned with Postgres ast_nodes.kind) ──
|
|
2
|
-
export type NodeKind = 'Function' | 'Method' | 'Constructor' | 'Class' | 'Interface' | 'Enum' | 'Test';
|
|
3
|
-
|
|
4
|
-
// ── Edge kinds (aligned with Postgres ast_edges.kind) ──
|
|
5
|
-
export type EdgeKind = 'CALLS' | 'IMPORTS' | 'INHERITS' | 'IMPLEMENTS' | 'TESTED_BY' | 'CONTAINS';
|
|
6
|
-
|
|
7
|
-
// ── Graph node (matches ast_nodes table) ──
|
|
8
|
-
export interface GraphNode {
|
|
9
|
-
kind: NodeKind;
|
|
10
|
-
name: string;
|
|
11
|
-
qualified_name: string;
|
|
12
|
-
file_path: string;
|
|
13
|
-
line_start: number;
|
|
14
|
-
line_end: number;
|
|
15
|
-
language: string;
|
|
16
|
-
parent_name?: string;
|
|
17
|
-
params?: string;
|
|
18
|
-
return_type?: string;
|
|
19
|
-
modifiers?: string;
|
|
20
|
-
is_test: boolean;
|
|
21
|
-
file_hash: string;
|
|
22
|
-
content_hash?: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// ── Graph edge (matches ast_edges table) ──
|
|
26
|
-
export interface GraphEdge {
|
|
27
|
-
kind: EdgeKind;
|
|
28
|
-
source_qualified: string;
|
|
29
|
-
target_qualified: string;
|
|
30
|
-
file_path: string;
|
|
31
|
-
line: number;
|
|
32
|
-
confidence?: number; // 0.0-1.0, only for CALLS
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// ── Full graph data ──
|
|
36
|
-
export interface GraphData {
|
|
37
|
-
nodes: GraphNode[];
|
|
38
|
-
edges: GraphEdge[];
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// ── Parse command output ──
|
|
42
|
-
export interface ParseMetadata {
|
|
43
|
-
repo_dir: string;
|
|
44
|
-
files_parsed: number;
|
|
45
|
-
total_nodes: number;
|
|
46
|
-
total_edges: number;
|
|
47
|
-
duration_ms: number;
|
|
48
|
-
parse_errors: number;
|
|
49
|
-
extract_errors: number;
|
|
50
|
-
files_unchanged?: number;
|
|
51
|
-
incremental?: boolean;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export interface ParseOutput {
|
|
55
|
-
metadata: ParseMetadata;
|
|
56
|
-
nodes: GraphNode[];
|
|
57
|
-
edges: GraphEdge[];
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// ── Analyze command output ──
|
|
61
|
-
export interface BlastRadiusResult {
|
|
62
|
-
total_functions: number;
|
|
63
|
-
total_files: number;
|
|
64
|
-
by_depth: Record<string, string[]>;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export interface RiskFactor {
|
|
68
|
-
weight: number;
|
|
69
|
-
value: number;
|
|
70
|
-
detail: string;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export interface RiskScoreResult {
|
|
74
|
-
level: 'LOW' | 'MEDIUM' | 'HIGH';
|
|
75
|
-
score: number;
|
|
76
|
-
factors: {
|
|
77
|
-
blast_radius: RiskFactor;
|
|
78
|
-
test_gaps: RiskFactor;
|
|
79
|
-
complexity: RiskFactor;
|
|
80
|
-
inheritance: RiskFactor;
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export interface TestGap {
|
|
85
|
-
function: string;
|
|
86
|
-
file_path: string;
|
|
87
|
-
line_start: number;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export interface AnalysisOutput {
|
|
91
|
-
blast_radius: BlastRadiusResult;
|
|
92
|
-
risk_score: RiskScoreResult;
|
|
93
|
-
test_gaps: TestGap[];
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// ── Context command output ──
|
|
97
|
-
export interface ContextMetadata {
|
|
98
|
-
changed_functions: number;
|
|
99
|
-
caller_count: number;
|
|
100
|
-
callee_count: number;
|
|
101
|
-
untested_count: number;
|
|
102
|
-
blast_radius: { functions: number; files: number };
|
|
103
|
-
risk_level: 'LOW' | 'MEDIUM' | 'HIGH';
|
|
104
|
-
risk_score: number;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export interface ContextOutput {
|
|
108
|
-
text: string;
|
|
109
|
-
metadata: ContextMetadata;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// ── Main graph JSON (input --graph, from Postgres) ──
|
|
113
|
-
export interface MainGraphInput {
|
|
114
|
-
repo_id: string;
|
|
115
|
-
sha: string;
|
|
116
|
-
nodes: GraphNode[];
|
|
117
|
-
edges: GraphEdge[];
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// ── Context V2 types ──
|
|
121
|
-
export interface CallerRef {
|
|
122
|
-
qualified_name: string;
|
|
123
|
-
name: string;
|
|
124
|
-
file_path: string;
|
|
125
|
-
line: number;
|
|
126
|
-
confidence: number;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export interface CalleeRef {
|
|
130
|
-
qualified_name: string;
|
|
131
|
-
name: string;
|
|
132
|
-
file_path: string;
|
|
133
|
-
signature: string;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
export interface EnrichedFunction {
|
|
137
|
-
qualified_name: string;
|
|
138
|
-
name: string;
|
|
139
|
-
kind: NodeKind;
|
|
140
|
-
signature: string;
|
|
141
|
-
file_path: string;
|
|
142
|
-
line_start: number;
|
|
143
|
-
line_end: number;
|
|
144
|
-
callers: CallerRef[];
|
|
145
|
-
callees: CalleeRef[];
|
|
146
|
-
has_test_coverage: boolean;
|
|
147
|
-
diff_changes: string[];
|
|
148
|
-
is_new: boolean;
|
|
149
|
-
in_flows: string[];
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
export interface AffectedFlow {
|
|
153
|
-
entry_point: string;
|
|
154
|
-
type: 'test' | 'http';
|
|
155
|
-
touches_changed: string[];
|
|
156
|
-
depth: number;
|
|
157
|
-
path: string[];
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
export interface InheritanceEntry {
|
|
161
|
-
qualified_name: string;
|
|
162
|
-
file_path: string;
|
|
163
|
-
extends?: string;
|
|
164
|
-
implements: string[];
|
|
165
|
-
children: string[];
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
export interface ContextAnalysisMetadata {
|
|
169
|
-
changed_functions_count: number;
|
|
170
|
-
total_callers: number;
|
|
171
|
-
total_callees: number;
|
|
172
|
-
untested_count: number;
|
|
173
|
-
affected_flows_count: number;
|
|
174
|
-
duration_ms: number;
|
|
175
|
-
min_confidence: number;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// ── Internal types used during parsing pipeline ──
|
|
179
|
-
export interface RawFunction {
|
|
180
|
-
name: string;
|
|
181
|
-
file: string;
|
|
182
|
-
line_start: number;
|
|
183
|
-
line_end: number;
|
|
184
|
-
params: string;
|
|
185
|
-
returnType: string;
|
|
186
|
-
kind: 'Function' | 'Method' | 'Constructor';
|
|
187
|
-
className: string;
|
|
188
|
-
qualified: string;
|
|
189
|
-
content_hash?: string;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
export interface RawClass {
|
|
193
|
-
name: string;
|
|
194
|
-
file: string;
|
|
195
|
-
line_start: number;
|
|
196
|
-
line_end: number;
|
|
197
|
-
extends: string;
|
|
198
|
-
implements: string;
|
|
199
|
-
qualified: string;
|
|
200
|
-
content_hash?: string;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
export interface RawInterface {
|
|
204
|
-
name: string;
|
|
205
|
-
file: string;
|
|
206
|
-
line_start: number;
|
|
207
|
-
line_end: number;
|
|
208
|
-
methods: string[];
|
|
209
|
-
qualified: string;
|
|
210
|
-
content_hash?: string;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
export interface RawEnum {
|
|
214
|
-
name: string;
|
|
215
|
-
file: string;
|
|
216
|
-
line_start: number;
|
|
217
|
-
line_end: number;
|
|
218
|
-
qualified: string;
|
|
219
|
-
content_hash?: string;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
export interface RawTest {
|
|
223
|
-
name: string;
|
|
224
|
-
file: string;
|
|
225
|
-
line_start: number;
|
|
226
|
-
line_end: number;
|
|
227
|
-
qualified: string;
|
|
228
|
-
content_hash?: string;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
export interface RawImport {
|
|
232
|
-
module: string;
|
|
233
|
-
file: string;
|
|
234
|
-
line: number;
|
|
235
|
-
names: string[];
|
|
236
|
-
lang: string;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
export interface RawReExport {
|
|
240
|
-
module: string;
|
|
241
|
-
file: string;
|
|
242
|
-
line: number;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
export interface RawCallSite {
|
|
246
|
-
source: string; // relative file path
|
|
247
|
-
callName: string; // function or method name being called
|
|
248
|
-
line: number; // line number of the call
|
|
249
|
-
diField?: string; // if DI pattern (this.field.method), the field name
|
|
250
|
-
resolveInClass?: string; // class to resolve in: current class for self.X(), parent for super().X()
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
export interface RawCallEdge {
|
|
254
|
-
source: string; // file path of the caller
|
|
255
|
-
target: string; // qualified name of the callee
|
|
256
|
-
callName: string;
|
|
257
|
-
line: number;
|
|
258
|
-
confidence: number;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
export interface ImportEdge {
|
|
262
|
-
source: string; // source file
|
|
263
|
-
target: string; // resolved target file or unresolved module
|
|
264
|
-
resolved: boolean;
|
|
265
|
-
line: number;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
export interface RawGraph {
|
|
269
|
-
functions: RawFunction[];
|
|
270
|
-
classes: RawClass[];
|
|
271
|
-
interfaces: RawInterface[];
|
|
272
|
-
enums: RawEnum[];
|
|
273
|
-
tests: RawTest[];
|
|
274
|
-
imports: RawImport[];
|
|
275
|
-
reExports: RawReExport[];
|
|
276
|
-
rawCalls: RawCallSite[];
|
|
277
|
-
diMaps: Map<string, Map<string, string>>; // file -> Map<fieldName, typeName>
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
export interface ParseBatchResult extends RawGraph {
|
|
281
|
-
parseErrors: number;
|
|
282
|
-
extractErrors: number;
|
|
283
|
-
}
|
package/src/parser/batch.ts
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import type { SgRoot } from '@ast-grep/napi';
|
|
2
|
-
import { parseAsync } from '@ast-grep/napi';
|
|
3
|
-
import { readFileSync } from 'fs';
|
|
4
|
-
import { extname, relative } from 'path';
|
|
5
|
-
import type { ParseBatchResult, RawCallSite, RawGraph } from '../graph/types';
|
|
6
|
-
import { NOISE } from '../shared/filters';
|
|
7
|
-
import { log } from '../shared/logger';
|
|
8
|
-
import { extractCallsFromFile, extractFromFile } from './extractor';
|
|
9
|
-
import { getLanguage } from './languages';
|
|
10
|
-
|
|
11
|
-
const BATCH_SIZE = 50;
|
|
12
|
-
|
|
13
|
-
export async function parseBatch(files: string[], repoRoot: string): Promise<ParseBatchResult> {
|
|
14
|
-
const graph: RawGraph = {
|
|
15
|
-
functions: [],
|
|
16
|
-
classes: [],
|
|
17
|
-
interfaces: [],
|
|
18
|
-
enums: [],
|
|
19
|
-
tests: [],
|
|
20
|
-
imports: [],
|
|
21
|
-
reExports: [],
|
|
22
|
-
rawCalls: [],
|
|
23
|
-
diMaps: new Map(),
|
|
24
|
-
};
|
|
25
|
-
const seen = new Set<string>();
|
|
26
|
-
let parseErrors = 0;
|
|
27
|
-
let extractErrors = 0;
|
|
28
|
-
|
|
29
|
-
for (let i = 0; i < files.length; i += BATCH_SIZE) {
|
|
30
|
-
const batch = files.slice(i, i + BATCH_SIZE);
|
|
31
|
-
|
|
32
|
-
const promises = batch.map(async (filePath) => {
|
|
33
|
-
const lang = getLanguage(extname(filePath));
|
|
34
|
-
if (!lang) return;
|
|
35
|
-
|
|
36
|
-
let source: string;
|
|
37
|
-
try {
|
|
38
|
-
source = readFileSync(filePath, 'utf-8');
|
|
39
|
-
} catch (err) {
|
|
40
|
-
log.warn('Failed to read file', { file: filePath, error: String(err) });
|
|
41
|
-
parseErrors++;
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
let root: SgRoot;
|
|
46
|
-
try {
|
|
47
|
-
root = await parseAsync(lang, source);
|
|
48
|
-
} catch (err) {
|
|
49
|
-
log.warn('Failed to parse file', { file: filePath, error: String(err) });
|
|
50
|
-
parseErrors++;
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const fp = relative(repoRoot, filePath);
|
|
55
|
-
|
|
56
|
-
try {
|
|
57
|
-
extractFromFile(root, fp, lang, seen, graph);
|
|
58
|
-
} catch (err) {
|
|
59
|
-
log.error('Extraction crashed', { file: fp, error: String(err) });
|
|
60
|
-
extractErrors++;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
try {
|
|
64
|
-
// Extract calls into a temporary buffer, then filter noise before pushing
|
|
65
|
-
const rawCalls: RawCallSite[] = [];
|
|
66
|
-
extractCallsFromFile(root, fp, lang, rawCalls);
|
|
67
|
-
for (const call of rawCalls) {
|
|
68
|
-
if (!NOISE.has(call.callName)) {
|
|
69
|
-
graph.rawCalls.push(call);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
} catch (err) {
|
|
73
|
-
log.error('Call extraction crashed', { file: fp, error: String(err) });
|
|
74
|
-
extractErrors++;
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
await Promise.all(promises);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return { ...graph, parseErrors, extractErrors };
|
|
82
|
-
}
|
package/src/parser/discovery.ts
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { readdirSync } from 'fs';
|
|
2
|
-
import { extname, join, relative, resolve } from 'path';
|
|
3
|
-
import { isSkippableFile, SKIP_DIRS } from '../shared/filters';
|
|
4
|
-
import { log } from '../shared/logger';
|
|
5
|
-
import { ensureWithinRoot } from '../shared/safe-path';
|
|
6
|
-
import { getLanguage } from './languages';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Walk the filesystem and find all supported source files.
|
|
10
|
-
* If `filterFiles` is provided, only return those specific files (resolved to absolute paths).
|
|
11
|
-
* If `include` patterns are provided, keep only files matching at least one pattern.
|
|
12
|
-
* If `exclude` patterns are provided, remove files matching any pattern.
|
|
13
|
-
*/
|
|
14
|
-
export function discoverFiles(
|
|
15
|
-
repoDir: string,
|
|
16
|
-
filterFiles?: string[],
|
|
17
|
-
include?: string[],
|
|
18
|
-
exclude?: string[],
|
|
19
|
-
): string[] {
|
|
20
|
-
const absRepoDir = resolve(repoDir);
|
|
21
|
-
|
|
22
|
-
if (filterFiles) {
|
|
23
|
-
return filterFiles
|
|
24
|
-
.map((f) => (f.startsWith('/') ? f : join(absRepoDir, f)))
|
|
25
|
-
.filter((f) => {
|
|
26
|
-
try {
|
|
27
|
-
ensureWithinRoot(f, absRepoDir);
|
|
28
|
-
return getLanguage(extname(f)) !== null;
|
|
29
|
-
} catch (err) {
|
|
30
|
-
log.warn('Skipping file outside repository root', { file: f, error: String(err) });
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
let files: string[] = [];
|
|
37
|
-
walkFiles(absRepoDir, files);
|
|
38
|
-
|
|
39
|
-
// Apply include/exclude filters using Bun.Glob
|
|
40
|
-
const hasInclude = include && include.length > 0;
|
|
41
|
-
const hasExclude = exclude && exclude.length > 0;
|
|
42
|
-
|
|
43
|
-
if (hasInclude || hasExclude) {
|
|
44
|
-
const includeGlobs = hasInclude ? include.map((p) => new Bun.Glob(p)) : null;
|
|
45
|
-
const excludeGlobs = hasExclude ? exclude.map((p) => new Bun.Glob(p)) : null;
|
|
46
|
-
|
|
47
|
-
files = files.filter((absPath) => {
|
|
48
|
-
const rel = relative(absRepoDir, absPath);
|
|
49
|
-
|
|
50
|
-
// If include patterns exist, file must match at least one
|
|
51
|
-
if (includeGlobs && !includeGlobs.some((g) => g.match(rel))) {
|
|
52
|
-
return false;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// If exclude patterns exist, file must not match any
|
|
56
|
-
if (excludeGlobs && excludeGlobs.some((g) => g.match(rel))) {
|
|
57
|
-
return false;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return true;
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return files;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function walkFiles(dir: string, files: string[]): void {
|
|
68
|
-
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
69
|
-
if (entry.isDirectory() && !SKIP_DIRS.has(entry.name)) {
|
|
70
|
-
walkFiles(join(dir, entry.name), files);
|
|
71
|
-
} else if (entry.isFile() && getLanguage(extname(entry.name)) !== null && !isSkippableFile(entry.name)) {
|
|
72
|
-
files.push(join(dir, entry.name));
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
package/src/parser/extractor.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import type { Lang, SgRoot } from '@ast-grep/napi';
|
|
2
|
-
import type { RawCallSite, RawGraph } from '../graph/types';
|
|
3
|
-
import { extractCallsFromGeneric, extractGeneric } from './extractors/generic';
|
|
4
|
-
import { extractCallsFromPython, extractPython } from './extractors/python';
|
|
5
|
-
import { extractCallsFromRuby, extractRuby } from './extractors/ruby';
|
|
6
|
-
import { extractCallsFromTypeScript, extractTypeScript } from './extractors/typescript';
|
|
7
|
-
import { isTypeScriptLike } from './languages';
|
|
8
|
-
|
|
9
|
-
export function extractFromFile(
|
|
10
|
-
root: SgRoot,
|
|
11
|
-
filePath: string,
|
|
12
|
-
lang: Lang | string,
|
|
13
|
-
seen: Set<string>,
|
|
14
|
-
graph: RawGraph,
|
|
15
|
-
): void {
|
|
16
|
-
if (isTypeScriptLike(lang)) {
|
|
17
|
-
extractTypeScript(root, filePath, seen, graph, lang);
|
|
18
|
-
} else if (lang === 'python') {
|
|
19
|
-
extractPython(root, filePath, seen, graph);
|
|
20
|
-
} else if (lang === 'ruby') {
|
|
21
|
-
extractRuby(root, filePath, seen, graph);
|
|
22
|
-
} else {
|
|
23
|
-
extractGeneric(root, filePath, lang as string, seen, graph);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function extractCallsFromFile(root: SgRoot, filePath: string, lang: Lang | string, calls: RawCallSite[]): void {
|
|
28
|
-
if (isTypeScriptLike(lang)) {
|
|
29
|
-
extractCallsFromTypeScript(root, filePath, calls);
|
|
30
|
-
} else if (lang === 'python') {
|
|
31
|
-
extractCallsFromPython(root, filePath, calls);
|
|
32
|
-
} else if (lang === 'ruby') {
|
|
33
|
-
extractCallsFromRuby(root, filePath, calls);
|
|
34
|
-
} else {
|
|
35
|
-
extractCallsFromGeneric(root, filePath, lang as string, calls);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import type { SgNode, SgRoot } from '@ast-grep/napi';
|
|
2
|
-
import type { RawCallSite, RawGraph } from '../../graph/types';
|
|
3
|
-
import { type CallExtractionConfig, extractCalls } from '../../shared/extract-calls';
|
|
4
|
-
import { computeContentHash } from '../../shared/file-hash';
|
|
5
|
-
import { log } from '../../shared/logger';
|
|
6
|
-
import { LANG_KINDS } from '../languages';
|
|
7
|
-
|
|
8
|
-
export function extractGeneric(root: SgRoot, fp: string, lang: string, seen: Set<string>, graph: RawGraph): void {
|
|
9
|
-
const kinds = LANG_KINDS[lang];
|
|
10
|
-
if (!kinds) return;
|
|
11
|
-
const rootNode = root.root();
|
|
12
|
-
|
|
13
|
-
// Try to extract classes
|
|
14
|
-
for (const classKind of [kinds.class, kinds.struct, kinds.interface].filter(Boolean)) {
|
|
15
|
-
try {
|
|
16
|
-
for (const node of rootNode.findAll({ rule: { kind: classKind } })) {
|
|
17
|
-
const name = node.field('name')?.text();
|
|
18
|
-
if (!name || seen.has(`c:${fp}:${name}`)) continue;
|
|
19
|
-
seen.add(`c:${fp}:${name}`);
|
|
20
|
-
graph.classes.push({
|
|
21
|
-
name,
|
|
22
|
-
file: fp,
|
|
23
|
-
line_start: node.range().start.line,
|
|
24
|
-
line_end: node.range().end.line,
|
|
25
|
-
extends: '',
|
|
26
|
-
implements: '',
|
|
27
|
-
qualified: `${fp}::${name}`,
|
|
28
|
-
content_hash: computeContentHash(node.text()),
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
} catch (err) {
|
|
32
|
-
log.debug('Generic extraction failed', { file: fp, error: String(err) });
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Try to extract functions/methods
|
|
37
|
-
// biome-ignore lint/complexity/useLiteralKeys: 'constructor' must use bracket notation to avoid Object.prototype.constructor
|
|
38
|
-
for (const funcKind of [kinds.function, kinds.method, kinds['constructor'] as string | undefined].filter(Boolean)) {
|
|
39
|
-
try {
|
|
40
|
-
for (const node of rootNode.findAll({ rule: { kind: funcKind } })) {
|
|
41
|
-
const name = node.field('name')?.text();
|
|
42
|
-
if (!name) continue;
|
|
43
|
-
const line = node.range().start.line;
|
|
44
|
-
if (seen.has(`f:${fp}:${name}:${line}`)) continue;
|
|
45
|
-
seen.add(`f:${fp}:${name}:${line}`);
|
|
46
|
-
|
|
47
|
-
const classAncestor = node.ancestors().find((a: SgNode) => {
|
|
48
|
-
const k = String(a.kind());
|
|
49
|
-
return k.includes('class') || k.includes('struct') || k.includes('impl');
|
|
50
|
-
});
|
|
51
|
-
const className = classAncestor?.field('name')?.text() || '';
|
|
52
|
-
|
|
53
|
-
graph.functions.push({
|
|
54
|
-
name,
|
|
55
|
-
file: fp,
|
|
56
|
-
line_start: line,
|
|
57
|
-
line_end: node.range().end.line,
|
|
58
|
-
params: node.field('parameters')?.text() || '()',
|
|
59
|
-
returnType: node.field('return_type')?.text() || '',
|
|
60
|
-
kind: className ? 'Method' : 'Function',
|
|
61
|
-
className,
|
|
62
|
-
qualified: className ? `${fp}::${className}.${name}` : `${fp}::${name}`,
|
|
63
|
-
content_hash: computeContentHash(node.text()),
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
} catch (err) {
|
|
67
|
-
log.debug('Generic extraction failed', { file: fp, error: String(err) });
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/** Shared class-finder for languages using class/struct/impl AST kinds. */
|
|
73
|
-
function findEnclosingClassGeneric(node: import('@ast-grep/napi').SgNode): import('@ast-grep/napi').SgNode | null {
|
|
74
|
-
return node.ancestors().find((a) => {
|
|
75
|
-
const k = String(a.kind());
|
|
76
|
-
return k.includes('class') || k.includes('struct') || k.includes('impl');
|
|
77
|
-
}) ?? null;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/** Per-language call extraction configs for self/super detection. */
|
|
81
|
-
const GENERIC_CONFIGS: Record<string, CallExtractionConfig> = {
|
|
82
|
-
java: {
|
|
83
|
-
selfPrefixes: ['this.'],
|
|
84
|
-
superPrefixes: ['super.'],
|
|
85
|
-
findEnclosingClass: findEnclosingClassGeneric,
|
|
86
|
-
getParentClass: (classNode) => {
|
|
87
|
-
const sc = classNode.children().find((c) => c.kind() === 'superclass');
|
|
88
|
-
return sc?.children().find((c) => c.kind() === 'type_identifier')?.text();
|
|
89
|
-
},
|
|
90
|
-
},
|
|
91
|
-
csharp: {
|
|
92
|
-
selfPrefixes: ['this.'],
|
|
93
|
-
superPrefixes: ['base.'],
|
|
94
|
-
findEnclosingClass: findEnclosingClassGeneric,
|
|
95
|
-
getParentClass: (classNode) => {
|
|
96
|
-
const bl = classNode.children().find((c) => c.kind() === 'base_list');
|
|
97
|
-
return bl?.children().find((c) => c.kind() === 'identifier' || c.kind() === 'type_identifier')?.text();
|
|
98
|
-
},
|
|
99
|
-
},
|
|
100
|
-
rust: {
|
|
101
|
-
selfPrefixes: ['self.'],
|
|
102
|
-
superPrefixes: [],
|
|
103
|
-
findEnclosingClass: (node) =>
|
|
104
|
-
node.ancestors().find((a) => a.kind() === 'impl_item') ?? null,
|
|
105
|
-
},
|
|
106
|
-
go: {
|
|
107
|
-
selfPrefixes: [],
|
|
108
|
-
superPrefixes: [],
|
|
109
|
-
findEnclosingClass: findEnclosingClassGeneric,
|
|
110
|
-
},
|
|
111
|
-
php: {
|
|
112
|
-
selfPrefixes: [],
|
|
113
|
-
superPrefixes: [],
|
|
114
|
-
findEnclosingClass: findEnclosingClassGeneric,
|
|
115
|
-
},
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
/** Fallback config for unknown languages — no self/super detection. */
|
|
119
|
-
const FALLBACK_CONFIG: CallExtractionConfig = {
|
|
120
|
-
selfPrefixes: [],
|
|
121
|
-
superPrefixes: [],
|
|
122
|
-
findEnclosingClass: findEnclosingClassGeneric,
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Extract raw call sites from a generic language AST.
|
|
127
|
-
* Uses per-language config for self/super detection.
|
|
128
|
-
*/
|
|
129
|
-
export function extractCallsFromGeneric(root: SgRoot, fp: string, lang: string, calls: RawCallSite[]): void {
|
|
130
|
-
const config = GENERIC_CONFIGS[lang] ?? FALLBACK_CONFIG;
|
|
131
|
-
extractCalls(root.root(), fp, config, calls);
|
|
132
|
-
}
|