@kodus/kodus-graph 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.
Files changed (50) hide show
  1. package/package.json +62 -0
  2. package/src/analysis/blast-radius.ts +54 -0
  3. package/src/analysis/communities.ts +135 -0
  4. package/src/analysis/diff.ts +120 -0
  5. package/src/analysis/flows.ts +112 -0
  6. package/src/analysis/review-context.ts +141 -0
  7. package/src/analysis/risk-score.ts +62 -0
  8. package/src/analysis/search.ts +76 -0
  9. package/src/analysis/test-gaps.ts +21 -0
  10. package/src/cli.ts +192 -0
  11. package/src/commands/analyze.ts +66 -0
  12. package/src/commands/communities.ts +19 -0
  13. package/src/commands/context.ts +69 -0
  14. package/src/commands/diff.ts +96 -0
  15. package/src/commands/flows.ts +19 -0
  16. package/src/commands/parse.ts +100 -0
  17. package/src/commands/search.ts +41 -0
  18. package/src/commands/update.ts +166 -0
  19. package/src/graph/builder.ts +170 -0
  20. package/src/graph/edges.ts +101 -0
  21. package/src/graph/loader.ts +100 -0
  22. package/src/graph/merger.ts +25 -0
  23. package/src/graph/types.ts +218 -0
  24. package/src/parser/batch.ts +74 -0
  25. package/src/parser/discovery.ts +42 -0
  26. package/src/parser/extractor.ts +37 -0
  27. package/src/parser/extractors/generic.ts +87 -0
  28. package/src/parser/extractors/python.ts +127 -0
  29. package/src/parser/extractors/ruby.ts +142 -0
  30. package/src/parser/extractors/typescript.ts +329 -0
  31. package/src/parser/languages.ts +122 -0
  32. package/src/resolver/call-resolver.ts +179 -0
  33. package/src/resolver/import-map.ts +27 -0
  34. package/src/resolver/import-resolver.ts +72 -0
  35. package/src/resolver/languages/csharp.ts +7 -0
  36. package/src/resolver/languages/go.ts +7 -0
  37. package/src/resolver/languages/java.ts +7 -0
  38. package/src/resolver/languages/php.ts +7 -0
  39. package/src/resolver/languages/python.ts +35 -0
  40. package/src/resolver/languages/ruby.ts +21 -0
  41. package/src/resolver/languages/rust.ts +7 -0
  42. package/src/resolver/languages/typescript.ts +168 -0
  43. package/src/resolver/symbol-table.ts +53 -0
  44. package/src/shared/file-hash.ts +7 -0
  45. package/src/shared/filters.ts +243 -0
  46. package/src/shared/logger.ts +14 -0
  47. package/src/shared/qualified-name.ts +5 -0
  48. package/src/shared/safe-path.ts +31 -0
  49. package/src/shared/schemas.ts +31 -0
  50. package/src/shared/temp.ts +17 -0
@@ -0,0 +1,142 @@
1
+ import type { SgNode, SgRoot } from '@ast-grep/napi';
2
+ import type { RawCallSite, RawGraph } from '../../graph/types';
3
+ import { NOISE } from '../../shared/filters';
4
+ import { log } from '../../shared/logger';
5
+ import { LANG_KINDS } from '../languages';
6
+
7
+ export function extractRuby(root: SgRoot, fp: string, seen: Set<string>, graph: RawGraph): void {
8
+ const kinds = LANG_KINDS.ruby;
9
+ const rootNode = root.root();
10
+
11
+ // ── Classes ──
12
+ for (const node of rootNode.findAll({ rule: { kind: kinds.class } })) {
13
+ const name = node.field('name')?.text();
14
+ if (!name || seen.has(`c:${fp}:${name}`)) continue;
15
+ seen.add(`c:${fp}:${name}`);
16
+
17
+ const superclass = node.field('superclass')?.text() || '';
18
+ graph.classes.push({
19
+ name,
20
+ file: fp,
21
+ line_start: node.range().start.line,
22
+ line_end: node.range().end.line,
23
+ extends: superclass,
24
+ implements: '',
25
+ qualified: `${fp}::${name}`,
26
+ });
27
+ }
28
+
29
+ // ── Modules ──
30
+ for (const node of rootNode.findAll({ rule: { kind: kinds.module } })) {
31
+ const name = node.field('name')?.text();
32
+ if (!name || seen.has(`c:${fp}:${name}`)) continue;
33
+ seen.add(`c:${fp}:${name}`);
34
+ graph.classes.push({
35
+ name,
36
+ file: fp,
37
+ line_start: node.range().start.line,
38
+ line_end: node.range().end.line,
39
+ extends: '',
40
+ implements: '',
41
+ qualified: `${fp}::${name}`,
42
+ });
43
+ }
44
+
45
+ // ── Methods ──
46
+ for (const node of rootNode.findAll({ rule: { kind: kinds.method } })) {
47
+ const name = node.field('name')?.text();
48
+ if (!name) continue;
49
+ const line = node.range().start.line;
50
+ if (seen.has(`m:${fp}:${name}:${line}`)) continue;
51
+ seen.add(`m:${fp}:${name}:${line}`);
52
+
53
+ const classAncestor = node.ancestors().find((a: SgNode) => a.kind() === kinds.class || a.kind() === kinds.module);
54
+ const className = classAncestor?.field('name')?.text() || '';
55
+
56
+ graph.functions.push({
57
+ name,
58
+ file: fp,
59
+ line_start: line,
60
+ line_end: node.range().end.line,
61
+ params: node.field('parameters')?.text() || '()',
62
+ returnType: '',
63
+ kind: className ? 'Method' : 'Function',
64
+ className,
65
+ qualified: className ? `${fp}::${className}.${name}` : `${fp}::${name}`,
66
+ });
67
+ }
68
+
69
+ // ── Tests (RSpec: describe/it/context) ──
70
+ for (const p of [
71
+ "describe '$NAME' do $$$BODY end",
72
+ 'describe "$NAME" do $$$BODY end',
73
+ "it '$NAME' do $$$BODY end",
74
+ 'it "$NAME" do $$$BODY end',
75
+ "context '$NAME' do $$$BODY end",
76
+ 'context "$NAME" do $$$BODY end',
77
+ ]) {
78
+ try {
79
+ for (const m of rootNode.findAll(p)) {
80
+ const name = m.getMatch('NAME')?.text();
81
+ if (!name) continue;
82
+ const key = `t:${fp}:${name}:${m.range().start.line}`;
83
+ if (seen.has(key)) continue;
84
+ seen.add(key);
85
+ graph.tests.push({
86
+ name,
87
+ file: fp,
88
+ line_start: m.range().start.line,
89
+ line_end: m.range().end.line,
90
+ qualified: `${fp}::test:${name}`,
91
+ });
92
+ }
93
+ } catch (err) {
94
+ log.debug('Ruby pattern mismatch', { file: fp, pattern: p, error: String(err) });
95
+ }
96
+ }
97
+
98
+ // ── Imports (require/require_relative) ──
99
+ for (const p of [
100
+ "require '$MODULE'",
101
+ 'require "$MODULE"',
102
+ "require_relative '$MODULE'",
103
+ 'require_relative "$MODULE"',
104
+ ]) {
105
+ try {
106
+ for (const m of rootNode.findAll(p)) {
107
+ const mod = m.getMatch('MODULE')?.text();
108
+ if (mod) {
109
+ graph.imports.push({
110
+ module: mod,
111
+ file: fp,
112
+ line: m.range().start.line,
113
+ names: [],
114
+ lang: 'ruby',
115
+ });
116
+ }
117
+ }
118
+ } catch (err) {
119
+ log.debug('Ruby pattern mismatch', { file: fp, pattern: p, error: String(err) });
120
+ }
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Extract raw call sites from a Ruby AST.
126
+ * Direct calls only — Ruby has no DI pattern.
127
+ */
128
+ export function extractCallsFromRuby(root: SgRoot, fp: string, calls: RawCallSite[]): void {
129
+ const rootNode = root.root();
130
+
131
+ for (const m of rootNode.findAll('$CALLEE($$$ARGS)')) {
132
+ const callee = m.getMatch('CALLEE')?.text();
133
+ if (!callee) continue;
134
+ const callName = callee.includes('.') ? callee.split('.').pop()! : callee;
135
+ if (NOISE.has(callName)) continue;
136
+ calls.push({
137
+ source: fp,
138
+ callName,
139
+ line: m.range().start.line,
140
+ });
141
+ }
142
+ }
@@ -0,0 +1,329 @@
1
+ import type { SgNode, SgRoot } from '@ast-grep/napi';
2
+ import { Lang } from '@ast-grep/napi';
3
+ import type { RawCallSite, RawGraph } from '../../graph/types';
4
+ import { NOISE } from '../../shared/filters';
5
+ import { LANG_KINDS } from '../languages';
6
+
7
+ export function extractTypeScript(
8
+ root: SgRoot,
9
+ fp: string,
10
+ seen: Set<string>,
11
+ graph: RawGraph,
12
+ lang: Lang | string = Lang.TypeScript,
13
+ ): void {
14
+ const kinds = LANG_KINDS.typescript;
15
+ const rootNode = root.root();
16
+ const isTS = lang === Lang.TypeScript || lang === Lang.Tsx;
17
+
18
+ // ── Classes ──
19
+ const classKinds = isTS ? [kinds.class, kinds.abstractClass] : [kinds.class];
20
+ for (const kind of classKinds) {
21
+ for (const node of rootNode.findAll({ rule: { kind } })) {
22
+ const name = node.field('name')?.text();
23
+ if (!name || seen.has(`c:${fp}:${name}`)) continue;
24
+ seen.add(`c:${fp}:${name}`);
25
+
26
+ let extendsName = '';
27
+ let implementsName = '';
28
+ const heritage = node.children().find((c: SgNode) => c.kind() === 'class_heritage');
29
+ if (heritage) {
30
+ const ext = heritage.children().find((c: SgNode) => c.kind() === 'extends_clause');
31
+ extendsName =
32
+ ext
33
+ ?.children()
34
+ .find(
35
+ (c: SgNode) =>
36
+ c.kind() === 'identifier' || c.kind() === 'type_identifier' || c.kind() === 'member_expression',
37
+ )
38
+ ?.text() || '';
39
+ const impl = heritage.children().find((c: SgNode) => c.kind() === 'implements_clause');
40
+ implementsName =
41
+ impl
42
+ ?.children()
43
+ .find((c: SgNode) => c.kind() === 'type_identifier' || c.kind() === 'identifier')
44
+ ?.text() || '';
45
+ }
46
+
47
+ graph.classes.push({
48
+ name,
49
+ file: fp,
50
+ line_start: node.range().start.line,
51
+ line_end: node.range().end.line,
52
+ extends: extendsName,
53
+ implements: implementsName,
54
+ qualified: `${fp}::${name}`,
55
+ });
56
+ }
57
+ }
58
+
59
+ // ── Methods (kind-based: catches constructor, async, getters/setters) ──
60
+ for (const node of rootNode.findAll({ rule: { kind: kinds.method } })) {
61
+ const name = node.field('name')?.text();
62
+ if (!name) continue;
63
+ const line = node.range().start.line;
64
+ if (seen.has(`m:${fp}:${name}:${line}`)) continue;
65
+ seen.add(`m:${fp}:${name}:${line}`);
66
+
67
+ const classAncestor = node
68
+ .ancestors()
69
+ .find((a: SgNode) => a.kind() === kinds.class || (isTS && a.kind() === kinds.abstractClass));
70
+ const className = classAncestor?.field('name')?.text() || '';
71
+ const params = node.field('parameters');
72
+ const retType = node.field('return_type')?.text()?.replace(/^:\s*/, '') || '';
73
+
74
+ if (name === 'constructor' && className) {
75
+ // Constructor DI extraction
76
+ const fieldTypeMap = new Map<string, string>();
77
+ if (params) {
78
+ for (const p of params.children()) {
79
+ if (p.kind() !== 'required_parameter') continue;
80
+ if (!p.children().some((c: SgNode) => c.kind() === 'accessibility_modifier')) continue;
81
+ const ident = p.children().find((c: SgNode) => c.kind() === 'identifier');
82
+ const typeAnn = p.children().find((c: SgNode) => c.kind() === 'type_annotation');
83
+ if (ident && typeAnn) {
84
+ const typeNode = typeAnn
85
+ .children()
86
+ .find(
87
+ (c: SgNode) =>
88
+ c.kind() === 'type_identifier' || c.kind() === 'identifier' || c.kind() === 'generic_type',
89
+ );
90
+ if (typeNode) {
91
+ const typeName =
92
+ typeNode.kind() === 'generic_type'
93
+ ? typeNode
94
+ .children()
95
+ .find((c: SgNode) => c.kind() === 'type_identifier')
96
+ ?.text() || typeNode.text()
97
+ : typeNode.text();
98
+ fieldTypeMap.set(ident.text(), typeName);
99
+ }
100
+ }
101
+ }
102
+ }
103
+ if (fieldTypeMap.size > 0) graph.diMaps.set(fp, fieldTypeMap);
104
+
105
+ graph.functions.push({
106
+ name: `${className}.constructor`,
107
+ file: fp,
108
+ line_start: line,
109
+ line_end: node.range().end.line,
110
+ params: params?.text() || '()',
111
+ returnType: '',
112
+ kind: 'Constructor',
113
+ className,
114
+ qualified: `${fp}::${className}.constructor`,
115
+ });
116
+ } else {
117
+ graph.functions.push({
118
+ name,
119
+ file: fp,
120
+ line_start: line,
121
+ line_end: node.range().end.line,
122
+ params: params?.text() || '()',
123
+ returnType: retType,
124
+ kind: className ? 'Method' : 'Function',
125
+ className,
126
+ qualified: className ? `${fp}::${className}.${name}` : `${fp}::${name}`,
127
+ });
128
+ }
129
+ }
130
+
131
+ // ── Standalone functions ──
132
+ for (const node of rootNode.findAll({ rule: { kind: kinds.function } })) {
133
+ const name = node.field('name')?.text();
134
+ if (!name) continue;
135
+ const line = node.range().start.line;
136
+ if (seen.has(`f:${fp}:${name}:${line}`)) continue;
137
+ if (node.ancestors().some((a: SgNode) => a.kind() === kinds.class || (isTS && a.kind() === kinds.abstractClass)))
138
+ continue;
139
+ seen.add(`f:${fp}:${name}:${line}`);
140
+
141
+ graph.functions.push({
142
+ name,
143
+ file: fp,
144
+ line_start: line,
145
+ line_end: node.range().end.line,
146
+ params: node.field('parameters')?.text() || '()',
147
+ returnType: node.field('return_type')?.text()?.replace(/^:\s*/, '') || '',
148
+ kind: 'Function',
149
+ className: '',
150
+ qualified: `${fp}::${name}`,
151
+ });
152
+ }
153
+
154
+ // ── Arrow functions ──
155
+ for (const node of rootNode.findAll({
156
+ rule: { kind: kinds.arrowContainer, has: { kind: kinds.arrowFunction } },
157
+ })) {
158
+ const name = node.field('name')?.text();
159
+ if (!name) continue;
160
+ const line = node.range().start.line;
161
+ if (seen.has(`f:${fp}:${name}:${line}`)) continue;
162
+ seen.add(`f:${fp}:${name}:${line}`);
163
+
164
+ const arrow = node.children().find((c: SgNode) => c.kind() === kinds.arrowFunction);
165
+ graph.functions.push({
166
+ name,
167
+ file: fp,
168
+ line_start: line,
169
+ line_end: node.range().end.line,
170
+ params: arrow?.field('parameters')?.text() || '()',
171
+ returnType: arrow?.field('return_type')?.text()?.replace(/^:\s*/, '') || '',
172
+ kind: 'Function',
173
+ className: '',
174
+ qualified: `${fp}::${name}`,
175
+ });
176
+ }
177
+
178
+ // ── Interfaces (TS only — JS grammar has no interface_declaration) ──
179
+ if (isTS)
180
+ for (const node of rootNode.findAll({ rule: { kind: kinds.interface } })) {
181
+ const name = node.field('name')?.text();
182
+ if (!name || seen.has(`i:${fp}:${name}`)) continue;
183
+ seen.add(`i:${fp}:${name}`);
184
+
185
+ const methods: string[] = [];
186
+ const body = node.field('body');
187
+ if (body) {
188
+ for (const child of body.findAll({ rule: { kind: kinds.methodSignature } })) {
189
+ const mn = child.field('name')?.text();
190
+ if (mn) methods.push(mn);
191
+ }
192
+ }
193
+
194
+ graph.interfaces.push({
195
+ name,
196
+ file: fp,
197
+ line_start: node.range().start.line,
198
+ line_end: node.range().end.line,
199
+ methods,
200
+ qualified: `${fp}::${name}`,
201
+ });
202
+ }
203
+
204
+ // ── Enums (TS only — JS grammar has no enum_declaration) ──
205
+ if (isTS)
206
+ for (const node of rootNode.findAll({ rule: { kind: kinds.enum } })) {
207
+ const name = node.field('name')?.text();
208
+ if (!name || seen.has(`e:${fp}:${name}`)) continue;
209
+ seen.add(`e:${fp}:${name}`);
210
+ graph.enums.push({
211
+ name,
212
+ file: fp,
213
+ line_start: node.range().start.line,
214
+ line_end: node.range().end.line,
215
+ qualified: `${fp}::${name}`,
216
+ });
217
+ }
218
+
219
+ // ── Imports ──
220
+ for (const node of rootNode.findAll({ rule: { kind: kinds.import } })) {
221
+ const sourceNode = node.children().find((c: SgNode) => c.kind() === 'string');
222
+ const frag = sourceNode?.children().find((c: SgNode) => c.kind() === 'string_fragment');
223
+ const modulePath = frag?.text() || sourceNode?.text()?.replace(/['"]/g, '') || '';
224
+ if (!modulePath) continue;
225
+
226
+ const names: string[] = [];
227
+ const importClause = node.children().find((c: SgNode) => c.kind() === 'import_clause');
228
+ if (importClause) {
229
+ for (const child of importClause.children()) {
230
+ if (child.kind() === 'identifier') {
231
+ names.push(child.text());
232
+ } else if (child.kind() === 'named_imports') {
233
+ for (const spec of child.findAll({ rule: { kind: 'import_specifier' } })) {
234
+ const n =
235
+ spec.field('name')?.text() ||
236
+ spec
237
+ .children()
238
+ .find((c: SgNode) => c.kind() === 'identifier')
239
+ ?.text();
240
+ if (n) names.push(n);
241
+ }
242
+ } else if (child.kind() === 'namespace_import') {
243
+ const alias = child.children().find((c: SgNode) => c.kind() === 'identifier');
244
+ if (alias) names.push(alias.text());
245
+ }
246
+ }
247
+ }
248
+ graph.imports.push({
249
+ module: modulePath,
250
+ file: fp,
251
+ line: node.range().start.line,
252
+ names,
253
+ lang: 'ts',
254
+ });
255
+ }
256
+
257
+ // ── Re-exports ──
258
+ for (const node of rootNode.findAll({ rule: { kind: kinds.export } })) {
259
+ const src = node.children().find((c: SgNode) => c.kind() === 'string');
260
+ if (src) {
261
+ const frag = src.children().find((c: SgNode) => c.kind() === 'string_fragment');
262
+ graph.reExports.push({
263
+ module: frag?.text() || src.text().replace(/['"]/g, ''),
264
+ file: fp,
265
+ line: node.range().start.line,
266
+ });
267
+ }
268
+ }
269
+
270
+ // ── Tests (pattern-based) ──
271
+ for (const p of [
272
+ 'describe("$NAME", $$$BODY)',
273
+ "describe('$NAME', $$$BODY)",
274
+ 'it("$NAME", $$$BODY)',
275
+ "it('$NAME', $$$BODY)",
276
+ 'test("$NAME", $$$BODY)',
277
+ "test('$NAME', $$$BODY)",
278
+ ]) {
279
+ for (const m of rootNode.findAll(p)) {
280
+ const name = m.getMatch('NAME')?.text();
281
+ if (!name) continue;
282
+ const key = `t:${fp}:${name}:${m.range().start.line}`;
283
+ if (seen.has(key)) continue;
284
+ seen.add(key);
285
+ graph.tests.push({
286
+ name,
287
+ file: fp,
288
+ line_start: m.range().start.line,
289
+ line_end: m.range().end.line,
290
+ qualified: `${fp}::test:${name}`,
291
+ });
292
+ }
293
+ }
294
+ }
295
+
296
+ /**
297
+ * Extract raw call sites from a TypeScript/JavaScript AST.
298
+ * Finds DI calls (this.field.method) and direct calls ($CALLEE($$$ARGS)).
299
+ * Filters NOISE. Does NOT resolve — just collects raw sites.
300
+ */
301
+ export function extractCallsFromTypeScript(root: SgRoot, fp: string, calls: RawCallSite[]): void {
302
+ const rootNode = root.root();
303
+
304
+ // DI pattern: this.$FIELD.$METHOD($$$ARGS)
305
+ for (const m of rootNode.findAll('this.$FIELD.$METHOD($$$ARGS)')) {
306
+ const field = m.getMatch('FIELD')?.text();
307
+ const method = m.getMatch('METHOD')?.text();
308
+ if (!method || NOISE.has(method)) continue;
309
+ calls.push({
310
+ source: fp,
311
+ callName: method,
312
+ line: m.range().start.line,
313
+ diField: field,
314
+ });
315
+ }
316
+
317
+ // Direct calls: $CALLEE($$$ARGS)
318
+ for (const m of rootNode.findAll('$CALLEE($$$ARGS)')) {
319
+ const callee = m.getMatch('CALLEE')?.text();
320
+ if (!callee || callee.startsWith('this.')) continue;
321
+ const callName = callee.includes('.') ? callee.split('.').pop()! : callee;
322
+ if (NOISE.has(callName)) continue;
323
+ calls.push({
324
+ source: fp,
325
+ callName,
326
+ line: m.range().start.line,
327
+ });
328
+ }
329
+ }
@@ -0,0 +1,122 @@
1
+ import csharp from '@ast-grep/lang-csharp';
2
+ import go from '@ast-grep/lang-go';
3
+ import java from '@ast-grep/lang-java';
4
+ import php from '@ast-grep/lang-php';
5
+ import python from '@ast-grep/lang-python';
6
+ import ruby from '@ast-grep/lang-ruby';
7
+ import rust from '@ast-grep/lang-rust';
8
+ import { Lang, registerDynamicLanguage } from '@ast-grep/napi';
9
+
10
+ // Register dynamic languages at import time (side effect).
11
+ // This must happen before parseAsync can parse these languages.
12
+ registerDynamicLanguage({ python, ruby, go, java, rust, php, csharp });
13
+
14
+ // Extension -> language identifier
15
+ // Built-in langs use Lang enum, dynamic langs use lowercase string
16
+ const EXT_TO_LANG: Record<string, Lang | string> = {
17
+ '.ts': Lang.TypeScript,
18
+ '.tsx': Lang.Tsx,
19
+ '.js': Lang.JavaScript,
20
+ '.jsx': Lang.JavaScript,
21
+ '.mjs': Lang.JavaScript,
22
+ '.cjs': Lang.JavaScript,
23
+ '.py': 'python',
24
+ '.rb': 'ruby',
25
+ '.go': 'go',
26
+ '.java': 'java',
27
+ '.rs': 'rust',
28
+ '.cs': 'csharp',
29
+ '.php': 'php',
30
+ };
31
+
32
+ export function getLanguage(ext: string): Lang | string | null {
33
+ return EXT_TO_LANG[ext] ?? null;
34
+ }
35
+
36
+ export function getSupportedExtensions(): string[] {
37
+ return Object.keys(EXT_TO_LANG);
38
+ }
39
+
40
+ export function getLanguageName(lang: Lang | string): string {
41
+ if (typeof lang === 'string') return lang;
42
+ if (lang === Lang.TypeScript || lang === Lang.Tsx) return 'typescript';
43
+ if (lang === Lang.JavaScript) return 'javascript';
44
+ return 'unknown';
45
+ }
46
+
47
+ export function isTypeScriptLike(lang: Lang | string): boolean {
48
+ return lang === Lang.TypeScript || lang === Lang.Tsx || lang === Lang.JavaScript;
49
+ }
50
+
51
+ // AST node kinds per language for structural extraction
52
+ export const LANG_KINDS: Record<string, Record<string, string>> = {
53
+ typescript: {
54
+ class: 'class_declaration',
55
+ abstractClass: 'abstract_class_declaration',
56
+ method: 'method_definition',
57
+ function: 'function_declaration',
58
+ arrowContainer: 'variable_declarator',
59
+ arrowFunction: 'arrow_function',
60
+ interface: 'interface_declaration',
61
+ enum: 'enum_declaration',
62
+ import: 'import_statement',
63
+ export: 'export_statement',
64
+ methodSignature: 'method_signature',
65
+ },
66
+ python: {
67
+ class: 'class_definition',
68
+ method: 'function_definition',
69
+ function: 'function_definition',
70
+ import: 'import_from_statement',
71
+ importRegular: 'import_statement',
72
+ decorator: 'decorator',
73
+ },
74
+ ruby: {
75
+ class: 'class',
76
+ method: 'method',
77
+ singletonMethod: 'singleton_method',
78
+ module: 'module',
79
+ call: 'call',
80
+ },
81
+ go: {
82
+ function: 'function_declaration',
83
+ method: 'method_declaration',
84
+ struct: 'type_declaration',
85
+ interface: 'type_declaration',
86
+ import: 'import_declaration',
87
+ },
88
+ java: {
89
+ class: 'class_declaration',
90
+ interface: 'interface_declaration',
91
+ method: 'method_declaration',
92
+ constructor: 'constructor_declaration',
93
+ import: 'import_declaration',
94
+ enum: 'enum_declaration',
95
+ },
96
+ rust: {
97
+ function: 'function_item',
98
+ struct: 'struct_item',
99
+ impl: 'impl_item',
100
+ trait: 'trait_item',
101
+ enum: 'enum_item',
102
+ use: 'use_declaration',
103
+ },
104
+ csharp: {
105
+ class: 'class_declaration',
106
+ interface: 'interface_declaration',
107
+ method: 'method_declaration',
108
+ constructor: 'constructor_declaration',
109
+ using: 'using_directive',
110
+ enum: 'enum_declaration',
111
+ namespace: 'namespace_declaration',
112
+ },
113
+ php: {
114
+ class: 'class_declaration',
115
+ method: 'method_declaration',
116
+ function: 'function_definition',
117
+ namespace: 'namespace_definition',
118
+ use: 'namespace_use_declaration',
119
+ },
120
+ };
121
+
122
+ export { Lang };