@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,248 @@
1
+ import { findChild, nodeEndLine } from './helpers.js';
2
+
3
+ /**
4
+ * Extract symbols from C# files.
5
+ */
6
+ export function extractCSharpSymbols(tree, _filePath) {
7
+ const definitions = [];
8
+ const calls = [];
9
+ const imports = [];
10
+ const classes = [];
11
+ const exports = [];
12
+
13
+ function findCSharpParentType(node) {
14
+ let current = node.parent;
15
+ while (current) {
16
+ if (
17
+ current.type === 'class_declaration' ||
18
+ current.type === 'struct_declaration' ||
19
+ current.type === 'interface_declaration' ||
20
+ current.type === 'enum_declaration' ||
21
+ current.type === 'record_declaration'
22
+ ) {
23
+ const nameNode = current.childForFieldName('name');
24
+ return nameNode ? nameNode.text : null;
25
+ }
26
+ current = current.parent;
27
+ }
28
+ return null;
29
+ }
30
+
31
+ function walkCSharpNode(node) {
32
+ switch (node.type) {
33
+ case 'class_declaration': {
34
+ const nameNode = node.childForFieldName('name');
35
+ if (nameNode) {
36
+ definitions.push({
37
+ name: nameNode.text,
38
+ kind: 'class',
39
+ line: node.startPosition.row + 1,
40
+ endLine: nodeEndLine(node),
41
+ });
42
+ extractCSharpBaseTypes(node, nameNode.text, classes);
43
+ }
44
+ break;
45
+ }
46
+
47
+ case 'struct_declaration': {
48
+ const nameNode = node.childForFieldName('name');
49
+ if (nameNode) {
50
+ definitions.push({
51
+ name: nameNode.text,
52
+ kind: 'struct',
53
+ line: node.startPosition.row + 1,
54
+ endLine: nodeEndLine(node),
55
+ });
56
+ extractCSharpBaseTypes(node, nameNode.text, classes);
57
+ }
58
+ break;
59
+ }
60
+
61
+ case 'record_declaration': {
62
+ const nameNode = node.childForFieldName('name');
63
+ if (nameNode) {
64
+ definitions.push({
65
+ name: nameNode.text,
66
+ kind: 'record',
67
+ line: node.startPosition.row + 1,
68
+ endLine: nodeEndLine(node),
69
+ });
70
+ extractCSharpBaseTypes(node, nameNode.text, classes);
71
+ }
72
+ break;
73
+ }
74
+
75
+ case 'interface_declaration': {
76
+ const nameNode = node.childForFieldName('name');
77
+ if (nameNode) {
78
+ definitions.push({
79
+ name: nameNode.text,
80
+ kind: 'interface',
81
+ line: node.startPosition.row + 1,
82
+ endLine: nodeEndLine(node),
83
+ });
84
+ const body = node.childForFieldName('body');
85
+ if (body) {
86
+ for (let i = 0; i < body.childCount; i++) {
87
+ const child = body.child(i);
88
+ if (child && child.type === 'method_declaration') {
89
+ const methName = child.childForFieldName('name');
90
+ if (methName) {
91
+ definitions.push({
92
+ name: `${nameNode.text}.${methName.text}`,
93
+ kind: 'method',
94
+ line: child.startPosition.row + 1,
95
+ endLine: child.endPosition.row + 1,
96
+ });
97
+ }
98
+ }
99
+ }
100
+ }
101
+ }
102
+ break;
103
+ }
104
+
105
+ case 'enum_declaration': {
106
+ const nameNode = node.childForFieldName('name');
107
+ if (nameNode) {
108
+ definitions.push({
109
+ name: nameNode.text,
110
+ kind: 'enum',
111
+ line: node.startPosition.row + 1,
112
+ endLine: nodeEndLine(node),
113
+ });
114
+ }
115
+ break;
116
+ }
117
+
118
+ case 'method_declaration': {
119
+ const nameNode = node.childForFieldName('name');
120
+ if (nameNode) {
121
+ const parentType = findCSharpParentType(node);
122
+ const fullName = parentType ? `${parentType}.${nameNode.text}` : nameNode.text;
123
+ definitions.push({
124
+ name: fullName,
125
+ kind: 'method',
126
+ line: node.startPosition.row + 1,
127
+ endLine: nodeEndLine(node),
128
+ });
129
+ }
130
+ break;
131
+ }
132
+
133
+ case 'constructor_declaration': {
134
+ const nameNode = node.childForFieldName('name');
135
+ if (nameNode) {
136
+ const parentType = findCSharpParentType(node);
137
+ const fullName = parentType ? `${parentType}.${nameNode.text}` : nameNode.text;
138
+ definitions.push({
139
+ name: fullName,
140
+ kind: 'method',
141
+ line: node.startPosition.row + 1,
142
+ endLine: nodeEndLine(node),
143
+ });
144
+ }
145
+ break;
146
+ }
147
+
148
+ case 'property_declaration': {
149
+ const nameNode = node.childForFieldName('name');
150
+ if (nameNode) {
151
+ const parentType = findCSharpParentType(node);
152
+ const fullName = parentType ? `${parentType}.${nameNode.text}` : nameNode.text;
153
+ definitions.push({
154
+ name: fullName,
155
+ kind: 'method',
156
+ line: node.startPosition.row + 1,
157
+ endLine: nodeEndLine(node),
158
+ });
159
+ }
160
+ break;
161
+ }
162
+
163
+ case 'using_directive': {
164
+ // using System.Collections.Generic;
165
+ const nameNode =
166
+ node.childForFieldName('name') ||
167
+ findChild(node, 'qualified_name') ||
168
+ findChild(node, 'identifier');
169
+ if (nameNode) {
170
+ const fullPath = nameNode.text;
171
+ const lastName = fullPath.split('.').pop();
172
+ imports.push({
173
+ source: fullPath,
174
+ names: [lastName],
175
+ line: node.startPosition.row + 1,
176
+ csharpUsing: true,
177
+ });
178
+ }
179
+ break;
180
+ }
181
+
182
+ case 'invocation_expression': {
183
+ const fn = node.childForFieldName('function') || node.child(0);
184
+ if (fn) {
185
+ if (fn.type === 'identifier') {
186
+ calls.push({ name: fn.text, line: node.startPosition.row + 1 });
187
+ } else if (fn.type === 'member_access_expression') {
188
+ const name = fn.childForFieldName('name');
189
+ if (name) {
190
+ const expr = fn.childForFieldName('expression');
191
+ const call = { name: name.text, line: node.startPosition.row + 1 };
192
+ if (expr) call.receiver = expr.text;
193
+ calls.push(call);
194
+ }
195
+ } else if (fn.type === 'generic_name' || fn.type === 'member_binding_expression') {
196
+ const name = fn.childForFieldName('name') || fn.child(0);
197
+ if (name) calls.push({ name: name.text, line: node.startPosition.row + 1 });
198
+ }
199
+ }
200
+ break;
201
+ }
202
+
203
+ case 'object_creation_expression': {
204
+ const typeNode = node.childForFieldName('type');
205
+ if (typeNode) {
206
+ const typeName =
207
+ typeNode.type === 'generic_name'
208
+ ? typeNode.childForFieldName('name')?.text || typeNode.child(0)?.text
209
+ : typeNode.text;
210
+ if (typeName) calls.push({ name: typeName, line: node.startPosition.row + 1 });
211
+ }
212
+ break;
213
+ }
214
+ }
215
+
216
+ for (let i = 0; i < node.childCount; i++) walkCSharpNode(node.child(i));
217
+ }
218
+
219
+ walkCSharpNode(tree.rootNode);
220
+ return { definitions, calls, imports, classes, exports };
221
+ }
222
+
223
+ function extractCSharpBaseTypes(node, className, classes) {
224
+ const baseList = node.childForFieldName('bases');
225
+ if (!baseList) return;
226
+ for (let i = 0; i < baseList.childCount; i++) {
227
+ const child = baseList.child(i);
228
+ if (!child) continue;
229
+ if (child.type === 'identifier' || child.type === 'qualified_name') {
230
+ classes.push({ name: className, extends: child.text, line: node.startPosition.row + 1 });
231
+ } else if (child.type === 'generic_name') {
232
+ const name = child.childForFieldName('name') || child.child(0);
233
+ if (name)
234
+ classes.push({ name: className, extends: name.text, line: node.startPosition.row + 1 });
235
+ } else if (child.type === 'base_list') {
236
+ for (let j = 0; j < child.childCount; j++) {
237
+ const base = child.child(j);
238
+ if (base && (base.type === 'identifier' || base.type === 'qualified_name')) {
239
+ classes.push({ name: className, extends: base.text, line: node.startPosition.row + 1 });
240
+ } else if (base && base.type === 'generic_name') {
241
+ const name = base.childForFieldName('name') || base.child(0);
242
+ if (name)
243
+ classes.push({ name: className, extends: name.text, line: node.startPosition.row + 1 });
244
+ }
245
+ }
246
+ }
247
+ }
248
+ }
@@ -0,0 +1,172 @@
1
+ import { nodeEndLine } from './helpers.js';
2
+
3
+ /**
4
+ * Extract symbols from Go files.
5
+ */
6
+ export function extractGoSymbols(tree, _filePath) {
7
+ const definitions = [];
8
+ const calls = [];
9
+ const imports = [];
10
+ const classes = [];
11
+ const exports = [];
12
+
13
+ function walkGoNode(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 'method_declaration': {
29
+ const nameNode = node.childForFieldName('name');
30
+ const receiver = node.childForFieldName('receiver');
31
+ if (nameNode) {
32
+ let receiverType = null;
33
+ if (receiver) {
34
+ // receiver is a parameter_list like (r *Foo) or (r Foo)
35
+ for (let i = 0; i < receiver.childCount; i++) {
36
+ const param = receiver.child(i);
37
+ if (!param) continue;
38
+ const typeNode = param.childForFieldName('type');
39
+ if (typeNode) {
40
+ receiverType =
41
+ typeNode.type === 'pointer_type'
42
+ ? typeNode.text.replace(/^\*/, '')
43
+ : typeNode.text;
44
+ break;
45
+ }
46
+ }
47
+ }
48
+ const fullName = receiverType ? `${receiverType}.${nameNode.text}` : nameNode.text;
49
+ definitions.push({
50
+ name: fullName,
51
+ kind: 'method',
52
+ line: node.startPosition.row + 1,
53
+ endLine: nodeEndLine(node),
54
+ });
55
+ }
56
+ break;
57
+ }
58
+
59
+ case 'type_declaration': {
60
+ for (let i = 0; i < node.childCount; i++) {
61
+ const spec = node.child(i);
62
+ if (!spec || spec.type !== 'type_spec') continue;
63
+ const nameNode = spec.childForFieldName('name');
64
+ const typeNode = spec.childForFieldName('type');
65
+ if (nameNode && typeNode) {
66
+ if (typeNode.type === 'struct_type') {
67
+ definitions.push({
68
+ name: nameNode.text,
69
+ kind: 'struct',
70
+ line: node.startPosition.row + 1,
71
+ endLine: nodeEndLine(node),
72
+ });
73
+ } else if (typeNode.type === 'interface_type') {
74
+ definitions.push({
75
+ name: nameNode.text,
76
+ kind: 'interface',
77
+ line: node.startPosition.row + 1,
78
+ endLine: nodeEndLine(node),
79
+ });
80
+ for (let j = 0; j < typeNode.childCount; j++) {
81
+ const member = typeNode.child(j);
82
+ if (member && member.type === 'method_elem') {
83
+ const methName = member.childForFieldName('name');
84
+ if (methName) {
85
+ definitions.push({
86
+ name: `${nameNode.text}.${methName.text}`,
87
+ kind: 'method',
88
+ line: member.startPosition.row + 1,
89
+ endLine: member.endPosition.row + 1,
90
+ });
91
+ }
92
+ }
93
+ }
94
+ } else {
95
+ definitions.push({
96
+ name: nameNode.text,
97
+ kind: 'type',
98
+ line: node.startPosition.row + 1,
99
+ endLine: nodeEndLine(node),
100
+ });
101
+ }
102
+ }
103
+ }
104
+ break;
105
+ }
106
+
107
+ case 'import_declaration': {
108
+ for (let i = 0; i < node.childCount; i++) {
109
+ const child = node.child(i);
110
+ if (!child) continue;
111
+ if (child.type === 'import_spec') {
112
+ const pathNode = child.childForFieldName('path');
113
+ if (pathNode) {
114
+ const importPath = pathNode.text.replace(/"/g, '');
115
+ const nameNode = child.childForFieldName('name');
116
+ const alias = nameNode ? nameNode.text : importPath.split('/').pop();
117
+ imports.push({
118
+ source: importPath,
119
+ names: [alias],
120
+ line: child.startPosition.row + 1,
121
+ goImport: true,
122
+ });
123
+ }
124
+ }
125
+ if (child.type === 'import_spec_list') {
126
+ for (let j = 0; j < child.childCount; j++) {
127
+ const spec = child.child(j);
128
+ if (spec && spec.type === 'import_spec') {
129
+ const pathNode = spec.childForFieldName('path');
130
+ if (pathNode) {
131
+ const importPath = pathNode.text.replace(/"/g, '');
132
+ const nameNode = spec.childForFieldName('name');
133
+ const alias = nameNode ? nameNode.text : importPath.split('/').pop();
134
+ imports.push({
135
+ source: importPath,
136
+ names: [alias],
137
+ line: spec.startPosition.row + 1,
138
+ goImport: true,
139
+ });
140
+ }
141
+ }
142
+ }
143
+ }
144
+ }
145
+ break;
146
+ }
147
+
148
+ case 'call_expression': {
149
+ const fn = node.childForFieldName('function');
150
+ if (fn) {
151
+ if (fn.type === 'identifier') {
152
+ calls.push({ name: fn.text, line: node.startPosition.row + 1 });
153
+ } else if (fn.type === 'selector_expression') {
154
+ const field = fn.childForFieldName('field');
155
+ if (field) {
156
+ const operand = fn.childForFieldName('operand');
157
+ const call = { name: field.text, line: node.startPosition.row + 1 };
158
+ if (operand) call.receiver = operand.text;
159
+ calls.push(call);
160
+ }
161
+ }
162
+ }
163
+ break;
164
+ }
165
+ }
166
+
167
+ for (let i = 0; i < node.childCount; i++) walkGoNode(node.child(i));
168
+ }
169
+
170
+ walkGoNode(tree.rootNode);
171
+ return { definitions, calls, imports, classes, exports };
172
+ }
@@ -0,0 +1,73 @@
1
+ import { nodeEndLine } from './helpers.js';
2
+
3
+ /**
4
+ * Extract symbols from HCL (Terraform) files.
5
+ */
6
+ export function extractHCLSymbols(tree, _filePath) {
7
+ const definitions = [];
8
+ const imports = [];
9
+
10
+ function walkHclNode(node) {
11
+ if (node.type === 'block') {
12
+ const children = [];
13
+ for (let i = 0; i < node.childCount; i++) children.push(node.child(i));
14
+
15
+ const identifiers = children.filter((c) => c.type === 'identifier');
16
+ const strings = children.filter((c) => c.type === 'string_lit');
17
+
18
+ if (identifiers.length > 0) {
19
+ const blockType = identifiers[0].text;
20
+ let name = '';
21
+
22
+ if (blockType === 'resource' && strings.length >= 2) {
23
+ name = `${strings[0].text.replace(/"/g, '')}.${strings[1].text.replace(/"/g, '')}`;
24
+ } else if (blockType === 'data' && strings.length >= 2) {
25
+ name = `data.${strings[0].text.replace(/"/g, '')}.${strings[1].text.replace(/"/g, '')}`;
26
+ } else if (
27
+ (blockType === 'variable' || blockType === 'output' || blockType === 'module') &&
28
+ strings.length >= 1
29
+ ) {
30
+ name = `${blockType}.${strings[0].text.replace(/"/g, '')}`;
31
+ } else if (blockType === 'locals') {
32
+ name = 'locals';
33
+ } else if (blockType === 'terraform' || blockType === 'provider') {
34
+ name = blockType;
35
+ if (strings.length >= 1) name += `.${strings[0].text.replace(/"/g, '')}`;
36
+ }
37
+
38
+ if (name) {
39
+ definitions.push({
40
+ name,
41
+ kind: blockType,
42
+ line: node.startPosition.row + 1,
43
+ endLine: nodeEndLine(node),
44
+ });
45
+ }
46
+
47
+ if (blockType === 'module') {
48
+ const body = children.find((c) => c.type === 'body');
49
+ if (body) {
50
+ for (let i = 0; i < body.childCount; i++) {
51
+ const attr = body.child(i);
52
+ if (attr && attr.type === 'attribute') {
53
+ const key = attr.childForFieldName('key') || attr.child(0);
54
+ const val = attr.childForFieldName('val') || attr.child(2);
55
+ if (key && key.text === 'source' && val) {
56
+ const src = val.text.replace(/"/g, '');
57
+ if (src.startsWith('./') || src.startsWith('../')) {
58
+ imports.push({ source: src, names: [], line: attr.startPosition.row + 1 });
59
+ }
60
+ }
61
+ }
62
+ }
63
+ }
64
+ }
65
+ }
66
+ }
67
+
68
+ for (let i = 0; i < node.childCount; i++) walkHclNode(node.child(i));
69
+ }
70
+
71
+ walkHclNode(tree.rootNode);
72
+ return { definitions, calls: [], imports, classes: [], exports: [] };
73
+ }
@@ -0,0 +1,10 @@
1
+ export function nodeEndLine(node) {
2
+ return node.endPosition.row + 1;
3
+ }
4
+
5
+ export function findChild(node, type) {
6
+ for (let i = 0; i < node.childCount; i++) {
7
+ if (node.child(i).type === type) return node.child(i);
8
+ }
9
+ return null;
10
+ }
@@ -0,0 +1,9 @@
1
+ export { extractCSharpSymbols } from './csharp.js';
2
+ export { extractGoSymbols } from './go.js';
3
+ export { extractHCLSymbols } from './hcl.js';
4
+ export { extractJavaSymbols } from './java.js';
5
+ export { extractSymbols } from './javascript.js';
6
+ export { extractPHPSymbols } from './php.js';
7
+ export { extractPythonSymbols } from './python.js';
8
+ export { extractRubySymbols } from './ruby.js';
9
+ export { extractRustSymbols } from './rust.js';