@optave/codegraph 2.0.0 → 2.1.1-dev.00f091c

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,230 @@
1
+ import { nodeEndLine } from './helpers.js';
2
+
3
+ /**
4
+ * Extract symbols from Java files.
5
+ */
6
+ export function extractJavaSymbols(tree, _filePath) {
7
+ const definitions = [];
8
+ const calls = [];
9
+ const imports = [];
10
+ const classes = [];
11
+ const exports = [];
12
+
13
+ function findJavaParentClass(node) {
14
+ let current = node.parent;
15
+ while (current) {
16
+ if (
17
+ current.type === 'class_declaration' ||
18
+ current.type === 'enum_declaration' ||
19
+ current.type === 'interface_declaration'
20
+ ) {
21
+ const nameNode = current.childForFieldName('name');
22
+ return nameNode ? nameNode.text : null;
23
+ }
24
+ current = current.parent;
25
+ }
26
+ return null;
27
+ }
28
+
29
+ function walkJavaNode(node) {
30
+ switch (node.type) {
31
+ case 'class_declaration': {
32
+ const nameNode = node.childForFieldName('name');
33
+ if (nameNode) {
34
+ definitions.push({
35
+ name: nameNode.text,
36
+ kind: 'class',
37
+ line: node.startPosition.row + 1,
38
+ endLine: nodeEndLine(node),
39
+ });
40
+
41
+ const superclass = node.childForFieldName('superclass');
42
+ if (superclass) {
43
+ for (let i = 0; i < superclass.childCount; i++) {
44
+ const child = superclass.child(i);
45
+ if (
46
+ child &&
47
+ (child.type === 'type_identifier' ||
48
+ child.type === 'identifier' ||
49
+ child.type === 'generic_type')
50
+ ) {
51
+ const superName = child.type === 'generic_type' ? child.child(0)?.text : child.text;
52
+ if (superName)
53
+ classes.push({
54
+ name: nameNode.text,
55
+ extends: superName,
56
+ line: node.startPosition.row + 1,
57
+ });
58
+ break;
59
+ }
60
+ }
61
+ }
62
+
63
+ const interfaces = node.childForFieldName('interfaces');
64
+ if (interfaces) {
65
+ for (let i = 0; i < interfaces.childCount; i++) {
66
+ const child = interfaces.child(i);
67
+ if (
68
+ child &&
69
+ (child.type === 'type_identifier' ||
70
+ child.type === 'identifier' ||
71
+ child.type === 'type_list' ||
72
+ child.type === 'generic_type')
73
+ ) {
74
+ if (child.type === 'type_list') {
75
+ for (let j = 0; j < child.childCount; j++) {
76
+ const t = child.child(j);
77
+ if (
78
+ t &&
79
+ (t.type === 'type_identifier' ||
80
+ t.type === 'identifier' ||
81
+ t.type === 'generic_type')
82
+ ) {
83
+ const ifaceName = t.type === 'generic_type' ? t.child(0)?.text : t.text;
84
+ if (ifaceName)
85
+ classes.push({
86
+ name: nameNode.text,
87
+ implements: ifaceName,
88
+ line: node.startPosition.row + 1,
89
+ });
90
+ }
91
+ }
92
+ } else {
93
+ const ifaceName =
94
+ child.type === 'generic_type' ? child.child(0)?.text : child.text;
95
+ if (ifaceName)
96
+ classes.push({
97
+ name: nameNode.text,
98
+ implements: ifaceName,
99
+ line: node.startPosition.row + 1,
100
+ });
101
+ }
102
+ }
103
+ }
104
+ }
105
+ }
106
+ break;
107
+ }
108
+
109
+ case 'interface_declaration': {
110
+ const nameNode = node.childForFieldName('name');
111
+ if (nameNode) {
112
+ definitions.push({
113
+ name: nameNode.text,
114
+ kind: 'interface',
115
+ line: node.startPosition.row + 1,
116
+ endLine: nodeEndLine(node),
117
+ });
118
+ const body = node.childForFieldName('body');
119
+ if (body) {
120
+ for (let i = 0; i < body.childCount; i++) {
121
+ const child = body.child(i);
122
+ if (child && child.type === 'method_declaration') {
123
+ const methName = child.childForFieldName('name');
124
+ if (methName) {
125
+ definitions.push({
126
+ name: `${nameNode.text}.${methName.text}`,
127
+ kind: 'method',
128
+ line: child.startPosition.row + 1,
129
+ endLine: child.endPosition.row + 1,
130
+ });
131
+ }
132
+ }
133
+ }
134
+ }
135
+ }
136
+ break;
137
+ }
138
+
139
+ case 'enum_declaration': {
140
+ const nameNode = node.childForFieldName('name');
141
+ if (nameNode) {
142
+ definitions.push({
143
+ name: nameNode.text,
144
+ kind: 'enum',
145
+ line: node.startPosition.row + 1,
146
+ endLine: nodeEndLine(node),
147
+ });
148
+ }
149
+ break;
150
+ }
151
+
152
+ case 'method_declaration': {
153
+ const nameNode = node.childForFieldName('name');
154
+ if (nameNode) {
155
+ const parentClass = findJavaParentClass(node);
156
+ const fullName = parentClass ? `${parentClass}.${nameNode.text}` : nameNode.text;
157
+ definitions.push({
158
+ name: fullName,
159
+ kind: 'method',
160
+ line: node.startPosition.row + 1,
161
+ endLine: nodeEndLine(node),
162
+ });
163
+ }
164
+ break;
165
+ }
166
+
167
+ case 'constructor_declaration': {
168
+ const nameNode = node.childForFieldName('name');
169
+ if (nameNode) {
170
+ const parentClass = findJavaParentClass(node);
171
+ const fullName = parentClass ? `${parentClass}.${nameNode.text}` : nameNode.text;
172
+ definitions.push({
173
+ name: fullName,
174
+ kind: 'method',
175
+ line: node.startPosition.row + 1,
176
+ endLine: nodeEndLine(node),
177
+ });
178
+ }
179
+ break;
180
+ }
181
+
182
+ case 'import_declaration': {
183
+ for (let i = 0; i < node.childCount; i++) {
184
+ const child = node.child(i);
185
+ if (child && (child.type === 'scoped_identifier' || child.type === 'identifier')) {
186
+ const fullPath = child.text;
187
+ const lastName = fullPath.split('.').pop();
188
+ imports.push({
189
+ source: fullPath,
190
+ names: [lastName],
191
+ line: node.startPosition.row + 1,
192
+ javaImport: true,
193
+ });
194
+ }
195
+ if (child && child.type === 'asterisk') {
196
+ const lastImport = imports[imports.length - 1];
197
+ if (lastImport) lastImport.names = ['*'];
198
+ }
199
+ }
200
+ break;
201
+ }
202
+
203
+ case 'method_invocation': {
204
+ const nameNode = node.childForFieldName('name');
205
+ if (nameNode) {
206
+ const obj = node.childForFieldName('object');
207
+ const call = { name: nameNode.text, line: node.startPosition.row + 1 };
208
+ if (obj) call.receiver = obj.text;
209
+ calls.push(call);
210
+ }
211
+ break;
212
+ }
213
+
214
+ case 'object_creation_expression': {
215
+ const typeNode = node.childForFieldName('type');
216
+ if (typeNode) {
217
+ const typeName =
218
+ typeNode.type === 'generic_type' ? typeNode.child(0)?.text : typeNode.text;
219
+ if (typeName) calls.push({ name: typeName, line: node.startPosition.row + 1 });
220
+ }
221
+ break;
222
+ }
223
+ }
224
+
225
+ for (let i = 0; i < node.childCount; i++) walkJavaNode(node.child(i));
226
+ }
227
+
228
+ walkJavaNode(tree.rootNode);
229
+ return { definitions, calls, imports, classes, exports };
230
+ }
@@ -0,0 +1,414 @@
1
+ import { findChild, nodeEndLine } from './helpers.js';
2
+
3
+ /**
4
+ * Extract symbols from a JS/TS parsed AST.
5
+ */
6
+ export function extractSymbols(tree, _filePath) {
7
+ const definitions = [];
8
+ const calls = [];
9
+ const imports = [];
10
+ const classes = [];
11
+ const exports = [];
12
+
13
+ function walkJavaScriptNode(node) {
14
+ switch (node.type) {
15
+ case 'function_declaration': {
16
+ const nameNode = node.childForFieldName('name');
17
+ if (nameNode) {
18
+ definitions.push({
19
+ name: nameNode.text,
20
+ kind: 'function',
21
+ line: node.startPosition.row + 1,
22
+ endLine: nodeEndLine(node),
23
+ });
24
+ }
25
+ break;
26
+ }
27
+
28
+ case 'class_declaration': {
29
+ const nameNode = node.childForFieldName('name');
30
+ if (nameNode) {
31
+ const cls = {
32
+ name: nameNode.text,
33
+ kind: 'class',
34
+ line: node.startPosition.row + 1,
35
+ endLine: nodeEndLine(node),
36
+ };
37
+ definitions.push(cls);
38
+ const heritage = node.childForFieldName('heritage') || findChild(node, 'class_heritage');
39
+ if (heritage) {
40
+ const superName = extractSuperclass(heritage);
41
+ if (superName) {
42
+ classes.push({
43
+ name: nameNode.text,
44
+ extends: superName,
45
+ line: node.startPosition.row + 1,
46
+ });
47
+ }
48
+ const implementsList = extractImplements(heritage);
49
+ for (const iface of implementsList) {
50
+ classes.push({
51
+ name: nameNode.text,
52
+ implements: iface,
53
+ line: node.startPosition.row + 1,
54
+ });
55
+ }
56
+ }
57
+ }
58
+ break;
59
+ }
60
+
61
+ case 'method_definition': {
62
+ const nameNode = node.childForFieldName('name');
63
+ if (nameNode) {
64
+ const parentClass = findParentClass(node);
65
+ const fullName = parentClass ? `${parentClass}.${nameNode.text}` : nameNode.text;
66
+ definitions.push({
67
+ name: fullName,
68
+ kind: 'method',
69
+ line: node.startPosition.row + 1,
70
+ endLine: nodeEndLine(node),
71
+ });
72
+ }
73
+ break;
74
+ }
75
+
76
+ case 'interface_declaration': {
77
+ const nameNode = node.childForFieldName('name');
78
+ if (nameNode) {
79
+ definitions.push({
80
+ name: nameNode.text,
81
+ kind: 'interface',
82
+ line: node.startPosition.row + 1,
83
+ endLine: nodeEndLine(node),
84
+ });
85
+ const body =
86
+ node.childForFieldName('body') ||
87
+ findChild(node, 'interface_body') ||
88
+ findChild(node, 'object_type');
89
+ if (body) {
90
+ extractInterfaceMethods(body, nameNode.text, definitions);
91
+ }
92
+ }
93
+ break;
94
+ }
95
+
96
+ case 'type_alias_declaration': {
97
+ const nameNode = node.childForFieldName('name');
98
+ if (nameNode) {
99
+ definitions.push({
100
+ name: nameNode.text,
101
+ kind: 'type',
102
+ line: node.startPosition.row + 1,
103
+ endLine: nodeEndLine(node),
104
+ });
105
+ }
106
+ break;
107
+ }
108
+
109
+ case 'lexical_declaration':
110
+ case 'variable_declaration': {
111
+ for (let i = 0; i < node.childCount; i++) {
112
+ const declarator = node.child(i);
113
+ if (declarator && declarator.type === 'variable_declarator') {
114
+ const nameN = declarator.childForFieldName('name');
115
+ const valueN = declarator.childForFieldName('value');
116
+ if (
117
+ nameN &&
118
+ valueN &&
119
+ (valueN.type === 'arrow_function' ||
120
+ valueN.type === 'function_expression' ||
121
+ valueN.type === 'function')
122
+ ) {
123
+ definitions.push({
124
+ name: nameN.text,
125
+ kind: 'function',
126
+ line: node.startPosition.row + 1,
127
+ endLine: nodeEndLine(valueN),
128
+ });
129
+ }
130
+ }
131
+ }
132
+ break;
133
+ }
134
+
135
+ case 'call_expression': {
136
+ const fn = node.childForFieldName('function');
137
+ if (fn) {
138
+ const callInfo = extractCallInfo(fn, node);
139
+ if (callInfo) {
140
+ calls.push(callInfo);
141
+ }
142
+ }
143
+ break;
144
+ }
145
+
146
+ case 'import_statement': {
147
+ const isTypeOnly = node.text.startsWith('import type');
148
+ const source = node.childForFieldName('source') || findChild(node, 'string');
149
+ if (source) {
150
+ const modPath = source.text.replace(/['"]/g, '');
151
+ const names = extractImportNames(node);
152
+ imports.push({
153
+ source: modPath,
154
+ names,
155
+ line: node.startPosition.row + 1,
156
+ typeOnly: isTypeOnly,
157
+ });
158
+ }
159
+ break;
160
+ }
161
+
162
+ case 'export_statement': {
163
+ const decl = node.childForFieldName('declaration');
164
+ if (decl) {
165
+ if (decl.type === 'function_declaration') {
166
+ const n = decl.childForFieldName('name');
167
+ if (n)
168
+ exports.push({ name: n.text, kind: 'function', line: node.startPosition.row + 1 });
169
+ } else if (decl.type === 'class_declaration') {
170
+ const n = decl.childForFieldName('name');
171
+ if (n) exports.push({ name: n.text, kind: 'class', line: node.startPosition.row + 1 });
172
+ } else if (decl.type === 'interface_declaration') {
173
+ const n = decl.childForFieldName('name');
174
+ if (n)
175
+ exports.push({ name: n.text, kind: 'interface', line: node.startPosition.row + 1 });
176
+ } else if (decl.type === 'type_alias_declaration') {
177
+ const n = decl.childForFieldName('name');
178
+ if (n) exports.push({ name: n.text, kind: 'type', line: node.startPosition.row + 1 });
179
+ }
180
+ }
181
+ const source = node.childForFieldName('source') || findChild(node, 'string');
182
+ if (source && !decl) {
183
+ const modPath = source.text.replace(/['"]/g, '');
184
+ const reexportNames = extractImportNames(node);
185
+ const isWildcard = node.text.includes('export *') || node.text.includes('export*');
186
+ imports.push({
187
+ source: modPath,
188
+ names: reexportNames,
189
+ line: node.startPosition.row + 1,
190
+ reexport: true,
191
+ wildcardReexport: isWildcard && reexportNames.length === 0,
192
+ });
193
+ }
194
+ break;
195
+ }
196
+
197
+ case 'expression_statement': {
198
+ const expr = node.child(0);
199
+ if (expr && expr.type === 'assignment_expression') {
200
+ const left = expr.childForFieldName('left');
201
+ const right = expr.childForFieldName('right');
202
+ if (left && right) {
203
+ const leftText = left.text;
204
+ if (leftText.startsWith('module.exports') || leftText === 'exports') {
205
+ if (right.type === 'call_expression') {
206
+ const fn = right.childForFieldName('function');
207
+ const args = right.childForFieldName('arguments') || findChild(right, 'arguments');
208
+ if (fn && fn.text === 'require' && args) {
209
+ const strArg = findChild(args, 'string');
210
+ if (strArg) {
211
+ const modPath = strArg.text.replace(/['"]/g, '');
212
+ imports.push({
213
+ source: modPath,
214
+ names: [],
215
+ line: node.startPosition.row + 1,
216
+ reexport: true,
217
+ wildcardReexport: true,
218
+ });
219
+ }
220
+ }
221
+ }
222
+ if (right.type === 'object') {
223
+ for (let ci = 0; ci < right.childCount; ci++) {
224
+ const child = right.child(ci);
225
+ if (child && child.type === 'spread_element') {
226
+ const spreadExpr = child.child(1) || child.childForFieldName('value');
227
+ if (spreadExpr && spreadExpr.type === 'call_expression') {
228
+ const fn2 = spreadExpr.childForFieldName('function');
229
+ const args2 =
230
+ spreadExpr.childForFieldName('arguments') ||
231
+ findChild(spreadExpr, 'arguments');
232
+ if (fn2 && fn2.text === 'require' && args2) {
233
+ const strArg2 = findChild(args2, 'string');
234
+ if (strArg2) {
235
+ const modPath2 = strArg2.text.replace(/['"]/g, '');
236
+ imports.push({
237
+ source: modPath2,
238
+ names: [],
239
+ line: node.startPosition.row + 1,
240
+ reexport: true,
241
+ wildcardReexport: true,
242
+ });
243
+ }
244
+ }
245
+ }
246
+ }
247
+ }
248
+ }
249
+ }
250
+ }
251
+ }
252
+ break;
253
+ }
254
+ }
255
+
256
+ for (let i = 0; i < node.childCount; i++) {
257
+ walkJavaScriptNode(node.child(i));
258
+ }
259
+ }
260
+
261
+ walkJavaScriptNode(tree.rootNode);
262
+ return { definitions, calls, imports, classes, exports };
263
+ }
264
+
265
+ function extractInterfaceMethods(bodyNode, interfaceName, definitions) {
266
+ for (let i = 0; i < bodyNode.childCount; i++) {
267
+ const child = bodyNode.child(i);
268
+ if (!child) continue;
269
+ if (child.type === 'method_signature' || child.type === 'property_signature') {
270
+ const nameNode = child.childForFieldName('name');
271
+ if (nameNode) {
272
+ definitions.push({
273
+ name: `${interfaceName}.${nameNode.text}`,
274
+ kind: 'method',
275
+ line: child.startPosition.row + 1,
276
+ endLine: child.endPosition.row + 1,
277
+ });
278
+ }
279
+ }
280
+ }
281
+ }
282
+
283
+ function extractImplements(heritage) {
284
+ const interfaces = [];
285
+ for (let i = 0; i < heritage.childCount; i++) {
286
+ const child = heritage.child(i);
287
+ if (!child) continue;
288
+ if (child.text === 'implements') {
289
+ for (let j = i + 1; j < heritage.childCount; j++) {
290
+ const next = heritage.child(j);
291
+ if (!next) continue;
292
+ if (next.type === 'identifier') interfaces.push(next.text);
293
+ else if (next.type === 'type_identifier') interfaces.push(next.text);
294
+ if (next.childCount > 0) interfaces.push(...extractImplementsFromNode(next));
295
+ }
296
+ break;
297
+ }
298
+ if (child.type === 'implements_clause') {
299
+ interfaces.push(...extractImplementsFromNode(child));
300
+ }
301
+ }
302
+ return interfaces;
303
+ }
304
+
305
+ function extractImplementsFromNode(node) {
306
+ const result = [];
307
+ for (let i = 0; i < node.childCount; i++) {
308
+ const child = node.child(i);
309
+ if (!child) continue;
310
+ if (child.type === 'identifier' || child.type === 'type_identifier') result.push(child.text);
311
+ if (child.childCount > 0) result.push(...extractImplementsFromNode(child));
312
+ }
313
+ return result;
314
+ }
315
+
316
+ function extractReceiverName(objNode) {
317
+ if (!objNode) return undefined;
318
+ if (objNode.type === 'identifier') return objNode.text;
319
+ if (objNode.type === 'this') return 'this';
320
+ if (objNode.type === 'super') return 'super';
321
+ if (objNode.type === 'member_expression') {
322
+ const prop = objNode.childForFieldName('property');
323
+ if (prop) return objNode.text;
324
+ }
325
+ return objNode.text;
326
+ }
327
+
328
+ function extractCallInfo(fn, callNode) {
329
+ if (fn.type === 'identifier') {
330
+ return { name: fn.text, line: callNode.startPosition.row + 1 };
331
+ }
332
+
333
+ if (fn.type === 'member_expression') {
334
+ const obj = fn.childForFieldName('object');
335
+ const prop = fn.childForFieldName('property');
336
+ if (!prop) return null;
337
+
338
+ if (prop.text === 'call' || prop.text === 'apply' || prop.text === 'bind') {
339
+ if (obj && obj.type === 'identifier')
340
+ return { name: obj.text, line: callNode.startPosition.row + 1, dynamic: true };
341
+ if (obj && obj.type === 'member_expression') {
342
+ const innerProp = obj.childForFieldName('property');
343
+ if (innerProp)
344
+ return { name: innerProp.text, line: callNode.startPosition.row + 1, dynamic: true };
345
+ }
346
+ }
347
+
348
+ if (prop.type === 'string' || prop.type === 'string_fragment') {
349
+ const methodName = prop.text.replace(/['"]/g, '');
350
+ if (methodName) {
351
+ const receiver = extractReceiverName(obj);
352
+ return { name: methodName, line: callNode.startPosition.row + 1, dynamic: true, receiver };
353
+ }
354
+ }
355
+
356
+ const receiver = extractReceiverName(obj);
357
+ return { name: prop.text, line: callNode.startPosition.row + 1, receiver };
358
+ }
359
+
360
+ if (fn.type === 'subscript_expression') {
361
+ const obj = fn.childForFieldName('object');
362
+ const index = fn.childForFieldName('index');
363
+ if (index && (index.type === 'string' || index.type === 'template_string')) {
364
+ const methodName = index.text.replace(/['"`]/g, '');
365
+ if (methodName && !methodName.includes('$')) {
366
+ const receiver = extractReceiverName(obj);
367
+ return { name: methodName, line: callNode.startPosition.row + 1, dynamic: true, receiver };
368
+ }
369
+ }
370
+ }
371
+
372
+ return null;
373
+ }
374
+
375
+ function extractSuperclass(heritage) {
376
+ for (let i = 0; i < heritage.childCount; i++) {
377
+ const child = heritage.child(i);
378
+ if (child.type === 'identifier') return child.text;
379
+ if (child.type === 'member_expression') return child.text;
380
+ const found = extractSuperclass(child);
381
+ if (found) return found;
382
+ }
383
+ return null;
384
+ }
385
+
386
+ function findParentClass(node) {
387
+ let current = node.parent;
388
+ while (current) {
389
+ if (current.type === 'class_declaration' || current.type === 'class') {
390
+ const nameNode = current.childForFieldName('name');
391
+ return nameNode ? nameNode.text : null;
392
+ }
393
+ current = current.parent;
394
+ }
395
+ return null;
396
+ }
397
+
398
+ function extractImportNames(node) {
399
+ const names = [];
400
+ function scan(n) {
401
+ if (n.type === 'import_specifier' || n.type === 'export_specifier') {
402
+ const nameNode = n.childForFieldName('name') || n.childForFieldName('alias');
403
+ if (nameNode) names.push(nameNode.text);
404
+ else names.push(n.text);
405
+ } else if (n.type === 'identifier' && n.parent && n.parent.type === 'import_clause') {
406
+ names.push(n.text);
407
+ } else if (n.type === 'namespace_import') {
408
+ names.push(n.text);
409
+ }
410
+ for (let i = 0; i < n.childCount; i++) scan(n.child(i));
411
+ }
412
+ scan(node);
413
+ return names;
414
+ }