@codeflow-map/core 0.1.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/README.md +158 -0
- package/dist/analyzer/extractorUtils.d.ts +50 -0
- package/dist/analyzer/extractorUtils.js +247 -0
- package/dist/analyzer/index.d.ts +9 -0
- package/dist/analyzer/index.js +149 -0
- package/dist/analyzer/languages/go.d.ts +2 -0
- package/dist/analyzer/languages/go.js +232 -0
- package/dist/analyzer/languages/javascript.d.ts +2 -0
- package/dist/analyzer/languages/javascript.js +265 -0
- package/dist/analyzer/languages/jsx.d.ts +2 -0
- package/dist/analyzer/languages/jsx.js +275 -0
- package/dist/analyzer/languages/python.d.ts +2 -0
- package/dist/analyzer/languages/python.js +236 -0
- package/dist/analyzer/languages/tsx.d.ts +2 -0
- package/dist/analyzer/languages/tsx.js +358 -0
- package/dist/analyzer/languages/typescript.d.ts +2 -0
- package/dist/analyzer/languages/typescript.js +351 -0
- package/dist/analyzer/treeSitter.d.ts +4 -0
- package/dist/analyzer/treeSitter.js +62 -0
- package/dist/graph/callGraphBuilder.d.ts +2 -0
- package/dist/graph/callGraphBuilder.js +58 -0
- package/dist/graph/entryPointDetector.d.ts +2 -0
- package/dist/graph/entryPointDetector.js +11 -0
- package/dist/graph/flowPartitioner.d.ts +5 -0
- package/dist/graph/flowPartitioner.js +101 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +22 -0
- package/dist/types.d.ts +62 -0
- package/dist/types.js +13 -0
- package/package.json +48 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.typescriptAnalyzer = void 0;
|
|
4
|
+
const extractorUtils_1 = require("../extractorUtils");
|
|
5
|
+
exports.typescriptAnalyzer = {
|
|
6
|
+
functionQuery: `
|
|
7
|
+
(function_declaration
|
|
8
|
+
name: (identifier) @fn.name
|
|
9
|
+
parameters: (formal_parameters) @fn.params
|
|
10
|
+
return_type: (type_annotation)? @fn.return_type) @fn.decl
|
|
11
|
+
|
|
12
|
+
(lexical_declaration
|
|
13
|
+
(variable_declarator
|
|
14
|
+
name: (identifier) @fn.name
|
|
15
|
+
value: [(arrow_function) (function_expression)] @fn.decl))
|
|
16
|
+
|
|
17
|
+
(export_statement
|
|
18
|
+
declaration: (lexical_declaration
|
|
19
|
+
(variable_declarator
|
|
20
|
+
name: (identifier) @fn.name
|
|
21
|
+
value: [(arrow_function) (function_expression)] @fn.decl)))
|
|
22
|
+
|
|
23
|
+
(method_definition
|
|
24
|
+
name: (property_identifier) @fn.name
|
|
25
|
+
parameters: (formal_parameters) @fn.params
|
|
26
|
+
return_type: (type_annotation)? @fn.return_type) @fn.decl
|
|
27
|
+
|
|
28
|
+
(call_expression
|
|
29
|
+
function: (identifier) @hook.name
|
|
30
|
+
arguments: (arguments
|
|
31
|
+
[(arrow_function) (function_expression)] @fn.decl)
|
|
32
|
+
(#match? @hook.name "^use"))
|
|
33
|
+
|
|
34
|
+
(generator_function_declaration
|
|
35
|
+
name: (identifier) @fn.name
|
|
36
|
+
parameters: (formal_parameters) @fn.params
|
|
37
|
+
return_type: (type_annotation)? @fn.return_type) @fn.decl
|
|
38
|
+
|
|
39
|
+
(lexical_declaration
|
|
40
|
+
(variable_declarator
|
|
41
|
+
name: (identifier) @fn.name
|
|
42
|
+
value: (generator_function) @fn.decl))
|
|
43
|
+
|
|
44
|
+
(export_statement
|
|
45
|
+
declaration: (lexical_declaration
|
|
46
|
+
(variable_declarator
|
|
47
|
+
name: (identifier) @fn.name
|
|
48
|
+
value: (generator_function) @fn.decl)))
|
|
49
|
+
|
|
50
|
+
(function_expression
|
|
51
|
+
name: (identifier) @fn.name
|
|
52
|
+
parameters: (formal_parameters) @fn.params) @fn.decl
|
|
53
|
+
|
|
54
|
+
(lexical_declaration
|
|
55
|
+
(variable_declarator
|
|
56
|
+
name: (identifier) @fn.name
|
|
57
|
+
value: (call_expression
|
|
58
|
+
function: [(parenthesized_expression
|
|
59
|
+
[(function_expression)(arrow_function)] @fn.decl)
|
|
60
|
+
(arrow_function) @fn.decl
|
|
61
|
+
(function_expression) @fn.decl])))
|
|
62
|
+
|
|
63
|
+
(expression_statement
|
|
64
|
+
(call_expression
|
|
65
|
+
function: (parenthesized_expression
|
|
66
|
+
[(function_expression)(arrow_function)] @fn.decl)))
|
|
67
|
+
|
|
68
|
+
(assignment_expression
|
|
69
|
+
left: (member_expression
|
|
70
|
+
object: (identifier) @worker.obj
|
|
71
|
+
property: (property_identifier) @worker.prop)
|
|
72
|
+
right: [(arrow_function) (function_expression)] @worker.fn
|
|
73
|
+
(#match? @worker.obj "^self$")
|
|
74
|
+
(#match? @worker.prop "^onmessage$"))
|
|
75
|
+
|
|
76
|
+
(call_expression
|
|
77
|
+
function: (member_expression
|
|
78
|
+
object: (identifier) @worker.obj
|
|
79
|
+
property: (property_identifier) @worker.method)
|
|
80
|
+
arguments: (arguments
|
|
81
|
+
(string) @worker.event
|
|
82
|
+
[(arrow_function) (function_expression)] @worker.fn)
|
|
83
|
+
(#match? @worker.obj "^self$")
|
|
84
|
+
(#match? @worker.method "^addEventListener$")
|
|
85
|
+
(#match? @worker.event "message"))
|
|
86
|
+
|
|
87
|
+
(lexical_declaration
|
|
88
|
+
(variable_declarator
|
|
89
|
+
name: (identifier) @fn.name
|
|
90
|
+
value: (call_expression
|
|
91
|
+
function: (identifier) @wrapper.name
|
|
92
|
+
arguments: (arguments
|
|
93
|
+
[(arrow_function) (function_expression)] @fn.decl)
|
|
94
|
+
(#match? @wrapper.name "^(forwardRef|memo)$"))))
|
|
95
|
+
|
|
96
|
+
(lexical_declaration
|
|
97
|
+
(variable_declarator
|
|
98
|
+
name: (identifier) @fn.name
|
|
99
|
+
value: (call_expression
|
|
100
|
+
function: (identifier) @wrapper.outer
|
|
101
|
+
arguments: (arguments
|
|
102
|
+
(call_expression
|
|
103
|
+
function: (identifier) @wrapper.inner
|
|
104
|
+
arguments: (arguments
|
|
105
|
+
[(arrow_function) (function_expression)] @fn.decl)))
|
|
106
|
+
(#match? @wrapper.outer "^(memo|forwardRef)$"))))
|
|
107
|
+
|
|
108
|
+
(export_statement
|
|
109
|
+
declaration: (lexical_declaration
|
|
110
|
+
(variable_declarator
|
|
111
|
+
name: (identifier) @fn.name
|
|
112
|
+
value: (call_expression
|
|
113
|
+
function: (identifier) @wrapper.name
|
|
114
|
+
arguments: (arguments
|
|
115
|
+
[(arrow_function) (function_expression)] @fn.decl)
|
|
116
|
+
(#match? @wrapper.name "^(forwardRef|memo)$")))))
|
|
117
|
+
|
|
118
|
+
(export_statement
|
|
119
|
+
declaration: (lexical_declaration
|
|
120
|
+
(variable_declarator
|
|
121
|
+
name: (identifier) @fn.name
|
|
122
|
+
value: (call_expression
|
|
123
|
+
function: (identifier) @wrapper.outer
|
|
124
|
+
arguments: (arguments
|
|
125
|
+
(call_expression
|
|
126
|
+
function: (identifier) @wrapper.inner
|
|
127
|
+
arguments: (arguments
|
|
128
|
+
[(arrow_function) (function_expression)] @fn.decl)))
|
|
129
|
+
(#match? @wrapper.outer "^(memo|forwardRef)$")))))
|
|
130
|
+
|
|
131
|
+
(public_field_definition
|
|
132
|
+
name: (property_identifier) @fn.name
|
|
133
|
+
value: [(arrow_function)(function_expression)] @fn.decl)
|
|
134
|
+
`,
|
|
135
|
+
callQuery: `
|
|
136
|
+
(call_expression
|
|
137
|
+
function: [(identifier)(member_expression)] @call.name) @call.expr
|
|
138
|
+
|
|
139
|
+
(new_expression
|
|
140
|
+
constructor: (identifier) @call.name) @call.expr
|
|
141
|
+
|
|
142
|
+
(call_expression
|
|
143
|
+
function: (member_expression
|
|
144
|
+
property: (property_identifier) @call.name
|
|
145
|
+
(#match? @call.name "^(then|catch|finally)$"))) @call.expr
|
|
146
|
+
`,
|
|
147
|
+
extractFunction(match, filePath, languageId) {
|
|
148
|
+
const declCapture = match.captures.find((c) => c.name === 'fn.decl');
|
|
149
|
+
if (!declCapture) {
|
|
150
|
+
const workerFn = match.captures.find((c) => c.name === 'worker.fn');
|
|
151
|
+
if (workerFn) {
|
|
152
|
+
const workerProp = match.captures.find((c) => c.name === 'worker.prop');
|
|
153
|
+
const node = workerFn.node;
|
|
154
|
+
const rawName = workerProp ? 'onmessage_handler' : `message_handler_${node.startPosition.row}`;
|
|
155
|
+
const paramsList = (0, extractorUtils_1.getParamsFromFunctionNode)(node, true);
|
|
156
|
+
return {
|
|
157
|
+
id: `${filePath}::${rawName}::${node.startPosition.row}`,
|
|
158
|
+
name: rawName,
|
|
159
|
+
filePath,
|
|
160
|
+
startLine: node.startPosition.row,
|
|
161
|
+
endLine: node.endPosition.row,
|
|
162
|
+
params: paramsList,
|
|
163
|
+
returnType: null,
|
|
164
|
+
isAsync: (0, extractorUtils_1.checkIsAsync)(node),
|
|
165
|
+
isExported: false,
|
|
166
|
+
isEntryPoint: true,
|
|
167
|
+
language: languageId,
|
|
168
|
+
kind: 'function'
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
const nameCapture = match.captures.find((c) => c.name === 'fn.name');
|
|
174
|
+
const hookNameCapture = match.captures.find((c) => c.name === 'hook.name');
|
|
175
|
+
// For wrapper patterns (memo, forwardRef), only capture the first argument
|
|
176
|
+
const wrapperCapture = match.captures.find((c) => c.name === 'wrapper.name' || c.name === 'wrapper.outer');
|
|
177
|
+
if (wrapperCapture) {
|
|
178
|
+
const argsParent = declCapture.node.parent;
|
|
179
|
+
if (argsParent?.type === 'arguments') {
|
|
180
|
+
const firstArg = argsParent.namedChildren[0];
|
|
181
|
+
if (firstArg.id !== declCapture.node.id) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
let rawName = "anonymous";
|
|
187
|
+
let kind = 'function';
|
|
188
|
+
let parentFunctionId;
|
|
189
|
+
if (nameCapture) {
|
|
190
|
+
rawName = nameCapture.node.text;
|
|
191
|
+
if (declCapture.node.type === 'function_expression' && nameCapture.node.parent?.id === declCapture.node.id) {
|
|
192
|
+
if (declCapture.node.parent?.type === 'variable_declarator') {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
parentFunctionId = (0, extractorUtils_1.getEnclosingFunctionId)(declCapture.node, filePath) ?? undefined;
|
|
196
|
+
}
|
|
197
|
+
if (declCapture.node.type === 'method_definition') {
|
|
198
|
+
kind = 'method';
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
else if (hookNameCapture) {
|
|
202
|
+
const startLine = declCapture.node.startPosition.row;
|
|
203
|
+
rawName = `${hookNameCapture.node.text}_callback_${startLine}`;
|
|
204
|
+
kind = 'hook';
|
|
205
|
+
}
|
|
206
|
+
// Detect IIFE: fn.decl is inside a call_expression (the IIFE invocation)
|
|
207
|
+
// Skip IIFE detection for wrapper patterns (forwardRef, memo)
|
|
208
|
+
const declParent = declCapture.node.parent;
|
|
209
|
+
const isParenWrapped = declParent?.type === 'parenthesized_expression';
|
|
210
|
+
const callParent = isParenWrapped ? declParent.parent : declParent;
|
|
211
|
+
if (callParent?.type === 'call_expression' && !wrapperCapture) {
|
|
212
|
+
if (rawName === 'anonymous') {
|
|
213
|
+
rawName = `iife_${declCapture.node.startPosition.row}`;
|
|
214
|
+
}
|
|
215
|
+
kind = 'iife';
|
|
216
|
+
}
|
|
217
|
+
else if (rawName === "anonymous" && !hookNameCapture) {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
// Prefix with enclosing scope chain (parent functions, classes, and object literals)
|
|
221
|
+
const scopeChain = (0, extractorUtils_1.getEnclosingScopeChain)(declCapture.node, extractorUtils_1.JS_FN_SCOPE_TYPES, extractorUtils_1.JS_CLASS_SCOPE_TYPES);
|
|
222
|
+
if (scopeChain.length > 0) {
|
|
223
|
+
rawName = `${scopeChain.map(s => s.name).join('.')}.${rawName}`;
|
|
224
|
+
if (kind === 'method' && scopeChain[scopeChain.length - 1].type === 'object') {
|
|
225
|
+
// method_definition inside object literal is still a method
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
// For wrapper patterns, use the outermost wrapper call_expression range
|
|
229
|
+
let rangeNode = declCapture.node;
|
|
230
|
+
if (wrapperCapture) {
|
|
231
|
+
let target = declCapture.node.parent;
|
|
232
|
+
while (target?.type === 'arguments') {
|
|
233
|
+
const callExpr = target.parent;
|
|
234
|
+
if (callExpr?.type !== 'call_expression')
|
|
235
|
+
break;
|
|
236
|
+
const fn = callExpr.childForFieldName('function');
|
|
237
|
+
const isKnownWrapper = fn && ((fn.type === 'identifier' && /^(forwardRef|memo)$/.test(fn.text)) ||
|
|
238
|
+
(fn.type === 'member_expression' && /^(forwardRef|memo)$/.test(fn.childForFieldName('property')?.text ?? '')));
|
|
239
|
+
if (!isKnownWrapper)
|
|
240
|
+
break;
|
|
241
|
+
rangeNode = callExpr;
|
|
242
|
+
target = callExpr.parent;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const paramsCapture = match.captures.find((c) => c.name === 'fn.params');
|
|
246
|
+
const returnCapture = match.captures.find((c) => c.name === 'fn.return_type');
|
|
247
|
+
const isExported = (0, extractorUtils_1.checkExportedByParentWalk)(declCapture.node);
|
|
248
|
+
const paramsList = paramsCapture
|
|
249
|
+
? (0, extractorUtils_1.buildJsParams)(paramsCapture.node, true)
|
|
250
|
+
: (0, extractorUtils_1.getParamsFromFunctionNode)(declCapture.node, true);
|
|
251
|
+
let finalReturnType = returnCapture ? returnCapture.node.text.replace(/^:\s*/, '') : null;
|
|
252
|
+
if (!finalReturnType) {
|
|
253
|
+
const returnValues = (0, extractorUtils_1.getReturnValues)(declCapture.node, extractorUtils_1.JS_NESTED_FN_TYPES);
|
|
254
|
+
if (returnValues.length > 0) {
|
|
255
|
+
finalReturnType = returnValues.join(' | ');
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
id: `${filePath}::${rawName}::${rangeNode.startPosition.row}`,
|
|
260
|
+
name: rawName,
|
|
261
|
+
filePath,
|
|
262
|
+
startLine: rangeNode.startPosition.row,
|
|
263
|
+
endLine: rangeNode.endPosition.row,
|
|
264
|
+
params: paramsList,
|
|
265
|
+
returnType: finalReturnType,
|
|
266
|
+
isAsync: (0, extractorUtils_1.checkIsAsync)(declCapture.node),
|
|
267
|
+
isExported,
|
|
268
|
+
isEntryPoint: false,
|
|
269
|
+
language: languageId,
|
|
270
|
+
kind,
|
|
271
|
+
...(parentFunctionId ? { parentFunctionId } : {})
|
|
272
|
+
};
|
|
273
|
+
},
|
|
274
|
+
extractCall(match, filePath) {
|
|
275
|
+
const nameCapture = match.captures.find((c) => c.name === 'call.name');
|
|
276
|
+
const exprCapture = match.captures.find((c) => c.name === 'call.expr');
|
|
277
|
+
if (nameCapture && exprCapture) {
|
|
278
|
+
let calleeName = nameCapture.node.text;
|
|
279
|
+
if (nameCapture.node.type === 'member_expression') {
|
|
280
|
+
const prop = nameCapture.node.childForFieldName('property');
|
|
281
|
+
if (prop)
|
|
282
|
+
calleeName = prop.text;
|
|
283
|
+
}
|
|
284
|
+
// Unwrap .bind(): handleWorkerMessage.bind(this) → ref to handleWorkerMessage
|
|
285
|
+
if (calleeName === 'bind' && nameCapture.node.type === 'member_expression') {
|
|
286
|
+
const obj = nameCapture.node.childForFieldName('object');
|
|
287
|
+
if (obj?.type === 'identifier') {
|
|
288
|
+
return {
|
|
289
|
+
callerFilePath: filePath,
|
|
290
|
+
calleeName: obj.text,
|
|
291
|
+
line: exprCapture.node.startPosition.row,
|
|
292
|
+
callType: (0, extractorUtils_1.isInsidePromiseCombinator)(exprCapture.node) ? 'concurrent' : 'ref',
|
|
293
|
+
isRef: true
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
const callType = (0, extractorUtils_1.isInsidePromiseCombinator)(exprCapture.node) ? 'concurrent' : undefined;
|
|
298
|
+
const directCall = {
|
|
299
|
+
callerFilePath: filePath,
|
|
300
|
+
calleeName,
|
|
301
|
+
line: exprCapture.node.startPosition.row,
|
|
302
|
+
...(callType ? { callType } : {})
|
|
303
|
+
};
|
|
304
|
+
// Extract function reference arguments from any call
|
|
305
|
+
const refCalls = [];
|
|
306
|
+
const callNode = exprCapture.node;
|
|
307
|
+
const argsNode = callNode.childForFieldName('arguments');
|
|
308
|
+
if (argsNode) {
|
|
309
|
+
for (const arg of argsNode.namedChildren) {
|
|
310
|
+
let refName = null;
|
|
311
|
+
if (arg.type === 'identifier') {
|
|
312
|
+
refName = arg.text;
|
|
313
|
+
}
|
|
314
|
+
else if (arg.type === 'member_expression') {
|
|
315
|
+
const obj = arg.childForFieldName('object');
|
|
316
|
+
const prop = arg.childForFieldName('property');
|
|
317
|
+
if (obj?.type === 'this' && prop) {
|
|
318
|
+
refName = prop.text;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
else if (arg.type === 'call_expression') {
|
|
322
|
+
// Unwrap .bind(): handleWorkerMessage.bind(this) -> handleWorkerMessage
|
|
323
|
+
const fn = arg.childForFieldName('function');
|
|
324
|
+
if (fn?.type === 'member_expression') {
|
|
325
|
+
const prop = fn.childForFieldName('property');
|
|
326
|
+
const obj = fn.childForFieldName('object');
|
|
327
|
+
if (prop?.text === 'bind' && obj?.type === 'identifier') {
|
|
328
|
+
refName = obj.text;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (refName) {
|
|
333
|
+
const refCallType = (0, extractorUtils_1.isInsidePromiseCombinator)(callNode) ? 'concurrent' : 'ref';
|
|
334
|
+
refCalls.push({
|
|
335
|
+
callerFilePath: filePath,
|
|
336
|
+
calleeName: refName,
|
|
337
|
+
line: callNode.startPosition.row,
|
|
338
|
+
callType: refCallType,
|
|
339
|
+
isRef: true
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
if (refCalls.length > 0) {
|
|
345
|
+
return [directCall, ...refCalls];
|
|
346
|
+
}
|
|
347
|
+
return directCall;
|
|
348
|
+
}
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import Parser from 'web-tree-sitter';
|
|
2
|
+
import { SupportedLanguage } from '../types';
|
|
3
|
+
export declare function initTreeSitter(wasmDirectory: string): Promise<void>;
|
|
4
|
+
export declare function loadLanguage(lang: SupportedLanguage, wasmDirectory: string): Promise<Parser.Language>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.initTreeSitter = initTreeSitter;
|
|
40
|
+
exports.loadLanguage = loadLanguage;
|
|
41
|
+
const web_tree_sitter_1 = __importDefault(require("web-tree-sitter"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
let initialized = false;
|
|
44
|
+
const languageCache = new Map();
|
|
45
|
+
async function initTreeSitter(wasmDirectory) {
|
|
46
|
+
if (initialized)
|
|
47
|
+
return;
|
|
48
|
+
await web_tree_sitter_1.default.init({
|
|
49
|
+
locateFile: () => path.join(wasmDirectory, 'tree-sitter.wasm'),
|
|
50
|
+
});
|
|
51
|
+
initialized = true;
|
|
52
|
+
}
|
|
53
|
+
async function loadLanguage(lang, wasmDirectory) {
|
|
54
|
+
if (languageCache.has(lang)) {
|
|
55
|
+
return languageCache.get(lang);
|
|
56
|
+
}
|
|
57
|
+
const wasmName = lang === 'jsx' ? 'javascript' : lang;
|
|
58
|
+
const wasmPath = path.join(wasmDirectory, `tree-sitter-${wasmName}.wasm`);
|
|
59
|
+
const language = await web_tree_sitter_1.default.Language.load(wasmPath);
|
|
60
|
+
languageCache.set(lang, language);
|
|
61
|
+
return language;
|
|
62
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildCallGraph = buildCallGraph;
|
|
4
|
+
function buildCallGraph(nodes, rawCalls) {
|
|
5
|
+
const byName = new Map();
|
|
6
|
+
const bySuffix = new Map();
|
|
7
|
+
for (const n of nodes) {
|
|
8
|
+
byName.set(n.name, n);
|
|
9
|
+
const dotIdx = n.name.lastIndexOf('.');
|
|
10
|
+
if (dotIdx >= 0) {
|
|
11
|
+
const suffix = n.name.substring(dotIdx + 1);
|
|
12
|
+
if (!bySuffix.has(suffix))
|
|
13
|
+
bySuffix.set(suffix, []);
|
|
14
|
+
bySuffix.get(suffix).push(n);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const edges = [];
|
|
18
|
+
for (const call of rawCalls) {
|
|
19
|
+
let callee = byName.get(call.calleeName);
|
|
20
|
+
// Find the innermost function whose range contains the call line,
|
|
21
|
+
// excluding the callee itself to avoid self-loops (e.g. go func(){...}())
|
|
22
|
+
const callerCandidates = nodes.filter(n => n.filePath === call.callerFilePath &&
|
|
23
|
+
call.line >= n.startLine &&
|
|
24
|
+
call.line <= n.endLine &&
|
|
25
|
+
n !== callee);
|
|
26
|
+
const caller = callerCandidates.length > 0
|
|
27
|
+
? callerCandidates.reduce((best, n) => (n.endLine - n.startLine) < (best.endLine - best.startLine) ? n : best)
|
|
28
|
+
: undefined;
|
|
29
|
+
if (!caller)
|
|
30
|
+
continue;
|
|
31
|
+
if (!callee) {
|
|
32
|
+
const candidates = bySuffix.get(call.calleeName);
|
|
33
|
+
if (candidates && candidates.length === 1) {
|
|
34
|
+
const candidate = candidates[0];
|
|
35
|
+
// Suffix matching requires shared receiver prefix between caller and callee
|
|
36
|
+
const callerDot = caller.name.lastIndexOf('.');
|
|
37
|
+
const calleeDot = candidate.name.lastIndexOf('.');
|
|
38
|
+
if (callerDot >= 0 && calleeDot >= 0) {
|
|
39
|
+
const callerPrefix = caller.name.substring(0, callerDot);
|
|
40
|
+
const calleePrefix = candidate.name.substring(0, calleeDot);
|
|
41
|
+
if (callerPrefix === calleePrefix) {
|
|
42
|
+
callee = candidate;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (!callee)
|
|
48
|
+
continue;
|
|
49
|
+
// Deduplicate: skip if an edge with same from, to, and line already exists
|
|
50
|
+
if (edges.some(e => e.from === caller.id && e.to === callee.id && e.line === call.line))
|
|
51
|
+
continue;
|
|
52
|
+
const edge = { from: caller.id, to: callee.id, line: call.line };
|
|
53
|
+
if (call.callType)
|
|
54
|
+
edge.callType = call.callType;
|
|
55
|
+
edges.push(edge);
|
|
56
|
+
}
|
|
57
|
+
return edges;
|
|
58
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.detectEntryPoints = detectEntryPoints;
|
|
4
|
+
function detectEntryPoints(nodes, edges) {
|
|
5
|
+
const nonSelfEdges = edges.filter(e => e.from !== e.to);
|
|
6
|
+
const calledIds = new Set(nonSelfEdges.map(e => e.to));
|
|
7
|
+
const callerIds = new Set(nonSelfEdges.map(e => e.from));
|
|
8
|
+
for (const node of nodes) {
|
|
9
|
+
node.isEntryPoint = node.isEntryPoint || (!calledIds.has(node.id) && callerIds.has(node.id));
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.partitionFlows = partitionFlows;
|
|
4
|
+
function partitionFlows(nodes, edges) {
|
|
5
|
+
const flows = [];
|
|
6
|
+
const visited = new Set();
|
|
7
|
+
const nonSelfEdges = edges.filter(e => e.from !== e.to);
|
|
8
|
+
// 1. BFS from every entry point (in-degree=0, out-degree>0)
|
|
9
|
+
const entryIds = findEntryIds(new Set(nodes.map(n => n.id)), nonSelfEdges);
|
|
10
|
+
for (const eid of entryIds) {
|
|
11
|
+
const reachable = bfs(eid, nonSelfEdges);
|
|
12
|
+
flows.push({ id: eid, entryPoint: eid, nodeIds: [...reachable] });
|
|
13
|
+
reachable.forEach(id => visited.add(id));
|
|
14
|
+
}
|
|
15
|
+
// 2. Iteratively find mini-flows in unvisited subgraph
|
|
16
|
+
let remaining = new Set(nodes.map(n => n.id).filter(id => !visited.has(id)));
|
|
17
|
+
while (remaining.size > 0) {
|
|
18
|
+
const subEdges = nonSelfEdges.filter(e => remaining.has(e.from) && remaining.has(e.to));
|
|
19
|
+
const subEntries = findEntryIds(remaining, subEdges);
|
|
20
|
+
if (subEntries.length === 0)
|
|
21
|
+
break;
|
|
22
|
+
for (const eid of subEntries) {
|
|
23
|
+
const reachable = bfs(eid, subEdges);
|
|
24
|
+
flows.push({ id: eid, entryPoint: eid, nodeIds: [...reachable] });
|
|
25
|
+
reachable.forEach(id => visited.add(id));
|
|
26
|
+
}
|
|
27
|
+
remaining = new Set([...remaining].filter(id => !visited.has(id)));
|
|
28
|
+
}
|
|
29
|
+
// 3. Remaining nodes with edges (pure cycles) → pick one per component
|
|
30
|
+
remaining = new Set([...remaining]);
|
|
31
|
+
if (remaining.size > 0) {
|
|
32
|
+
const subEdges = nonSelfEdges.filter(e => remaining.has(e.from) && remaining.has(e.to));
|
|
33
|
+
const nodesWithEdges = new Set();
|
|
34
|
+
for (const e of subEdges) {
|
|
35
|
+
nodesWithEdges.add(e.from);
|
|
36
|
+
nodesWithEdges.add(e.to);
|
|
37
|
+
}
|
|
38
|
+
const componentVisited = new Set();
|
|
39
|
+
for (const nid of nodesWithEdges) {
|
|
40
|
+
if (componentVisited.has(nid))
|
|
41
|
+
continue;
|
|
42
|
+
// BFS bidirectional to find connected component
|
|
43
|
+
const component = bfsBidirectional(nid, subEdges);
|
|
44
|
+
component.forEach(id => componentVisited.add(id));
|
|
45
|
+
// Pick the first node as synthetic entry
|
|
46
|
+
const syntheticEntry = nid;
|
|
47
|
+
const reachable = bfs(syntheticEntry, subEdges);
|
|
48
|
+
// Include all component members even if not forward-reachable
|
|
49
|
+
component.forEach(id => reachable.add(id));
|
|
50
|
+
flows.push({ id: syntheticEntry, entryPoint: syntheticEntry, nodeIds: [...reachable] });
|
|
51
|
+
reachable.forEach(id => visited.add(id));
|
|
52
|
+
}
|
|
53
|
+
remaining = new Set([...remaining].filter(id => !visited.has(id)));
|
|
54
|
+
}
|
|
55
|
+
// 4. True orphans — zero edges in the full graph
|
|
56
|
+
const orphans = [...remaining];
|
|
57
|
+
return { flows, orphans };
|
|
58
|
+
}
|
|
59
|
+
function findEntryIds(nodeIds, edges) {
|
|
60
|
+
const calledIds = new Set();
|
|
61
|
+
const callerIds = new Set();
|
|
62
|
+
for (const e of edges) {
|
|
63
|
+
if (nodeIds.has(e.from) && nodeIds.has(e.to)) {
|
|
64
|
+
calledIds.add(e.to);
|
|
65
|
+
callerIds.add(e.from);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return [...nodeIds].filter(id => !calledIds.has(id) && callerIds.has(id));
|
|
69
|
+
}
|
|
70
|
+
function bfs(startId, edges) {
|
|
71
|
+
const visited = new Set([startId]);
|
|
72
|
+
const queue = [startId];
|
|
73
|
+
while (queue.length) {
|
|
74
|
+
const current = queue.shift();
|
|
75
|
+
for (const edge of edges) {
|
|
76
|
+
if (edge.from === current && !visited.has(edge.to)) {
|
|
77
|
+
visited.add(edge.to);
|
|
78
|
+
queue.push(edge.to);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return visited;
|
|
83
|
+
}
|
|
84
|
+
function bfsBidirectional(startId, edges) {
|
|
85
|
+
const visited = new Set([startId]);
|
|
86
|
+
const queue = [startId];
|
|
87
|
+
while (queue.length) {
|
|
88
|
+
const current = queue.shift();
|
|
89
|
+
for (const edge of edges) {
|
|
90
|
+
if (edge.from === current && !visited.has(edge.to)) {
|
|
91
|
+
visited.add(edge.to);
|
|
92
|
+
queue.push(edge.to);
|
|
93
|
+
}
|
|
94
|
+
if (edge.to === current && !visited.has(edge.from)) {
|
|
95
|
+
visited.add(edge.from);
|
|
96
|
+
queue.push(edge.from);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return visited;
|
|
101
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./types"), exports);
|
|
18
|
+
__exportStar(require("./analyzer/treeSitter"), exports);
|
|
19
|
+
__exportStar(require("./analyzer/index"), exports);
|
|
20
|
+
__exportStar(require("./graph/callGraphBuilder"), exports);
|
|
21
|
+
__exportStar(require("./graph/entryPointDetector"), exports);
|
|
22
|
+
__exportStar(require("./graph/flowPartitioner"), exports);
|