@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.
package/README.md ADDED
@@ -0,0 +1,158 @@
1
+ # @flowmap/core
2
+
3
+ Language-agnostic call-graph analysis engine powered by [Tree-sitter](https://tree-sitter.github.io/tree-sitter/). Parse source files into a structured call graph with typed nodes, edges, and execution flows — entirely local, no LLM, no cloud.
4
+
5
+ Used by the [FlowMap VS Code extension](https://github.com/devricky-codes/flowmap-vscode-extension) to render interactive call-flow diagrams.
6
+
7
+ ## Supported Languages
8
+
9
+ | Language | Functions | Calls | Components/Hooks |
10
+ |------------|-----------|-------|------------------|
11
+ | TypeScript | ✅ | ✅ | ✅ |
12
+ | JavaScript | ✅ | ✅ | ✅ |
13
+ | TSX | ✅ | ✅ | ✅ |
14
+ | JSX | ✅ | ✅ | ✅ |
15
+ | Python | ✅ | ✅ | — |
16
+ | Go | ✅ | ✅ | — |
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install @flowmap/core
22
+ # or
23
+ pnpm add @flowmap/core
24
+ ```
25
+
26
+ > **Note:** `web-tree-sitter` requires WASM grammar files at runtime. You must provide a `wasmDirectory` pointing to a folder that contains the relevant `tree-sitter-<language>.wasm` files. These are available from the [`tree-sitter-<language>`](https://github.com/tree-sitter) grammar packages.
27
+
28
+ ## Usage
29
+
30
+ ### Parse a directory into a call graph
31
+
32
+ ```typescript
33
+ import { analyzeDirectory } from '@flowmap/core';
34
+
35
+ const graph = await analyzeDirectory({
36
+ rootPath: '/absolute/path/to/project',
37
+ wasmDirectory: '/path/to/wasm/grammars',
38
+ include: ['**/*.ts', '**/*.tsx'],
39
+ exclude: ['**/node_modules/**', '**/dist/**'],
40
+ });
41
+
42
+ console.log(`Scanned ${graph.scannedFiles} files in ${graph.durationMs}ms`);
43
+ console.log(`Found ${graph.nodes.length} functions, ${graph.edges.length} call edges`);
44
+ console.log(`Partitioned into ${graph.flows.length} flows`);
45
+ ```
46
+
47
+ ### Parse a single file
48
+
49
+ ```typescript
50
+ import { parseFile } from '@flowmap/core';
51
+
52
+ const { functions, calls } = await parseFile(
53
+ 'src/index.ts', // relative path (used as node IDs)
54
+ '/absolute/src/index.ts', // absolute path (for reading the file)
55
+ '/path/to/wasm', // wasmDirectory
56
+ 'typescript' // SupportedLanguage
57
+ );
58
+ ```
59
+
60
+ ### Build a call graph manually
61
+
62
+ ```typescript
63
+ import { buildCallGraph, detectEntryPoints, partitionFlows } from '@flowmap/core';
64
+
65
+ // nodes: FunctionNode[], rawCalls: RawCall[] — from parseFile()
66
+ const edges = buildCallGraph(nodes, rawCalls);
67
+ const nodesWithEntries = detectEntryPoints(nodes, edges);
68
+ const { flows, orphans } = partitionFlows(nodesWithEntries, edges);
69
+ ```
70
+
71
+ ## Data Model
72
+
73
+ ### `FunctionNode`
74
+
75
+ ```typescript
76
+ interface FunctionNode {
77
+ id: string; // "relative/path.ts::functionName::startLine"
78
+ name: string;
79
+ filePath: string; // relative to workspace root
80
+ startLine: number; // 0-indexed
81
+ endLine: number;
82
+ params: Parameter[];
83
+ returnType: string | null;
84
+ isAsync: boolean;
85
+ isExported: boolean;
86
+ isEntryPoint: boolean;
87
+ language: SupportedLanguage;
88
+ kind?: 'function' | 'component' | 'hook' | 'method' | 'class' | 'iife';
89
+ parentFunctionId?: string;
90
+ }
91
+ ```
92
+
93
+ ### `CallEdge`
94
+
95
+ ```typescript
96
+ interface CallEdge {
97
+ from: string; // caller FunctionNode.id
98
+ to: string; // callee FunctionNode.id
99
+ line: number;
100
+ callType?: 'direct' | 'ref' | 'concurrent' | 'goroutine';
101
+ }
102
+ ```
103
+
104
+ ### `Flow`
105
+
106
+ ```typescript
107
+ interface Flow {
108
+ id: string;
109
+ entryPoint: string; // FunctionNode.id of the root function
110
+ nodeIds: string[]; // all reachable function IDs in this flow
111
+ }
112
+ ```
113
+
114
+ ### `Graph`
115
+
116
+ ```typescript
117
+ interface Graph {
118
+ nodes: FunctionNode[];
119
+ edges: CallEdge[];
120
+ flows: Flow[];
121
+ orphans: string[]; // FunctionNode IDs with no connections
122
+ scannedFiles: number;
123
+ durationMs: number;
124
+ }
125
+ ```
126
+
127
+ ## API Reference
128
+
129
+ ### `analyzeDirectory(options): Promise<Graph>`
130
+
131
+ Scans a directory, parses all matched source files, and returns a complete `Graph`.
132
+
133
+ | Option | Type | Description |
134
+ |----------------|------------|--------------------------------------------------|
135
+ | `rootPath` | `string` | Absolute path to the project root |
136
+ | `wasmDirectory`| `string` | Directory containing `.wasm` grammar files |
137
+ | `include` | `string[]` | Glob patterns for files to include |
138
+ | `exclude` | `string[]` | Glob patterns for files to exclude |
139
+
140
+ ### `parseFile(relativePath, absolutePath, wasmDirectory, language): Promise<{functions, calls}>`
141
+
142
+ Parses a single source file using Tree-sitter.
143
+
144
+ ### `buildCallGraph(nodes, rawCalls): CallEdge[]`
145
+
146
+ Resolves raw call references into typed `CallEdge` objects.
147
+
148
+ ### `detectEntryPoints(nodes, edges): FunctionNode[]`
149
+
150
+ Marks functions with no callers (in-degree = 0) but at least one outgoing call as entry points.
151
+
152
+ ### `partitionFlows(nodes, edges): { flows: Flow[], orphans: string[] }`
153
+
154
+ Groups the call graph into independent execution flows using BFS from each entry point.
155
+
156
+ ## License
157
+
158
+ MIT
@@ -0,0 +1,50 @@
1
+ import { Parameter } from '../types';
2
+ export declare const JS_NESTED_FN_TYPES: string[];
3
+ export declare const PYTHON_NESTED_FN_TYPES: string[];
4
+ export declare const GO_NESTED_FN_TYPES: string[];
5
+ /**
6
+ * Walk an AST node collecting return statement values, skipping nested function scopes.
7
+ */
8
+ export declare function getReturnValues(node: any, skipNestedTypes: string[]): string[];
9
+ /**
10
+ * Walk parent chain to check for export_statement. Used by JS/TS family analyzers.
11
+ */
12
+ export declare function checkExportedByParentWalk(node: any): boolean;
13
+ /**
14
+ * Check if a declaration node has an `async` keyword child.
15
+ */
16
+ export declare function checkIsAsync(node: any): boolean;
17
+ /**
18
+ * Build parameter list from a formal_parameters AST node. Used by JS/TS family analyzers.
19
+ * @param paramsNode The formal_parameters AST node (may be null)
20
+ * @param includeTypes Whether to extract type annotations (true for TS/TSX, false for JS/JSX)
21
+ */
22
+ export declare function buildJsParams(paramsNode: any, includeTypes: boolean): Parameter[];
23
+ /**
24
+ * Walk parent chain to find an enclosing class (class_declaration or class_definition).
25
+ * Returns the class name or null.
26
+ */
27
+ export declare function getEnclosingClassName(node: any): string | null;
28
+ export declare const JS_FN_SCOPE_TYPES: string[];
29
+ export declare const JS_CLASS_SCOPE_TYPES: string[];
30
+ /**
31
+ * Walk parent chain collecting enclosing function and class scopes.
32
+ * Returns scopes from outermost to innermost.
33
+ */
34
+ export declare function getEnclosingScopeChain(node: any, functionTypes: string[], classTypes: string[]): {
35
+ name: string;
36
+ type: 'function' | 'class' | 'object';
37
+ }[];
38
+ /**
39
+ * Extract parameters from an arrow_function or function_expression node directly.
40
+ * Used when the query doesn't capture @fn.params (e.g., lexical_declaration patterns).
41
+ */
42
+ export declare function getParamsFromFunctionNode(node: any, includeTypes: boolean): Parameter[];
43
+ /**
44
+ * Check if a call_expression node is inside a Promise.all/race/allSettled array argument.
45
+ */
46
+ export declare function isInsidePromiseCombinator(node: any): boolean;
47
+ /**
48
+ * Find the enclosing function and construct its expected ID.
49
+ */
50
+ export declare function getEnclosingFunctionId(node: any, filePath: string): string | null;
@@ -0,0 +1,247 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.JS_CLASS_SCOPE_TYPES = exports.JS_FN_SCOPE_TYPES = exports.GO_NESTED_FN_TYPES = exports.PYTHON_NESTED_FN_TYPES = exports.JS_NESTED_FN_TYPES = void 0;
4
+ exports.getReturnValues = getReturnValues;
5
+ exports.checkExportedByParentWalk = checkExportedByParentWalk;
6
+ exports.checkIsAsync = checkIsAsync;
7
+ exports.buildJsParams = buildJsParams;
8
+ exports.getEnclosingClassName = getEnclosingClassName;
9
+ exports.getEnclosingScopeChain = getEnclosingScopeChain;
10
+ exports.getParamsFromFunctionNode = getParamsFromFunctionNode;
11
+ exports.isInsidePromiseCombinator = isInsidePromiseCombinator;
12
+ exports.getEnclosingFunctionId = getEnclosingFunctionId;
13
+ // Nested function/class types to skip when walking for return statements
14
+ exports.JS_NESTED_FN_TYPES = ['function_declaration', 'arrow_function', 'function_expression', 'method_definition', 'generator_function_declaration', 'generator_function', 'class_declaration', 'class'];
15
+ exports.PYTHON_NESTED_FN_TYPES = ['function_definition', 'class_definition'];
16
+ exports.GO_NESTED_FN_TYPES = ['function_declaration', 'method_declaration', 'func_literal'];
17
+ /**
18
+ * Walk an AST node collecting return statement values, skipping nested function scopes.
19
+ */
20
+ function getReturnValues(node, skipNestedTypes) {
21
+ const returns = [];
22
+ function walk(n) {
23
+ if (!n)
24
+ return;
25
+ if (n.type === 'return_statement') {
26
+ const returnClause = n.children.find((c) => c.type !== 'return' && c.type !== ';');
27
+ if (returnClause && !skipNestedTypes.includes(returnClause.type))
28
+ returns.push(returnClause.text);
29
+ }
30
+ else {
31
+ for (const child of n.children) {
32
+ if (n !== node && skipNestedTypes.includes(child.type))
33
+ continue;
34
+ walk(child);
35
+ }
36
+ }
37
+ }
38
+ walk(node);
39
+ return Array.from(new Set(returns));
40
+ }
41
+ /**
42
+ * Walk parent chain to check for export_statement. Used by JS/TS family analyzers.
43
+ */
44
+ function checkExportedByParentWalk(node) {
45
+ let current = node;
46
+ while (current && current.type !== 'program') {
47
+ if (current.type === 'export_statement')
48
+ return true;
49
+ current = current.parent;
50
+ }
51
+ return false;
52
+ }
53
+ /**
54
+ * Check if a declaration node has an `async` keyword child.
55
+ */
56
+ function checkIsAsync(node) {
57
+ for (const child of node.children) {
58
+ if (child.type === 'async')
59
+ return true;
60
+ }
61
+ return false;
62
+ }
63
+ /**
64
+ * Build parameter list from a formal_parameters AST node. Used by JS/TS family analyzers.
65
+ * @param paramsNode The formal_parameters AST node (may be null)
66
+ * @param includeTypes Whether to extract type annotations (true for TS/TSX, false for JS/JSX)
67
+ */
68
+ function buildJsParams(paramsNode, includeTypes) {
69
+ const params = [];
70
+ if (!paramsNode)
71
+ return params;
72
+ for (const child of paramsNode.namedChildren) {
73
+ if (child.type === 'identifier') {
74
+ params.push({ name: child.text, type: null });
75
+ }
76
+ else {
77
+ const pName = child.childForFieldName('pattern') || child.childForFieldName('name');
78
+ const pType = includeTypes ? child.childForFieldName('type') : null;
79
+ params.push({
80
+ name: pName ? pName.text : child.text,
81
+ type: pType ? pType.text.replace(/^:\s*/, '') : null
82
+ });
83
+ }
84
+ }
85
+ return params;
86
+ }
87
+ /**
88
+ * Walk parent chain to find an enclosing class (class_declaration or class_definition).
89
+ * Returns the class name or null.
90
+ */
91
+ function getEnclosingClassName(node) {
92
+ let parent = node.parent;
93
+ while (parent) {
94
+ if (parent.type === 'class_declaration' || parent.type === 'class_definition') {
95
+ const nameNode = parent.childForFieldName('name');
96
+ return nameNode ? nameNode.text : null;
97
+ }
98
+ parent = parent.parent;
99
+ }
100
+ return null;
101
+ }
102
+ exports.JS_FN_SCOPE_TYPES = ['function_declaration', 'method_definition', 'arrow_function', 'function_expression', 'generator_function_declaration', 'generator_function'];
103
+ exports.JS_CLASS_SCOPE_TYPES = ['class_declaration', 'class_definition', 'class'];
104
+ /**
105
+ * Walk parent chain collecting enclosing function and class scopes.
106
+ * Returns scopes from outermost to innermost.
107
+ */
108
+ function getEnclosingScopeChain(node, functionTypes, classTypes) {
109
+ const chain = [];
110
+ let current = node.parent;
111
+ while (current) {
112
+ if (functionTypes.includes(current.type)) {
113
+ let name = null;
114
+ const nameNode = current.childForFieldName('name');
115
+ if (nameNode) {
116
+ name = nameNode.text;
117
+ }
118
+ else if (['arrow_function', 'function_expression', 'generator_function'].includes(current.type)) {
119
+ const varDecl = current.parent;
120
+ if (varDecl?.type === 'variable_declarator') {
121
+ const nm = varDecl.childForFieldName('name');
122
+ if (nm)
123
+ name = nm.text;
124
+ }
125
+ } // Handle wrapper patterns: forwardRef, memo, etc.
126
+ // Walk up through arguments → call_expression chains for known wrappers
127
+ if (!name) {
128
+ let target = current.parent;
129
+ while (target?.type === 'arguments') {
130
+ const callExpr = target.parent;
131
+ if (callExpr?.type !== 'call_expression')
132
+ break;
133
+ const fn = callExpr.childForFieldName('function');
134
+ const isWrapper = fn && ((fn.type === 'identifier' && /^(forwardRef|memo)$/.test(fn.text)) ||
135
+ (fn.type === 'member_expression' && /^(forwardRef|memo)$/.test(fn.childForFieldName('property')?.text ?? '')));
136
+ if (!isWrapper)
137
+ break;
138
+ target = callExpr.parent;
139
+ }
140
+ if (target?.type === 'variable_declarator') {
141
+ const nm = target.childForFieldName('name');
142
+ if (nm)
143
+ name = nm.text;
144
+ }
145
+ }
146
+ if (name) {
147
+ chain.unshift({ name, type: 'function' });
148
+ }
149
+ }
150
+ else if (classTypes.includes(current.type)) {
151
+ const nameNode = current.childForFieldName('name');
152
+ if (nameNode) {
153
+ chain.unshift({ name: nameNode.text, type: 'class' });
154
+ }
155
+ }
156
+ else if (current.type === 'object') {
157
+ // Object literal assigned to a variable: const foo = { method() {} }
158
+ const varDecl = current.parent;
159
+ if (varDecl?.type === 'variable_declarator') {
160
+ const nm = varDecl.childForFieldName('name');
161
+ if (nm) {
162
+ chain.unshift({ name: nm.text, type: 'object' });
163
+ }
164
+ }
165
+ }
166
+ current = current.parent;
167
+ }
168
+ return chain;
169
+ }
170
+ /**
171
+ * Extract parameters from an arrow_function or function_expression node directly.
172
+ * Used when the query doesn't capture @fn.params (e.g., lexical_declaration patterns).
173
+ */
174
+ function getParamsFromFunctionNode(node, includeTypes) {
175
+ const formalParams = node.childForFieldName('parameters');
176
+ if (formalParams && formalParams.type === 'formal_parameters') {
177
+ return buildJsParams(formalParams, includeTypes);
178
+ }
179
+ const singleParam = node.childForFieldName('parameter');
180
+ if (singleParam && singleParam.type === 'identifier') {
181
+ return [{ name: singleParam.text, type: null }];
182
+ }
183
+ return [];
184
+ }
185
+ /**
186
+ * Check if a call_expression node is inside a Promise.all/race/allSettled array argument.
187
+ */
188
+ function isInsidePromiseCombinator(node) {
189
+ let current = node.parent;
190
+ while (current) {
191
+ if (current.type === 'array') {
192
+ const argsParent = current.parent;
193
+ if (argsParent?.type === 'arguments') {
194
+ const callParent = argsParent.parent;
195
+ if (callParent?.type === 'call_expression') {
196
+ const fn = callParent.childForFieldName('function');
197
+ if (fn?.type === 'member_expression') {
198
+ const obj = fn.childForFieldName('object');
199
+ const prop = fn.childForFieldName('property');
200
+ if (obj?.text === 'Promise' && ['all', 'race', 'allSettled'].includes(prop?.text)) {
201
+ return true;
202
+ }
203
+ }
204
+ }
205
+ }
206
+ }
207
+ current = current.parent;
208
+ }
209
+ return false;
210
+ }
211
+ /**
212
+ * Find the enclosing function and construct its expected ID.
213
+ */
214
+ function getEnclosingFunctionId(node, filePath) {
215
+ const fnTypes = ['function_declaration', 'method_definition', 'arrow_function', 'function_expression',
216
+ 'generator_function_declaration', 'generator_function'];
217
+ let parent = node.parent;
218
+ while (parent) {
219
+ if (fnTypes.includes(parent.type)) {
220
+ let parentName = null;
221
+ if (parent.type === 'function_declaration' || parent.type === 'generator_function_declaration') {
222
+ const nameNode = parent.childForFieldName('name');
223
+ parentName = nameNode?.text ?? null;
224
+ }
225
+ else if (parent.type === 'method_definition') {
226
+ const nameNode = parent.childForFieldName('name');
227
+ parentName = nameNode?.text ?? null;
228
+ const className = getEnclosingClassName(parent);
229
+ if (className && parentName)
230
+ parentName = `${className}.${parentName}`;
231
+ }
232
+ else if (parent.type === 'arrow_function' || parent.type === 'function_expression'
233
+ || parent.type === 'generator_function') {
234
+ const varDecl = parent.parent;
235
+ if (varDecl?.type === 'variable_declarator') {
236
+ const nameNode = varDecl.childForFieldName('name');
237
+ parentName = nameNode?.text ?? null;
238
+ }
239
+ }
240
+ if (parentName) {
241
+ return `${filePath}::${parentName}::${parent.startPosition.row}`;
242
+ }
243
+ }
244
+ parent = parent.parent;
245
+ }
246
+ return null;
247
+ }
@@ -0,0 +1,9 @@
1
+ import { FunctionNode, RawCall, SupportedLanguage } from '../types';
2
+ export declare function parseFile(filePath: string, absolutePath: string, wasmDirectory: string, languageId: SupportedLanguage): Promise<{
3
+ functions: FunctionNode[];
4
+ calls: RawCall[];
5
+ }>;
6
+ export declare function parseFileContent(filePath: string, content: string, wasmDirectory: string, languageId: SupportedLanguage): Promise<{
7
+ functions: FunctionNode[];
8
+ calls: RawCall[];
9
+ }>;
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.parseFile = parseFile;
40
+ exports.parseFileContent = parseFileContent;
41
+ // TODO v0.2: cross-file import resolution
42
+ const fs = __importStar(require("fs/promises"));
43
+ const web_tree_sitter_1 = __importDefault(require("web-tree-sitter"));
44
+ const treeSitter_1 = require("./treeSitter");
45
+ const typescript_1 = require("./languages/typescript");
46
+ const javascript_1 = require("./languages/javascript");
47
+ const tsx_1 = require("./languages/tsx");
48
+ const jsx_1 = require("./languages/jsx");
49
+ const go_1 = require("./languages/go");
50
+ const python_1 = require("./languages/python");
51
+ const ANALYZERS = {
52
+ typescript: typescript_1.typescriptAnalyzer,
53
+ tsx: tsx_1.tsxAnalyzer,
54
+ javascript: javascript_1.javascriptAnalyzer,
55
+ jsx: jsx_1.jsxAnalyzer,
56
+ python: python_1.pythonAnalyzer,
57
+ java: null,
58
+ go: go_1.goAnalyzer,
59
+ rust: null
60
+ };
61
+ async function parseFile(filePath, absolutePath, wasmDirectory, languageId) {
62
+ const analyzer = ANALYZERS[languageId];
63
+ if (!analyzer)
64
+ return { functions: [], calls: [] };
65
+ const content = await fs.readFile(absolutePath, 'utf8');
66
+ const treeSitterLang = await (0, treeSitter_1.loadLanguage)(languageId, wasmDirectory);
67
+ const parser = new web_tree_sitter_1.default();
68
+ parser.setLanguage(treeSitterLang);
69
+ const tree = parser.parse(content);
70
+ let functionQuery = null;
71
+ let callQuery = null;
72
+ try {
73
+ functionQuery = treeSitterLang.query(analyzer.functionQuery);
74
+ callQuery = treeSitterLang.query(analyzer.callQuery);
75
+ const functions = [];
76
+ const calls = [];
77
+ for (const match of functionQuery.matches(tree.rootNode)) {
78
+ const fn = analyzer.extractFunction(match, filePath, languageId);
79
+ if (fn)
80
+ functions.push(fn);
81
+ }
82
+ for (const match of callQuery.matches(tree.rootNode)) {
83
+ const call = analyzer.extractCall(match, filePath);
84
+ if (call) {
85
+ if (Array.isArray(call)) {
86
+ calls.push(...call);
87
+ }
88
+ else {
89
+ calls.push(call);
90
+ }
91
+ }
92
+ }
93
+ return { functions, calls };
94
+ }
95
+ finally {
96
+ if (functionQuery)
97
+ functionQuery.delete();
98
+ if (callQuery)
99
+ callQuery.delete();
100
+ if (tree)
101
+ tree.delete();
102
+ if (parser)
103
+ parser.delete();
104
+ }
105
+ }
106
+ async function parseFileContent(filePath, content, wasmDirectory, languageId) {
107
+ const analyzer = ANALYZERS[languageId];
108
+ if (!analyzer)
109
+ return { functions: [], calls: [] };
110
+ const treeSitterLang = await (0, treeSitter_1.loadLanguage)(languageId, wasmDirectory);
111
+ const parser = new web_tree_sitter_1.default();
112
+ parser.setLanguage(treeSitterLang);
113
+ const tree = parser.parse(content);
114
+ let functionQuery = null;
115
+ let callQuery = null;
116
+ try {
117
+ functionQuery = treeSitterLang.query(analyzer.functionQuery);
118
+ callQuery = treeSitterLang.query(analyzer.callQuery);
119
+ const functions = [];
120
+ const calls = [];
121
+ for (const match of functionQuery.matches(tree.rootNode)) {
122
+ const fn = analyzer.extractFunction(match, filePath, languageId);
123
+ if (fn)
124
+ functions.push(fn);
125
+ }
126
+ for (const match of callQuery.matches(tree.rootNode)) {
127
+ const call = analyzer.extractCall(match, filePath);
128
+ if (call) {
129
+ if (Array.isArray(call)) {
130
+ calls.push(...call);
131
+ }
132
+ else {
133
+ calls.push(call);
134
+ }
135
+ }
136
+ }
137
+ return { functions, calls };
138
+ }
139
+ finally {
140
+ if (functionQuery)
141
+ functionQuery.delete();
142
+ if (callQuery)
143
+ callQuery.delete();
144
+ if (tree)
145
+ tree.delete();
146
+ if (parser)
147
+ parser.delete();
148
+ }
149
+ }
@@ -0,0 +1,2 @@
1
+ import { LanguageAnalyzer } from '../../types';
2
+ export declare const goAnalyzer: LanguageAnalyzer;