@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.
@@ -0,0 +1,275 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.jsxAnalyzer = void 0;
4
+ const extractorUtils_1 = require("../extractorUtils");
5
+ exports.jsxAnalyzer = {
6
+ functionQuery: `
7
+ (function_declaration
8
+ name: (identifier) @fn.name
9
+ parameters: (formal_parameters) @fn.params) @fn.decl
10
+
11
+ (lexical_declaration
12
+ (variable_declarator
13
+ name: (identifier) @fn.name
14
+ value: [(arrow_function) (function_expression)] @fn.decl))
15
+
16
+ (export_statement
17
+ declaration: (lexical_declaration
18
+ (variable_declarator
19
+ name: (identifier) @fn.name
20
+ value: [(arrow_function) (function_expression)] @fn.decl)))
21
+
22
+ (method_definition
23
+ name: (property_identifier) @fn.name
24
+ parameters: (formal_parameters) @fn.params) @fn.decl
25
+
26
+ (call_expression
27
+ function: (identifier) @hook.name
28
+ arguments: (arguments
29
+ [(arrow_function) (function_expression)] @fn.decl)
30
+ (#match? @hook.name "^use"))
31
+
32
+ (generator_function_declaration
33
+ name: (identifier) @fn.name
34
+ parameters: (formal_parameters) @fn.params) @fn.decl
35
+
36
+ (lexical_declaration
37
+ (variable_declarator
38
+ name: (identifier) @fn.name
39
+ value: (generator_function) @fn.decl))
40
+
41
+ (export_statement
42
+ declaration: (lexical_declaration
43
+ (variable_declarator
44
+ name: (identifier) @fn.name
45
+ value: (generator_function) @fn.decl)))
46
+
47
+ (function_expression
48
+ name: (identifier) @fn.name
49
+ parameters: (formal_parameters) @fn.params) @fn.decl
50
+
51
+ (lexical_declaration
52
+ (variable_declarator
53
+ name: (identifier) @fn.name
54
+ value: (call_expression
55
+ function: [(parenthesized_expression
56
+ [(function_expression)(arrow_function)] @fn.decl)
57
+ (arrow_function) @fn.decl
58
+ (function_expression) @fn.decl])))
59
+
60
+ (expression_statement
61
+ (call_expression
62
+ function: (parenthesized_expression
63
+ [(function_expression)(arrow_function)] @fn.decl)))
64
+
65
+ (assignment_expression
66
+ left: (member_expression
67
+ object: (identifier) @worker.obj
68
+ property: (property_identifier) @worker.prop)
69
+ right: [(arrow_function) (function_expression)] @worker.fn
70
+ (#match? @worker.obj "^self$")
71
+ (#match? @worker.prop "^onmessage$"))
72
+
73
+ (call_expression
74
+ function: (member_expression
75
+ object: (identifier) @worker.obj
76
+ property: (property_identifier) @worker.method)
77
+ arguments: (arguments
78
+ (string) @worker.event
79
+ [(arrow_function) (function_expression)] @worker.fn)
80
+ (#match? @worker.obj "^self$")
81
+ (#match? @worker.method "^addEventListener$")
82
+ (#match? @worker.event "message"))
83
+ `,
84
+ callQuery: `
85
+ (call_expression
86
+ function: [(identifier)(member_expression)] @call.name) @call.expr
87
+
88
+ (jsx_element
89
+ open_tag: (jsx_opening_element
90
+ name: (identifier) @call.name
91
+ (#match? @call.name "^[A-Z]"))) @call.expr
92
+
93
+ (jsx_self_closing_element
94
+ name: (identifier) @call.name
95
+ (#match? @call.name "^[A-Z]")) @call.expr
96
+
97
+ (new_expression
98
+ constructor: (identifier) @call.name) @call.expr
99
+
100
+ (call_expression
101
+ function: (member_expression
102
+ property: (property_identifier) @call.name
103
+ (#match? @call.name "^(then|catch|finally)$"))) @call.expr
104
+ `,
105
+ extractFunction(match, filePath, languageId) {
106
+ const declCapture = match.captures.find((c) => c.name === 'fn.decl');
107
+ if (!declCapture) {
108
+ const workerFn = match.captures.find((c) => c.name === 'worker.fn');
109
+ if (workerFn) {
110
+ const workerProp = match.captures.find((c) => c.name === 'worker.prop');
111
+ const node = workerFn.node;
112
+ const rawName = workerProp ? 'onmessage_handler' : `message_handler_${node.startPosition.row}`;
113
+ const paramsList = (0, extractorUtils_1.getParamsFromFunctionNode)(node, false);
114
+ return {
115
+ id: `${filePath}::${rawName}::${node.startPosition.row}`,
116
+ name: rawName,
117
+ filePath,
118
+ startLine: node.startPosition.row,
119
+ endLine: node.endPosition.row,
120
+ params: paramsList,
121
+ returnType: null,
122
+ isAsync: (0, extractorUtils_1.checkIsAsync)(node),
123
+ isExported: false,
124
+ isEntryPoint: true,
125
+ language: languageId,
126
+ kind: 'function'
127
+ };
128
+ }
129
+ return null;
130
+ }
131
+ const nameCapture = match.captures.find((c) => c.name === 'fn.name');
132
+ const hookNameCapture = match.captures.find((c) => c.name === 'hook.name');
133
+ let rawName = "anonymous";
134
+ let kind = 'function';
135
+ let parentFunctionId;
136
+ if (nameCapture) {
137
+ rawName = nameCapture.node.text;
138
+ if (declCapture.node.type === 'function_expression' && nameCapture.node.parent?.id === declCapture.node.id) {
139
+ if (declCapture.node.parent?.type === 'variable_declarator') {
140
+ return null;
141
+ }
142
+ parentFunctionId = (0, extractorUtils_1.getEnclosingFunctionId)(declCapture.node, filePath) ?? undefined;
143
+ }
144
+ if (declCapture.node.type === 'method_definition') {
145
+ kind = 'method';
146
+ }
147
+ else if (rawName.length > 0 && rawName[0] === rawName[0].toUpperCase())
148
+ kind = 'component';
149
+ }
150
+ else if (hookNameCapture) {
151
+ const startLine = declCapture.node.startPosition.row;
152
+ rawName = `${hookNameCapture.node.text}_callback_${startLine}`;
153
+ kind = 'hook';
154
+ }
155
+ // Detect IIFE: fn.decl is inside a call_expression (the IIFE invocation)
156
+ const declParent = declCapture.node.parent;
157
+ const isParenWrapped = declParent?.type === 'parenthesized_expression';
158
+ const callParent = isParenWrapped ? declParent.parent : declParent;
159
+ if (callParent?.type === 'call_expression') {
160
+ if (rawName === 'anonymous') {
161
+ rawName = `iife_${declCapture.node.startPosition.row}`;
162
+ }
163
+ kind = 'iife';
164
+ }
165
+ else if (rawName === "anonymous" && !hookNameCapture) {
166
+ return null;
167
+ }
168
+ // Prefix with enclosing scope chain (parent functions, classes, and object literals)
169
+ const scopeChain = (0, extractorUtils_1.getEnclosingScopeChain)(declCapture.node, extractorUtils_1.JS_FN_SCOPE_TYPES, extractorUtils_1.JS_CLASS_SCOPE_TYPES);
170
+ if (scopeChain.length > 0) {
171
+ rawName = `${scopeChain.map(s => s.name).join('.')}.${rawName}`;
172
+ }
173
+ const paramsCapture = match.captures.find((c) => c.name === 'fn.params');
174
+ const isExported = (0, extractorUtils_1.checkExportedByParentWalk)(declCapture.node);
175
+ const paramsList = paramsCapture
176
+ ? (0, extractorUtils_1.buildJsParams)(paramsCapture.node, false)
177
+ : (0, extractorUtils_1.getParamsFromFunctionNode)(declCapture.node, false);
178
+ let finalReturnType = null;
179
+ const returnValues = (0, extractorUtils_1.getReturnValues)(declCapture.node, extractorUtils_1.JS_NESTED_FN_TYPES);
180
+ if (returnValues.length > 0) {
181
+ finalReturnType = returnValues.join(' | ');
182
+ }
183
+ return {
184
+ id: `${filePath}::${rawName}::${declCapture.node.startPosition.row}`,
185
+ name: rawName,
186
+ filePath,
187
+ startLine: declCapture.node.startPosition.row,
188
+ endLine: declCapture.node.endPosition.row,
189
+ params: paramsList,
190
+ returnType: finalReturnType,
191
+ isAsync: (0, extractorUtils_1.checkIsAsync)(declCapture.node),
192
+ isExported,
193
+ isEntryPoint: false,
194
+ language: languageId,
195
+ kind,
196
+ ...(parentFunctionId ? { parentFunctionId } : {})
197
+ };
198
+ },
199
+ extractCall(match, filePath) {
200
+ const nameCapture = match.captures.find((c) => c.name === 'call.name');
201
+ const exprCapture = match.captures.find((c) => c.name === 'call.expr');
202
+ if (nameCapture && exprCapture) {
203
+ let calleeName = nameCapture.node.text;
204
+ if (nameCapture.node.type === 'member_expression') {
205
+ const prop = nameCapture.node.childForFieldName('property');
206
+ if (prop)
207
+ calleeName = prop.text;
208
+ }
209
+ // Unwrap .bind(): handleWorkerMessage.bind(this) → ref to handleWorkerMessage
210
+ if (calleeName === 'bind' && nameCapture.node.type === 'member_expression') {
211
+ const obj = nameCapture.node.childForFieldName('object');
212
+ if (obj?.type === 'identifier') {
213
+ return {
214
+ callerFilePath: filePath,
215
+ calleeName: obj.text,
216
+ line: exprCapture.node.startPosition.row,
217
+ callType: (0, extractorUtils_1.isInsidePromiseCombinator)(exprCapture.node) ? 'concurrent' : 'ref',
218
+ isRef: true
219
+ };
220
+ }
221
+ }
222
+ const callType = (0, extractorUtils_1.isInsidePromiseCombinator)(exprCapture.node) ? 'concurrent' : undefined;
223
+ const directCall = {
224
+ callerFilePath: filePath,
225
+ calleeName,
226
+ line: exprCapture.node.startPosition.row,
227
+ ...(callType ? { callType } : {})
228
+ };
229
+ // Extract function reference arguments from any call
230
+ const refCalls = [];
231
+ const callNode = exprCapture.node;
232
+ const argsNode = callNode.childForFieldName('arguments');
233
+ if (argsNode) {
234
+ for (const arg of argsNode.namedChildren) {
235
+ let refName = null;
236
+ if (arg.type === 'identifier') {
237
+ refName = arg.text;
238
+ }
239
+ else if (arg.type === 'member_expression') {
240
+ const obj = arg.childForFieldName('object');
241
+ const prop = arg.childForFieldName('property');
242
+ if (obj?.type === 'this' && prop) {
243
+ refName = prop.text;
244
+ }
245
+ }
246
+ else if (arg.type === 'call_expression') {
247
+ const fn = arg.childForFieldName('function');
248
+ if (fn?.type === 'member_expression') {
249
+ const prop = fn.childForFieldName('property');
250
+ const obj = fn.childForFieldName('object');
251
+ if (prop?.text === 'bind' && obj?.type === 'identifier') {
252
+ refName = obj.text;
253
+ }
254
+ }
255
+ }
256
+ if (refName) {
257
+ const refCallType = (0, extractorUtils_1.isInsidePromiseCombinator)(callNode) ? 'concurrent' : 'ref';
258
+ refCalls.push({
259
+ callerFilePath: filePath,
260
+ calleeName: refName,
261
+ line: callNode.startPosition.row,
262
+ callType: refCallType,
263
+ isRef: true
264
+ });
265
+ }
266
+ }
267
+ }
268
+ if (refCalls.length > 0) {
269
+ return [directCall, ...refCalls];
270
+ }
271
+ return directCall;
272
+ }
273
+ return null;
274
+ }
275
+ };
@@ -0,0 +1,2 @@
1
+ import { LanguageAnalyzer } from '../../types';
2
+ export declare const pythonAnalyzer: LanguageAnalyzer;
@@ -0,0 +1,236 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pythonAnalyzer = void 0;
4
+ const extractorUtils_1 = require("../extractorUtils");
5
+ /** Extract a function-reference name from an AST node (identifier or attribute). */
6
+ function extractFunctionRef(node) {
7
+ if (!node)
8
+ return null;
9
+ if (node.type === 'identifier')
10
+ return node.text;
11
+ if (node.type === 'attribute') {
12
+ const attr = node.childForFieldName('attribute');
13
+ return attr ? attr.text : null;
14
+ }
15
+ return null;
16
+ }
17
+ /** Get the function reference from a keyword argument by keyword name. */
18
+ function getKeywordArgRef(argsNode, keyword) {
19
+ if (!argsNode)
20
+ return null;
21
+ for (const child of argsNode.namedChildren) {
22
+ if (child.type === 'keyword_argument') {
23
+ const nameNode = child.childForFieldName('name');
24
+ if (nameNode?.text === keyword) {
25
+ return extractFunctionRef(child.childForFieldName('value'));
26
+ }
27
+ }
28
+ }
29
+ return null;
30
+ }
31
+ /** Get the function reference from the nth positional argument (skipping keyword args). */
32
+ function getPositionalArgRef(argsNode, index) {
33
+ if (!argsNode)
34
+ return null;
35
+ let pos = 0;
36
+ for (const child of argsNode.namedChildren) {
37
+ if (child.type === 'keyword_argument')
38
+ continue;
39
+ if (pos === index)
40
+ return extractFunctionRef(child);
41
+ pos++;
42
+ }
43
+ return null;
44
+ }
45
+ /** Check if a call node is a direct argument to asyncio.gather(). */
46
+ function isInsideAsyncioGather(node) {
47
+ const parent = node.parent;
48
+ if (!parent || parent.type !== 'argument_list')
49
+ return false;
50
+ const callNode = parent.parent;
51
+ if (!callNode || callNode.type !== 'call')
52
+ return false;
53
+ const fn = callNode.childForFieldName('function');
54
+ if (fn?.type === 'attribute') {
55
+ const obj = fn.childForFieldName('object');
56
+ const attr = fn.childForFieldName('attribute');
57
+ return obj?.text === 'asyncio' && attr?.text === 'gather';
58
+ }
59
+ return false;
60
+ }
61
+ /** Check if a call node is the argument to asyncio.create_task(). */
62
+ function isArgumentToCreateTask(node) {
63
+ const parent = node.parent;
64
+ if (!parent || parent.type !== 'argument_list')
65
+ return false;
66
+ const callNode = parent.parent;
67
+ if (!callNode || callNode.type !== 'call')
68
+ return false;
69
+ const fn = callNode.childForFieldName('function');
70
+ if (fn?.type === 'attribute') {
71
+ const obj = fn.childForFieldName('object');
72
+ const attr = fn.childForFieldName('attribute');
73
+ return obj?.text === 'asyncio' && attr?.text === 'create_task';
74
+ }
75
+ return false;
76
+ }
77
+ exports.pythonAnalyzer = {
78
+ functionQuery: `
79
+ (function_definition
80
+ name: (identifier) @fn.name
81
+ parameters: (parameters) @fn.params
82
+ return_type: (type)? @fn.return_type) @fn.decl
83
+
84
+ `,
85
+ callQuery: `
86
+ (call
87
+ function: [(identifier) (attribute attribute: (identifier))] @call.name) @call.expr
88
+ `,
89
+ extractFunction(match, filePath, languageId) {
90
+ const declCapture = match.captures.find((c) => c.name === 'fn.decl');
91
+ if (!declCapture)
92
+ return null;
93
+ const nameCapture = match.captures.find((c) => c.name === 'fn.name');
94
+ let fnName = "anonymous";
95
+ let kind = 'function';
96
+ if (nameCapture) {
97
+ fnName = nameCapture.node.text;
98
+ }
99
+ else {
100
+ return null;
101
+ }
102
+ // Check for enclosing scopes (classes and parent functions)
103
+ const scopeChain = (0, extractorUtils_1.getEnclosingScopeChain)(declCapture.node, ['function_definition'], ['class_definition']);
104
+ if (scopeChain.length > 0) {
105
+ fnName = `${scopeChain.map(s => s.name).join('.')}.${fnName}`;
106
+ if (scopeChain[scopeChain.length - 1].type === 'class') {
107
+ kind = 'method';
108
+ }
109
+ }
110
+ const paramsCapture = match.captures.find((c) => c.name === 'fn.params');
111
+ const returnCapture = match.captures.find((c) => c.name === 'fn.return_type');
112
+ // Python doesn't have explicit export keywords, but names not starting with _ are generally public API
113
+ const originalName = nameCapture.node.text;
114
+ const isExported = !originalName.startsWith('_');
115
+ // Parse parameters properly into name/type objects
116
+ const paramsList = [];
117
+ if (paramsCapture && paramsCapture.node) {
118
+ for (const child of paramsCapture.node.children) {
119
+ if (child.type === 'identifier') {
120
+ paramsList.push({ name: child.text, type: null });
121
+ }
122
+ else if (child.type === 'typed_parameter') {
123
+ const pName = child.childForFieldName('name') || child.children[0];
124
+ const pType = child.childForFieldName('type') || child.children[2];
125
+ paramsList.push({
126
+ name: pName ? pName.text : child.text,
127
+ type: pType ? pType.text : null
128
+ });
129
+ }
130
+ else if (child.type === 'default_parameter') {
131
+ const pName = child.childForFieldName('name') || child.children[0];
132
+ paramsList.push({
133
+ name: pName ? pName.text : child.text,
134
+ type: null
135
+ });
136
+ }
137
+ else if (child.type === 'typed_default_parameter') {
138
+ const pName = child.childForFieldName('name') || child.children[0];
139
+ const pType = child.childForFieldName('type') || child.children[2];
140
+ paramsList.push({
141
+ name: pName ? pName.text : child.text,
142
+ type: pType ? pType.text : null
143
+ });
144
+ }
145
+ else if (child.type === 'list_splat_pattern') {
146
+ const nameNode = child.namedChildren.find((c) => c.type === 'identifier');
147
+ if (nameNode) {
148
+ paramsList.push({ name: `*${nameNode.text}`, type: null });
149
+ }
150
+ }
151
+ else if (child.type === 'dictionary_splat_pattern') {
152
+ const nameNode = child.namedChildren.find((c) => c.type === 'identifier');
153
+ if (nameNode) {
154
+ paramsList.push({ name: `**${nameNode.text}`, type: null });
155
+ }
156
+ }
157
+ }
158
+ }
159
+ // Strip Python implementation-detail parameters
160
+ const filteredParams = paramsList.filter(p => p.name !== 'self' && p.name !== 'cls');
161
+ let finalReturnType = returnCapture ? returnCapture.node.text : null;
162
+ if (!finalReturnType) {
163
+ const returnValues = (0, extractorUtils_1.getReturnValues)(declCapture.node, extractorUtils_1.PYTHON_NESTED_FN_TYPES);
164
+ if (returnValues.length > 0) {
165
+ finalReturnType = returnValues.join(' | ');
166
+ }
167
+ }
168
+ return {
169
+ id: `${filePath}::${fnName}::${declCapture.node.startPosition.row}`,
170
+ name: fnName,
171
+ filePath,
172
+ startLine: declCapture.node.startPosition.row,
173
+ endLine: declCapture.node.endPosition.row,
174
+ params: filteredParams,
175
+ returnType: finalReturnType,
176
+ isAsync: (0, extractorUtils_1.checkIsAsync)(declCapture.node),
177
+ isExported,
178
+ isEntryPoint: false,
179
+ language: languageId,
180
+ kind
181
+ };
182
+ },
183
+ extractCall(match, filePath) {
184
+ const nameCapture = match.captures.find((c) => c.name === 'call.name');
185
+ const exprCapture = match.captures.find((c) => c.name === 'call.expr');
186
+ if (!nameCapture || !exprCapture)
187
+ return null;
188
+ const nameNode = nameCapture.node;
189
+ const callNode = exprCapture.node;
190
+ const isMethodCall = nameNode.type === 'attribute';
191
+ let calleeName = nameNode.text;
192
+ if (isMethodCall) {
193
+ const attr = nameNode.childForFieldName('attribute');
194
+ if (attr)
195
+ calleeName = attr.text;
196
+ }
197
+ const argsNode = callNode.childForFieldName('arguments');
198
+ // threading.Thread(target=fn) or Thread(target=fn)
199
+ if (calleeName === 'Thread') {
200
+ const ref = getKeywordArgRef(argsNode, 'target');
201
+ if (ref) {
202
+ return { callerFilePath: filePath, calleeName: ref, line: callNode.startPosition.row, callType: 'concurrent', isRef: true };
203
+ }
204
+ }
205
+ // executor.submit(fn, ...)
206
+ if (calleeName === 'submit' && isMethodCall) {
207
+ const ref = getPositionalArgRef(argsNode, 0);
208
+ if (ref) {
209
+ return { callerFilePath: filePath, calleeName: ref, line: callNode.startPosition.row, callType: 'concurrent', isRef: true };
210
+ }
211
+ }
212
+ // executor.map(fn, iterable)
213
+ if (calleeName === 'map' && isMethodCall) {
214
+ const ref = getPositionalArgRef(argsNode, 0);
215
+ if (ref) {
216
+ return { callerFilePath: filePath, calleeName: ref, line: callNode.startPosition.row, callType: 'concurrent', isRef: true };
217
+ }
218
+ }
219
+ // loop.run_in_executor(executor, fn, ...)
220
+ if (calleeName === 'run_in_executor' && isMethodCall) {
221
+ const ref = getPositionalArgRef(argsNode, 1);
222
+ if (ref) {
223
+ return { callerFilePath: filePath, calleeName: ref, line: callNode.startPosition.row, callType: 'concurrent', isRef: true };
224
+ }
225
+ }
226
+ // Calls inside asyncio.gather() or asyncio.create_task() are concurrent
227
+ let callType;
228
+ if (isInsideAsyncioGather(callNode) || isArgumentToCreateTask(callNode)) {
229
+ callType = 'concurrent';
230
+ }
231
+ const result = { callerFilePath: filePath, calleeName, line: callNode.startPosition.row };
232
+ if (callType)
233
+ result.callType = callType;
234
+ return result;
235
+ }
236
+ };
@@ -0,0 +1,2 @@
1
+ import { LanguageAnalyzer } from '../../types';
2
+ export declare const tsxAnalyzer: LanguageAnalyzer;