@optave/codegraph 2.6.0 → 3.0.1
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 +111 -54
- package/package.json +5 -5
- package/src/ast.js +418 -0
- package/src/batch.js +93 -3
- package/src/builder.js +371 -103
- package/src/cfg.js +1452 -0
- package/src/change-journal.js +130 -0
- package/src/cli.js +415 -139
- package/src/complexity.js +8 -8
- package/src/dataflow.js +1190 -0
- package/src/db.js +96 -0
- package/src/embedder.js +16 -16
- package/src/export.js +305 -0
- package/src/extractors/csharp.js +64 -1
- package/src/extractors/go.js +66 -1
- package/src/extractors/hcl.js +22 -0
- package/src/extractors/java.js +61 -1
- package/src/extractors/javascript.js +193 -0
- package/src/extractors/php.js +79 -0
- package/src/extractors/python.js +134 -0
- package/src/extractors/ruby.js +89 -0
- package/src/extractors/rust.js +71 -1
- package/src/flow.js +5 -2
- package/src/index.js +52 -4
- package/src/mcp.js +403 -222
- package/src/paginate.js +3 -3
- package/src/parser.js +24 -0
- package/src/queries.js +362 -36
- package/src/structure.js +64 -8
- package/src/viewer.js +948 -0
- package/src/watcher.js +36 -1
package/src/extractors/go.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { nodeEndLine } from './helpers.js';
|
|
1
|
+
import { findChild, nodeEndLine } from './helpers.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Extract symbols from Go files.
|
|
@@ -15,11 +15,13 @@ export function extractGoSymbols(tree, _filePath) {
|
|
|
15
15
|
case 'function_declaration': {
|
|
16
16
|
const nameNode = node.childForFieldName('name');
|
|
17
17
|
if (nameNode) {
|
|
18
|
+
const params = extractGoParameters(node.childForFieldName('parameters'));
|
|
18
19
|
definitions.push({
|
|
19
20
|
name: nameNode.text,
|
|
20
21
|
kind: 'function',
|
|
21
22
|
line: node.startPosition.row + 1,
|
|
22
23
|
endLine: nodeEndLine(node),
|
|
24
|
+
children: params.length > 0 ? params : undefined,
|
|
23
25
|
});
|
|
24
26
|
}
|
|
25
27
|
break;
|
|
@@ -46,11 +48,13 @@ export function extractGoSymbols(tree, _filePath) {
|
|
|
46
48
|
}
|
|
47
49
|
}
|
|
48
50
|
const fullName = receiverType ? `${receiverType}.${nameNode.text}` : nameNode.text;
|
|
51
|
+
const params = extractGoParameters(node.childForFieldName('parameters'));
|
|
49
52
|
definitions.push({
|
|
50
53
|
name: fullName,
|
|
51
54
|
kind: 'method',
|
|
52
55
|
line: node.startPosition.row + 1,
|
|
53
56
|
endLine: nodeEndLine(node),
|
|
57
|
+
children: params.length > 0 ? params : undefined,
|
|
54
58
|
});
|
|
55
59
|
}
|
|
56
60
|
break;
|
|
@@ -64,11 +68,13 @@ export function extractGoSymbols(tree, _filePath) {
|
|
|
64
68
|
const typeNode = spec.childForFieldName('type');
|
|
65
69
|
if (nameNode && typeNode) {
|
|
66
70
|
if (typeNode.type === 'struct_type') {
|
|
71
|
+
const fields = extractStructFields(typeNode);
|
|
67
72
|
definitions.push({
|
|
68
73
|
name: nameNode.text,
|
|
69
74
|
kind: 'struct',
|
|
70
75
|
line: node.startPosition.row + 1,
|
|
71
76
|
endLine: nodeEndLine(node),
|
|
77
|
+
children: fields.length > 0 ? fields : undefined,
|
|
72
78
|
});
|
|
73
79
|
} else if (typeNode.type === 'interface_type') {
|
|
74
80
|
definitions.push({
|
|
@@ -145,6 +151,23 @@ export function extractGoSymbols(tree, _filePath) {
|
|
|
145
151
|
break;
|
|
146
152
|
}
|
|
147
153
|
|
|
154
|
+
case 'const_declaration': {
|
|
155
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
156
|
+
const spec = node.child(i);
|
|
157
|
+
if (!spec || spec.type !== 'const_spec') continue;
|
|
158
|
+
const constName = spec.childForFieldName('name');
|
|
159
|
+
if (constName) {
|
|
160
|
+
definitions.push({
|
|
161
|
+
name: constName.text,
|
|
162
|
+
kind: 'constant',
|
|
163
|
+
line: spec.startPosition.row + 1,
|
|
164
|
+
endLine: spec.endPosition.row + 1,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
|
|
148
171
|
case 'call_expression': {
|
|
149
172
|
const fn = node.childForFieldName('function');
|
|
150
173
|
if (fn) {
|
|
@@ -170,3 +193,45 @@ export function extractGoSymbols(tree, _filePath) {
|
|
|
170
193
|
walkGoNode(tree.rootNode);
|
|
171
194
|
return { definitions, calls, imports, classes, exports };
|
|
172
195
|
}
|
|
196
|
+
|
|
197
|
+
// ── Child extraction helpers ────────────────────────────────────────────────
|
|
198
|
+
|
|
199
|
+
function extractGoParameters(paramListNode) {
|
|
200
|
+
const params = [];
|
|
201
|
+
if (!paramListNode) return params;
|
|
202
|
+
for (let i = 0; i < paramListNode.childCount; i++) {
|
|
203
|
+
const param = paramListNode.child(i);
|
|
204
|
+
if (!param || param.type !== 'parameter_declaration') continue;
|
|
205
|
+
// A parameter_declaration may have multiple identifiers (e.g., `a, b int`)
|
|
206
|
+
for (let j = 0; j < param.childCount; j++) {
|
|
207
|
+
const child = param.child(j);
|
|
208
|
+
if (child && child.type === 'identifier') {
|
|
209
|
+
params.push({ name: child.text, kind: 'parameter', line: child.startPosition.row + 1 });
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return params;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function extractStructFields(structTypeNode) {
|
|
217
|
+
const fields = [];
|
|
218
|
+
const fieldList = findChild(structTypeNode, 'field_declaration_list');
|
|
219
|
+
if (!fieldList) return fields;
|
|
220
|
+
for (let i = 0; i < fieldList.childCount; i++) {
|
|
221
|
+
const field = fieldList.child(i);
|
|
222
|
+
if (!field || field.type !== 'field_declaration') continue;
|
|
223
|
+
const nameNode = field.childForFieldName('name');
|
|
224
|
+
if (nameNode) {
|
|
225
|
+
fields.push({ name: nameNode.text, kind: 'property', line: field.startPosition.row + 1 });
|
|
226
|
+
} else {
|
|
227
|
+
// Struct fields may have multiple names or use first identifier child
|
|
228
|
+
for (let j = 0; j < field.childCount; j++) {
|
|
229
|
+
const child = field.child(j);
|
|
230
|
+
if (child && child.type === 'field_identifier') {
|
|
231
|
+
fields.push({ name: child.text, kind: 'property', line: field.startPosition.row + 1 });
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return fields;
|
|
237
|
+
}
|
package/src/extractors/hcl.js
CHANGED
|
@@ -36,11 +36,33 @@ export function extractHCLSymbols(tree, _filePath) {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
if (name) {
|
|
39
|
+
// Extract attributes as property children for variable/output blocks
|
|
40
|
+
let blockChildren;
|
|
41
|
+
if (blockType === 'variable' || blockType === 'output') {
|
|
42
|
+
blockChildren = [];
|
|
43
|
+
const body = children.find((c) => c.type === 'body');
|
|
44
|
+
if (body) {
|
|
45
|
+
for (let j = 0; j < body.childCount; j++) {
|
|
46
|
+
const attr = body.child(j);
|
|
47
|
+
if (attr && attr.type === 'attribute') {
|
|
48
|
+
const key = attr.childForFieldName('key') || attr.child(0);
|
|
49
|
+
if (key) {
|
|
50
|
+
blockChildren.push({
|
|
51
|
+
name: key.text,
|
|
52
|
+
kind: 'property',
|
|
53
|
+
line: attr.startPosition.row + 1,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
39
60
|
definitions.push({
|
|
40
61
|
name,
|
|
41
62
|
kind: blockType,
|
|
42
63
|
line: node.startPosition.row + 1,
|
|
43
64
|
endLine: nodeEndLine(node),
|
|
65
|
+
children: blockChildren?.length > 0 ? blockChildren : undefined,
|
|
44
66
|
});
|
|
45
67
|
}
|
|
46
68
|
|
package/src/extractors/java.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { nodeEndLine } from './helpers.js';
|
|
1
|
+
import { findChild, nodeEndLine } from './helpers.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Extract symbols from Java files.
|
|
@@ -31,11 +31,13 @@ export function extractJavaSymbols(tree, _filePath) {
|
|
|
31
31
|
case 'class_declaration': {
|
|
32
32
|
const nameNode = node.childForFieldName('name');
|
|
33
33
|
if (nameNode) {
|
|
34
|
+
const classChildren = extractClassFields(node);
|
|
34
35
|
definitions.push({
|
|
35
36
|
name: nameNode.text,
|
|
36
37
|
kind: 'class',
|
|
37
38
|
line: node.startPosition.row + 1,
|
|
38
39
|
endLine: nodeEndLine(node),
|
|
40
|
+
children: classChildren.length > 0 ? classChildren : undefined,
|
|
39
41
|
});
|
|
40
42
|
|
|
41
43
|
const superclass = node.childForFieldName('superclass');
|
|
@@ -139,11 +141,13 @@ export function extractJavaSymbols(tree, _filePath) {
|
|
|
139
141
|
case 'enum_declaration': {
|
|
140
142
|
const nameNode = node.childForFieldName('name');
|
|
141
143
|
if (nameNode) {
|
|
144
|
+
const enumChildren = extractEnumConstants(node);
|
|
142
145
|
definitions.push({
|
|
143
146
|
name: nameNode.text,
|
|
144
147
|
kind: 'enum',
|
|
145
148
|
line: node.startPosition.row + 1,
|
|
146
149
|
endLine: nodeEndLine(node),
|
|
150
|
+
children: enumChildren.length > 0 ? enumChildren : undefined,
|
|
147
151
|
});
|
|
148
152
|
}
|
|
149
153
|
break;
|
|
@@ -154,11 +158,13 @@ export function extractJavaSymbols(tree, _filePath) {
|
|
|
154
158
|
if (nameNode) {
|
|
155
159
|
const parentClass = findJavaParentClass(node);
|
|
156
160
|
const fullName = parentClass ? `${parentClass}.${nameNode.text}` : nameNode.text;
|
|
161
|
+
const params = extractJavaParameters(node.childForFieldName('parameters'));
|
|
157
162
|
definitions.push({
|
|
158
163
|
name: fullName,
|
|
159
164
|
kind: 'method',
|
|
160
165
|
line: node.startPosition.row + 1,
|
|
161
166
|
endLine: nodeEndLine(node),
|
|
167
|
+
children: params.length > 0 ? params : undefined,
|
|
162
168
|
});
|
|
163
169
|
}
|
|
164
170
|
break;
|
|
@@ -169,11 +175,13 @@ export function extractJavaSymbols(tree, _filePath) {
|
|
|
169
175
|
if (nameNode) {
|
|
170
176
|
const parentClass = findJavaParentClass(node);
|
|
171
177
|
const fullName = parentClass ? `${parentClass}.${nameNode.text}` : nameNode.text;
|
|
178
|
+
const params = extractJavaParameters(node.childForFieldName('parameters'));
|
|
172
179
|
definitions.push({
|
|
173
180
|
name: fullName,
|
|
174
181
|
kind: 'method',
|
|
175
182
|
line: node.startPosition.row + 1,
|
|
176
183
|
endLine: nodeEndLine(node),
|
|
184
|
+
children: params.length > 0 ? params : undefined,
|
|
177
185
|
});
|
|
178
186
|
}
|
|
179
187
|
break;
|
|
@@ -228,3 +236,55 @@ export function extractJavaSymbols(tree, _filePath) {
|
|
|
228
236
|
walkJavaNode(tree.rootNode);
|
|
229
237
|
return { definitions, calls, imports, classes, exports };
|
|
230
238
|
}
|
|
239
|
+
|
|
240
|
+
// ── Child extraction helpers ────────────────────────────────────────────────
|
|
241
|
+
|
|
242
|
+
function extractJavaParameters(paramListNode) {
|
|
243
|
+
const params = [];
|
|
244
|
+
if (!paramListNode) return params;
|
|
245
|
+
for (let i = 0; i < paramListNode.childCount; i++) {
|
|
246
|
+
const param = paramListNode.child(i);
|
|
247
|
+
if (!param) continue;
|
|
248
|
+
if (param.type === 'formal_parameter' || param.type === 'spread_parameter') {
|
|
249
|
+
const nameNode = param.childForFieldName('name');
|
|
250
|
+
if (nameNode) {
|
|
251
|
+
params.push({ name: nameNode.text, kind: 'parameter', line: param.startPosition.row + 1 });
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return params;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function extractClassFields(classNode) {
|
|
259
|
+
const fields = [];
|
|
260
|
+
const body = classNode.childForFieldName('body') || findChild(classNode, 'class_body');
|
|
261
|
+
if (!body) return fields;
|
|
262
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
263
|
+
const member = body.child(i);
|
|
264
|
+
if (!member || member.type !== 'field_declaration') continue;
|
|
265
|
+
for (let j = 0; j < member.childCount; j++) {
|
|
266
|
+
const child = member.child(j);
|
|
267
|
+
if (!child || child.type !== 'variable_declarator') continue;
|
|
268
|
+
const nameNode = child.childForFieldName('name');
|
|
269
|
+
if (nameNode) {
|
|
270
|
+
fields.push({ name: nameNode.text, kind: 'property', line: member.startPosition.row + 1 });
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return fields;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function extractEnumConstants(enumNode) {
|
|
278
|
+
const constants = [];
|
|
279
|
+
const body = enumNode.childForFieldName('body') || findChild(enumNode, 'enum_body');
|
|
280
|
+
if (!body) return constants;
|
|
281
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
282
|
+
const member = body.child(i);
|
|
283
|
+
if (!member || member.type !== 'enum_constant') continue;
|
|
284
|
+
const nameNode = member.childForFieldName('name');
|
|
285
|
+
if (nameNode) {
|
|
286
|
+
constants.push({ name: nameNode.text, kind: 'constant', line: member.startPosition.row + 1 });
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return constants;
|
|
290
|
+
}
|
|
@@ -28,31 +28,37 @@ function extractSymbolsQuery(tree, query) {
|
|
|
28
28
|
|
|
29
29
|
if (c.fn_node) {
|
|
30
30
|
// function_declaration
|
|
31
|
+
const fnChildren = extractParameters(c.fn_node);
|
|
31
32
|
definitions.push({
|
|
32
33
|
name: c.fn_name.text,
|
|
33
34
|
kind: 'function',
|
|
34
35
|
line: c.fn_node.startPosition.row + 1,
|
|
35
36
|
endLine: nodeEndLine(c.fn_node),
|
|
37
|
+
children: fnChildren.length > 0 ? fnChildren : undefined,
|
|
36
38
|
});
|
|
37
39
|
} else if (c.varfn_name) {
|
|
38
40
|
// variable_declarator with arrow_function / function_expression
|
|
39
41
|
const declNode = c.varfn_name.parent?.parent;
|
|
40
42
|
const line = declNode ? declNode.startPosition.row + 1 : c.varfn_name.startPosition.row + 1;
|
|
43
|
+
const varFnChildren = extractParameters(c.varfn_value);
|
|
41
44
|
definitions.push({
|
|
42
45
|
name: c.varfn_name.text,
|
|
43
46
|
kind: 'function',
|
|
44
47
|
line,
|
|
45
48
|
endLine: nodeEndLine(c.varfn_value),
|
|
49
|
+
children: varFnChildren.length > 0 ? varFnChildren : undefined,
|
|
46
50
|
});
|
|
47
51
|
} else if (c.cls_node) {
|
|
48
52
|
// class_declaration
|
|
49
53
|
const className = c.cls_name.text;
|
|
50
54
|
const startLine = c.cls_node.startPosition.row + 1;
|
|
55
|
+
const clsChildren = extractClassProperties(c.cls_node);
|
|
51
56
|
definitions.push({
|
|
52
57
|
name: className,
|
|
53
58
|
kind: 'class',
|
|
54
59
|
line: startLine,
|
|
55
60
|
endLine: nodeEndLine(c.cls_node),
|
|
61
|
+
children: clsChildren.length > 0 ? clsChildren : undefined,
|
|
56
62
|
});
|
|
57
63
|
const heritage =
|
|
58
64
|
c.cls_node.childForFieldName('heritage') || findChild(c.cls_node, 'class_heritage');
|
|
@@ -69,11 +75,13 @@ function extractSymbolsQuery(tree, query) {
|
|
|
69
75
|
const methName = c.meth_name.text;
|
|
70
76
|
const parentClass = findParentClass(c.meth_node);
|
|
71
77
|
const fullName = parentClass ? `${parentClass}.${methName}` : methName;
|
|
78
|
+
const methChildren = extractParameters(c.meth_node);
|
|
72
79
|
definitions.push({
|
|
73
80
|
name: fullName,
|
|
74
81
|
kind: 'method',
|
|
75
82
|
line: c.meth_node.startPosition.row + 1,
|
|
76
83
|
endLine: nodeEndLine(c.meth_node),
|
|
84
|
+
children: methChildren.length > 0 ? methChildren : undefined,
|
|
77
85
|
});
|
|
78
86
|
} else if (c.iface_node) {
|
|
79
87
|
// interface_declaration (TS/TSX only)
|
|
@@ -162,9 +170,60 @@ function extractSymbolsQuery(tree, query) {
|
|
|
162
170
|
}
|
|
163
171
|
}
|
|
164
172
|
|
|
173
|
+
// Extract top-level constants via targeted walk (query patterns don't cover these)
|
|
174
|
+
extractConstantsWalk(tree.rootNode, definitions);
|
|
175
|
+
|
|
165
176
|
return { definitions, calls, imports, classes, exports: exps };
|
|
166
177
|
}
|
|
167
178
|
|
|
179
|
+
/**
|
|
180
|
+
* Walk program-level children to extract `const x = <literal>` as constants.
|
|
181
|
+
* The query-based fast path has no pattern for lexical_declaration/variable_declaration,
|
|
182
|
+
* so constants are missed. This targeted walk fills that gap without a full tree traversal.
|
|
183
|
+
*/
|
|
184
|
+
function extractConstantsWalk(rootNode, definitions) {
|
|
185
|
+
for (let i = 0; i < rootNode.childCount; i++) {
|
|
186
|
+
const node = rootNode.child(i);
|
|
187
|
+
if (!node) continue;
|
|
188
|
+
|
|
189
|
+
let declNode = node;
|
|
190
|
+
// Handle `export const …` — unwrap the export_statement to its declaration child
|
|
191
|
+
if (node.type === 'export_statement') {
|
|
192
|
+
const inner = node.childForFieldName('declaration');
|
|
193
|
+
if (!inner) continue;
|
|
194
|
+
declNode = inner;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const t = declNode.type;
|
|
198
|
+
if (t !== 'lexical_declaration' && t !== 'variable_declaration') continue;
|
|
199
|
+
if (!declNode.text.startsWith('const ')) continue;
|
|
200
|
+
|
|
201
|
+
for (let j = 0; j < declNode.childCount; j++) {
|
|
202
|
+
const declarator = declNode.child(j);
|
|
203
|
+
if (!declarator || declarator.type !== 'variable_declarator') continue;
|
|
204
|
+
const nameN = declarator.childForFieldName('name');
|
|
205
|
+
const valueN = declarator.childForFieldName('value');
|
|
206
|
+
if (!nameN || nameN.type !== 'identifier' || !valueN) continue;
|
|
207
|
+
// Skip functions — already captured by query patterns
|
|
208
|
+
const valType = valueN.type;
|
|
209
|
+
if (
|
|
210
|
+
valType === 'arrow_function' ||
|
|
211
|
+
valType === 'function_expression' ||
|
|
212
|
+
valType === 'function'
|
|
213
|
+
)
|
|
214
|
+
continue;
|
|
215
|
+
if (isConstantValue(valueN)) {
|
|
216
|
+
definitions.push({
|
|
217
|
+
name: nameN.text,
|
|
218
|
+
kind: 'constant',
|
|
219
|
+
line: declNode.startPosition.row + 1,
|
|
220
|
+
endLine: nodeEndLine(declNode),
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
168
227
|
function handleCommonJSAssignment(left, right, node, imports) {
|
|
169
228
|
if (!left || !right) return;
|
|
170
229
|
const leftText = left.text;
|
|
@@ -231,11 +290,13 @@ function extractSymbolsWalk(tree) {
|
|
|
231
290
|
case 'function_declaration': {
|
|
232
291
|
const nameNode = node.childForFieldName('name');
|
|
233
292
|
if (nameNode) {
|
|
293
|
+
const fnChildren = extractParameters(node);
|
|
234
294
|
definitions.push({
|
|
235
295
|
name: nameNode.text,
|
|
236
296
|
kind: 'function',
|
|
237
297
|
line: node.startPosition.row + 1,
|
|
238
298
|
endLine: nodeEndLine(node),
|
|
299
|
+
children: fnChildren.length > 0 ? fnChildren : undefined,
|
|
239
300
|
});
|
|
240
301
|
}
|
|
241
302
|
break;
|
|
@@ -246,11 +307,13 @@ function extractSymbolsWalk(tree) {
|
|
|
246
307
|
if (nameNode) {
|
|
247
308
|
const className = nameNode.text;
|
|
248
309
|
const startLine = node.startPosition.row + 1;
|
|
310
|
+
const clsChildren = extractClassProperties(node);
|
|
249
311
|
definitions.push({
|
|
250
312
|
name: className,
|
|
251
313
|
kind: 'class',
|
|
252
314
|
line: startLine,
|
|
253
315
|
endLine: nodeEndLine(node),
|
|
316
|
+
children: clsChildren.length > 0 ? clsChildren : undefined,
|
|
254
317
|
});
|
|
255
318
|
const heritage = node.childForFieldName('heritage') || findChild(node, 'class_heritage');
|
|
256
319
|
if (heritage) {
|
|
@@ -272,11 +335,13 @@ function extractSymbolsWalk(tree) {
|
|
|
272
335
|
if (nameNode) {
|
|
273
336
|
const parentClass = findParentClass(node);
|
|
274
337
|
const fullName = parentClass ? `${parentClass}.${nameNode.text}` : nameNode.text;
|
|
338
|
+
const methChildren = extractParameters(node);
|
|
275
339
|
definitions.push({
|
|
276
340
|
name: fullName,
|
|
277
341
|
kind: 'method',
|
|
278
342
|
line: node.startPosition.row + 1,
|
|
279
343
|
endLine: nodeEndLine(node),
|
|
344
|
+
children: methChildren.length > 0 ? methChildren : undefined,
|
|
280
345
|
});
|
|
281
346
|
}
|
|
282
347
|
break;
|
|
@@ -317,6 +382,7 @@ function extractSymbolsWalk(tree) {
|
|
|
317
382
|
|
|
318
383
|
case 'lexical_declaration':
|
|
319
384
|
case 'variable_declaration': {
|
|
385
|
+
const isConst = node.text.startsWith('const ');
|
|
320
386
|
for (let i = 0; i < node.childCount; i++) {
|
|
321
387
|
const declarator = node.child(i);
|
|
322
388
|
if (declarator && declarator.type === 'variable_declarator') {
|
|
@@ -329,15 +395,59 @@ function extractSymbolsWalk(tree) {
|
|
|
329
395
|
valType === 'function_expression' ||
|
|
330
396
|
valType === 'function'
|
|
331
397
|
) {
|
|
398
|
+
const varFnChildren = extractParameters(valueN);
|
|
332
399
|
definitions.push({
|
|
333
400
|
name: nameN.text,
|
|
334
401
|
kind: 'function',
|
|
335
402
|
line: node.startPosition.row + 1,
|
|
336
403
|
endLine: nodeEndLine(valueN),
|
|
404
|
+
children: varFnChildren.length > 0 ? varFnChildren : undefined,
|
|
337
405
|
});
|
|
406
|
+
} else if (isConst && nameN.type === 'identifier' && isConstantValue(valueN)) {
|
|
407
|
+
definitions.push({
|
|
408
|
+
name: nameN.text,
|
|
409
|
+
kind: 'constant',
|
|
410
|
+
line: node.startPosition.row + 1,
|
|
411
|
+
endLine: nodeEndLine(node),
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
} else if (isConst && nameN && nameN.type === 'identifier' && !valueN) {
|
|
415
|
+
// const with no value (shouldn't happen but be safe)
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
break;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
case 'enum_declaration': {
|
|
423
|
+
// TypeScript enum
|
|
424
|
+
const nameNode = node.childForFieldName('name');
|
|
425
|
+
if (nameNode) {
|
|
426
|
+
const enumChildren = [];
|
|
427
|
+
const body = node.childForFieldName('body') || findChild(node, 'enum_body');
|
|
428
|
+
if (body) {
|
|
429
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
430
|
+
const member = body.child(i);
|
|
431
|
+
if (!member) continue;
|
|
432
|
+
if (member.type === 'enum_assignment' || member.type === 'property_identifier') {
|
|
433
|
+
const mName = member.childForFieldName('name') || member.child(0);
|
|
434
|
+
if (mName) {
|
|
435
|
+
enumChildren.push({
|
|
436
|
+
name: mName.text,
|
|
437
|
+
kind: 'constant',
|
|
438
|
+
line: member.startPosition.row + 1,
|
|
439
|
+
});
|
|
440
|
+
}
|
|
338
441
|
}
|
|
339
442
|
}
|
|
340
443
|
}
|
|
444
|
+
definitions.push({
|
|
445
|
+
name: nameNode.text,
|
|
446
|
+
kind: 'enum',
|
|
447
|
+
line: node.startPosition.row + 1,
|
|
448
|
+
endLine: nodeEndLine(node),
|
|
449
|
+
children: enumChildren.length > 0 ? enumChildren : undefined,
|
|
450
|
+
});
|
|
341
451
|
}
|
|
342
452
|
break;
|
|
343
453
|
}
|
|
@@ -471,6 +581,89 @@ function extractSymbolsWalk(tree) {
|
|
|
471
581
|
return { definitions, calls, imports, classes, exports };
|
|
472
582
|
}
|
|
473
583
|
|
|
584
|
+
// ── Child extraction helpers ────────────────────────────────────────────────
|
|
585
|
+
|
|
586
|
+
function extractParameters(node) {
|
|
587
|
+
const params = [];
|
|
588
|
+
const paramsNode = node.childForFieldName('parameters') || findChild(node, 'formal_parameters');
|
|
589
|
+
if (!paramsNode) return params;
|
|
590
|
+
for (let i = 0; i < paramsNode.childCount; i++) {
|
|
591
|
+
const child = paramsNode.child(i);
|
|
592
|
+
if (!child) continue;
|
|
593
|
+
const t = child.type;
|
|
594
|
+
if (t === 'identifier') {
|
|
595
|
+
params.push({ name: child.text, kind: 'parameter', line: child.startPosition.row + 1 });
|
|
596
|
+
} else if (
|
|
597
|
+
t === 'required_parameter' ||
|
|
598
|
+
t === 'optional_parameter' ||
|
|
599
|
+
t === 'assignment_pattern'
|
|
600
|
+
) {
|
|
601
|
+
const nameNode =
|
|
602
|
+
child.childForFieldName('pattern') || child.childForFieldName('left') || child.child(0);
|
|
603
|
+
if (
|
|
604
|
+
nameNode &&
|
|
605
|
+
(nameNode.type === 'identifier' ||
|
|
606
|
+
nameNode.type === 'shorthand_property_identifier_pattern')
|
|
607
|
+
) {
|
|
608
|
+
params.push({ name: nameNode.text, kind: 'parameter', line: child.startPosition.row + 1 });
|
|
609
|
+
}
|
|
610
|
+
} else if (t === 'rest_pattern' || t === 'rest_element') {
|
|
611
|
+
const nameNode = child.child(1) || child.childForFieldName('name');
|
|
612
|
+
if (nameNode && nameNode.type === 'identifier') {
|
|
613
|
+
params.push({ name: nameNode.text, kind: 'parameter', line: child.startPosition.row + 1 });
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
return params;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
function extractClassProperties(classNode) {
|
|
621
|
+
const props = [];
|
|
622
|
+
const body = classNode.childForFieldName('body') || findChild(classNode, 'class_body');
|
|
623
|
+
if (!body) return props;
|
|
624
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
625
|
+
const child = body.child(i);
|
|
626
|
+
if (!child) continue;
|
|
627
|
+
if (
|
|
628
|
+
child.type === 'field_definition' ||
|
|
629
|
+
child.type === 'public_field_definition' ||
|
|
630
|
+
child.type === 'property_definition'
|
|
631
|
+
) {
|
|
632
|
+
const nameNode =
|
|
633
|
+
child.childForFieldName('name') || child.childForFieldName('property') || child.child(0);
|
|
634
|
+
if (
|
|
635
|
+
nameNode &&
|
|
636
|
+
(nameNode.type === 'property_identifier' ||
|
|
637
|
+
nameNode.type === 'identifier' ||
|
|
638
|
+
nameNode.type === 'private_property_identifier')
|
|
639
|
+
) {
|
|
640
|
+
props.push({ name: nameNode.text, kind: 'property', line: child.startPosition.row + 1 });
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
return props;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
function isConstantValue(valueNode) {
|
|
648
|
+
if (!valueNode) return false;
|
|
649
|
+
const t = valueNode.type;
|
|
650
|
+
return (
|
|
651
|
+
t === 'number' ||
|
|
652
|
+
t === 'string' ||
|
|
653
|
+
t === 'template_string' ||
|
|
654
|
+
t === 'true' ||
|
|
655
|
+
t === 'false' ||
|
|
656
|
+
t === 'null' ||
|
|
657
|
+
t === 'undefined' ||
|
|
658
|
+
t === 'array' ||
|
|
659
|
+
t === 'object' ||
|
|
660
|
+
t === 'regex' ||
|
|
661
|
+
t === 'unary_expression' ||
|
|
662
|
+
t === 'binary_expression' ||
|
|
663
|
+
t === 'new_expression'
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
|
|
474
667
|
// ── Shared helpers ──────────────────────────────────────────────────────────
|
|
475
668
|
|
|
476
669
|
function extractInterfaceMethods(bodyNode, interfaceName, definitions) {
|