@zuvia-software-solutions/code-mapper 1.4.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 +215 -0
- package/dist/cli/ai-context.d.ts +19 -0
- package/dist/cli/ai-context.js +168 -0
- package/dist/cli/analyze.d.ts +7 -0
- package/dist/cli/analyze.js +325 -0
- package/dist/cli/augment.d.ts +7 -0
- package/dist/cli/augment.js +27 -0
- package/dist/cli/clean.d.ts +5 -0
- package/dist/cli/clean.js +56 -0
- package/dist/cli/eval-server.d.ts +25 -0
- package/dist/cli/eval-server.js +365 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.js +102 -0
- package/dist/cli/lazy-action.d.ts +6 -0
- package/dist/cli/lazy-action.js +19 -0
- package/dist/cli/list.d.ts +2 -0
- package/dist/cli/list.js +27 -0
- package/dist/cli/mcp.d.ts +8 -0
- package/dist/cli/mcp.js +35 -0
- package/dist/cli/refresh.d.ts +12 -0
- package/dist/cli/refresh.js +165 -0
- package/dist/cli/serve.d.ts +5 -0
- package/dist/cli/serve.js +8 -0
- package/dist/cli/setup.d.ts +6 -0
- package/dist/cli/setup.js +218 -0
- package/dist/cli/status.d.ts +2 -0
- package/dist/cli/status.js +33 -0
- package/dist/cli/tool.d.ts +28 -0
- package/dist/cli/tool.js +87 -0
- package/dist/config/ignore-service.d.ts +32 -0
- package/dist/config/ignore-service.js +282 -0
- package/dist/config/supported-languages.d.ts +23 -0
- package/dist/config/supported-languages.js +52 -0
- package/dist/core/augmentation/engine.d.ts +22 -0
- package/dist/core/augmentation/engine.js +232 -0
- package/dist/core/embeddings/embedder.d.ts +35 -0
- package/dist/core/embeddings/embedder.js +171 -0
- package/dist/core/embeddings/embedding-pipeline.d.ts +41 -0
- package/dist/core/embeddings/embedding-pipeline.js +402 -0
- package/dist/core/embeddings/index.d.ts +5 -0
- package/dist/core/embeddings/index.js +6 -0
- package/dist/core/embeddings/text-generator.d.ts +20 -0
- package/dist/core/embeddings/text-generator.js +159 -0
- package/dist/core/embeddings/types.d.ts +60 -0
- package/dist/core/embeddings/types.js +23 -0
- package/dist/core/graph/graph.d.ts +4 -0
- package/dist/core/graph/graph.js +65 -0
- package/dist/core/graph/types.d.ts +69 -0
- package/dist/core/graph/types.js +3 -0
- package/dist/core/incremental/child-process.d.ts +8 -0
- package/dist/core/incremental/child-process.js +649 -0
- package/dist/core/incremental/refresh-coordinator.d.ts +32 -0
- package/dist/core/incremental/refresh-coordinator.js +147 -0
- package/dist/core/incremental/types.d.ts +78 -0
- package/dist/core/incremental/types.js +153 -0
- package/dist/core/incremental/watcher.d.ts +63 -0
- package/dist/core/incremental/watcher.js +338 -0
- package/dist/core/ingestion/ast-cache.d.ts +12 -0
- package/dist/core/ingestion/ast-cache.js +34 -0
- package/dist/core/ingestion/call-processor.d.ts +34 -0
- package/dist/core/ingestion/call-processor.js +937 -0
- package/dist/core/ingestion/call-routing.d.ts +40 -0
- package/dist/core/ingestion/call-routing.js +97 -0
- package/dist/core/ingestion/cluster-enricher.d.ts +30 -0
- package/dist/core/ingestion/cluster-enricher.js +151 -0
- package/dist/core/ingestion/community-processor.d.ts +26 -0
- package/dist/core/ingestion/community-processor.js +272 -0
- package/dist/core/ingestion/constants.d.ts +5 -0
- package/dist/core/ingestion/constants.js +8 -0
- package/dist/core/ingestion/entry-point-scoring.d.ts +23 -0
- package/dist/core/ingestion/entry-point-scoring.js +317 -0
- package/dist/core/ingestion/export-detection.d.ts +11 -0
- package/dist/core/ingestion/export-detection.js +203 -0
- package/dist/core/ingestion/filesystem-walker.d.ts +18 -0
- package/dist/core/ingestion/filesystem-walker.js +64 -0
- package/dist/core/ingestion/framework-detection.d.ts +42 -0
- package/dist/core/ingestion/framework-detection.js +405 -0
- package/dist/core/ingestion/heritage-processor.d.ts +15 -0
- package/dist/core/ingestion/heritage-processor.js +237 -0
- package/dist/core/ingestion/import-processor.d.ts +31 -0
- package/dist/core/ingestion/import-processor.js +416 -0
- package/dist/core/ingestion/language-config.d.ts +32 -0
- package/dist/core/ingestion/language-config.js +161 -0
- package/dist/core/ingestion/mro-processor.d.ts +32 -0
- package/dist/core/ingestion/mro-processor.js +343 -0
- package/dist/core/ingestion/named-binding-extraction.d.ts +51 -0
- package/dist/core/ingestion/named-binding-extraction.js +343 -0
- package/dist/core/ingestion/parsing-processor.d.ts +20 -0
- package/dist/core/ingestion/parsing-processor.js +282 -0
- package/dist/core/ingestion/pipeline.d.ts +3 -0
- package/dist/core/ingestion/pipeline.js +416 -0
- package/dist/core/ingestion/process-processor.d.ts +42 -0
- package/dist/core/ingestion/process-processor.js +357 -0
- package/dist/core/ingestion/resolution-context.d.ts +40 -0
- package/dist/core/ingestion/resolution-context.js +171 -0
- package/dist/core/ingestion/resolvers/csharp.d.ts +10 -0
- package/dist/core/ingestion/resolvers/csharp.js +101 -0
- package/dist/core/ingestion/resolvers/go.d.ts +8 -0
- package/dist/core/ingestion/resolvers/go.js +33 -0
- package/dist/core/ingestion/resolvers/index.d.ts +14 -0
- package/dist/core/ingestion/resolvers/index.js +10 -0
- package/dist/core/ingestion/resolvers/jvm.d.ts +9 -0
- package/dist/core/ingestion/resolvers/jvm.js +74 -0
- package/dist/core/ingestion/resolvers/php.d.ts +7 -0
- package/dist/core/ingestion/resolvers/php.js +30 -0
- package/dist/core/ingestion/resolvers/ruby.d.ts +9 -0
- package/dist/core/ingestion/resolvers/ruby.js +13 -0
- package/dist/core/ingestion/resolvers/rust.d.ts +5 -0
- package/dist/core/ingestion/resolvers/rust.js +62 -0
- package/dist/core/ingestion/resolvers/standard.d.ts +16 -0
- package/dist/core/ingestion/resolvers/standard.js +144 -0
- package/dist/core/ingestion/resolvers/utils.d.ts +18 -0
- package/dist/core/ingestion/resolvers/utils.js +113 -0
- package/dist/core/ingestion/structure-processor.d.ts +4 -0
- package/dist/core/ingestion/structure-processor.js +39 -0
- package/dist/core/ingestion/symbol-table.d.ts +34 -0
- package/dist/core/ingestion/symbol-table.js +48 -0
- package/dist/core/ingestion/tree-sitter-queries.d.ts +20 -0
- package/dist/core/ingestion/tree-sitter-queries.js +691 -0
- package/dist/core/ingestion/type-env.d.ts +52 -0
- package/dist/core/ingestion/type-env.js +349 -0
- package/dist/core/ingestion/type-extractors/c-cpp.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/c-cpp.js +214 -0
- package/dist/core/ingestion/type-extractors/csharp.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/csharp.js +224 -0
- package/dist/core/ingestion/type-extractors/go.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/go.js +261 -0
- package/dist/core/ingestion/type-extractors/index.d.ts +20 -0
- package/dist/core/ingestion/type-extractors/index.js +30 -0
- package/dist/core/ingestion/type-extractors/jvm.d.ts +5 -0
- package/dist/core/ingestion/type-extractors/jvm.js +386 -0
- package/dist/core/ingestion/type-extractors/php.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/php.js +280 -0
- package/dist/core/ingestion/type-extractors/python.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/python.js +175 -0
- package/dist/core/ingestion/type-extractors/ruby.d.ts +12 -0
- package/dist/core/ingestion/type-extractors/ruby.js +218 -0
- package/dist/core/ingestion/type-extractors/rust.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/rust.js +290 -0
- package/dist/core/ingestion/type-extractors/shared.d.ts +81 -0
- package/dist/core/ingestion/type-extractors/shared.js +322 -0
- package/dist/core/ingestion/type-extractors/swift.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/swift.js +140 -0
- package/dist/core/ingestion/type-extractors/types.d.ts +111 -0
- package/dist/core/ingestion/type-extractors/types.js +4 -0
- package/dist/core/ingestion/type-extractors/typescript.d.ts +4 -0
- package/dist/core/ingestion/type-extractors/typescript.js +227 -0
- package/dist/core/ingestion/utils.d.ts +73 -0
- package/dist/core/ingestion/utils.js +992 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +99 -0
- package/dist/core/ingestion/workers/parse-worker.js +1055 -0
- package/dist/core/ingestion/workers/worker-pool.d.ts +15 -0
- package/dist/core/ingestion/workers/worker-pool.js +123 -0
- package/dist/core/lbug/csv-generator.d.ts +28 -0
- package/dist/core/lbug/csv-generator.js +355 -0
- package/dist/core/lbug/lbug-adapter.d.ts +96 -0
- package/dist/core/lbug/lbug-adapter.js +753 -0
- package/dist/core/lbug/schema.d.ts +46 -0
- package/dist/core/lbug/schema.js +402 -0
- package/dist/core/search/bm25-index.d.ts +20 -0
- package/dist/core/search/bm25-index.js +123 -0
- package/dist/core/search/hybrid-search.d.ts +32 -0
- package/dist/core/search/hybrid-search.js +131 -0
- package/dist/core/search/query-cache.d.ts +18 -0
- package/dist/core/search/query-cache.js +47 -0
- package/dist/core/search/query-expansion.d.ts +19 -0
- package/dist/core/search/query-expansion.js +75 -0
- package/dist/core/search/reranker.d.ts +29 -0
- package/dist/core/search/reranker.js +122 -0
- package/dist/core/search/types.d.ts +154 -0
- package/dist/core/search/types.js +51 -0
- package/dist/core/semantic/tsgo-service.d.ts +67 -0
- package/dist/core/semantic/tsgo-service.js +355 -0
- package/dist/core/tree-sitter/parser-loader.d.ts +12 -0
- package/dist/core/tree-sitter/parser-loader.js +71 -0
- package/dist/lib/memory-guard.d.ts +35 -0
- package/dist/lib/memory-guard.js +70 -0
- package/dist/lib/utils.d.ts +3 -0
- package/dist/lib/utils.js +6 -0
- package/dist/mcp/compatible-stdio-transport.d.ts +32 -0
- package/dist/mcp/compatible-stdio-transport.js +209 -0
- package/dist/mcp/core/embedder.d.ts +24 -0
- package/dist/mcp/core/embedder.js +168 -0
- package/dist/mcp/core/lbug-adapter.d.ts +29 -0
- package/dist/mcp/core/lbug-adapter.js +330 -0
- package/dist/mcp/local/local-backend.d.ts +188 -0
- package/dist/mcp/local/local-backend.js +2759 -0
- package/dist/mcp/resources.d.ts +22 -0
- package/dist/mcp/resources.js +379 -0
- package/dist/mcp/server.d.ts +10 -0
- package/dist/mcp/server.js +217 -0
- package/dist/mcp/staleness.d.ts +10 -0
- package/dist/mcp/staleness.js +25 -0
- package/dist/mcp/tools.d.ts +21 -0
- package/dist/mcp/tools.js +202 -0
- package/dist/server/api.d.ts +5 -0
- package/dist/server/api.js +340 -0
- package/dist/server/mcp-http.d.ts +7 -0
- package/dist/server/mcp-http.js +95 -0
- package/dist/storage/git.d.ts +6 -0
- package/dist/storage/git.js +35 -0
- package/dist/storage/repo-manager.d.ts +87 -0
- package/dist/storage/repo-manager.js +249 -0
- package/dist/types/pipeline.d.ts +35 -0
- package/dist/types/pipeline.js +20 -0
- package/hooks/claude/code-mapper-hook.cjs +238 -0
- package/hooks/claude/pre-tool-use.sh +79 -0
- package/hooks/claude/session-start.sh +42 -0
- package/models/mlx-embedder.py +185 -0
- package/package.json +100 -0
- package/scripts/patch-tree-sitter-swift.cjs +74 -0
- package/vendor/leiden/index.cjs +355 -0
- package/vendor/leiden/utils.cjs +392 -0
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
// code-mapper/src/core/ingestion/named-binding-extraction.ts
|
|
2
|
+
/** @file named-binding-extraction.ts @description Extracts named import bindings from AST import nodes and walks re-export chains through NamedImportMap to resolve barrel file indirection */
|
|
3
|
+
import { SupportedLanguages } from '../../config/supported-languages.js';
|
|
4
|
+
/**
|
|
5
|
+
* Walk a named-binding re-export chain through NamedImportMap
|
|
6
|
+
*
|
|
7
|
+
* Follows A->B->C chains until a definition is found (max depth 5)
|
|
8
|
+
* Returns null if the chain breaks (missing binding, circular reference, or depth exceeded)
|
|
9
|
+
*
|
|
10
|
+
* @param allDefs - Complete unfiltered lookupFuzzy(name) result; file-filtered subsets cause silent misses
|
|
11
|
+
*/
|
|
12
|
+
export function walkBindingChain(name, currentFilePath, symbolTable, namedImportMap, allDefs) {
|
|
13
|
+
let lookupFile = currentFilePath;
|
|
14
|
+
let lookupName = name;
|
|
15
|
+
const visited = new Set();
|
|
16
|
+
for (let depth = 0; depth < 5; depth++) {
|
|
17
|
+
const bindings = namedImportMap.get(lookupFile);
|
|
18
|
+
if (!bindings)
|
|
19
|
+
return null;
|
|
20
|
+
const binding = bindings.get(lookupName);
|
|
21
|
+
if (!binding)
|
|
22
|
+
return null;
|
|
23
|
+
const key = `${binding.sourcePath}:${binding.exportedName}`;
|
|
24
|
+
if (visited.has(key))
|
|
25
|
+
return null;
|
|
26
|
+
visited.add(key);
|
|
27
|
+
const targetName = binding.exportedName;
|
|
28
|
+
const resolvedDefs = targetName !== lookupName || depth > 0
|
|
29
|
+
? symbolTable.lookupFuzzy(targetName).filter(def => def.filePath === binding.sourcePath)
|
|
30
|
+
: allDefs.filter(def => def.filePath === binding.sourcePath);
|
|
31
|
+
if (resolvedDefs.length > 0)
|
|
32
|
+
return resolvedDefs;
|
|
33
|
+
// No definition in source file → follow re-export chain
|
|
34
|
+
lookupFile = binding.sourcePath;
|
|
35
|
+
lookupName = targetName;
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Extract named bindings from an import AST node
|
|
41
|
+
*
|
|
42
|
+
* Returns undefined for non-named imports (import *, default, side-effect)
|
|
43
|
+
* Example: `import { User, Repo as R }` -> [{local:'User', exported:'User'}, {local:'R', exported:'Repo'}]
|
|
44
|
+
*/
|
|
45
|
+
export function extractNamedBindings(importNode, language) {
|
|
46
|
+
if (language === SupportedLanguages.TypeScript || language === SupportedLanguages.JavaScript) {
|
|
47
|
+
return extractTsNamedBindings(importNode);
|
|
48
|
+
}
|
|
49
|
+
if (language === SupportedLanguages.Python) {
|
|
50
|
+
return extractPythonNamedBindings(importNode);
|
|
51
|
+
}
|
|
52
|
+
if (language === SupportedLanguages.Kotlin) {
|
|
53
|
+
return extractKotlinNamedBindings(importNode);
|
|
54
|
+
}
|
|
55
|
+
if (language === SupportedLanguages.Rust) {
|
|
56
|
+
return extractRustNamedBindings(importNode);
|
|
57
|
+
}
|
|
58
|
+
if (language === SupportedLanguages.PHP) {
|
|
59
|
+
return extractPhpNamedBindings(importNode);
|
|
60
|
+
}
|
|
61
|
+
if (language === SupportedLanguages.CSharp) {
|
|
62
|
+
return extractCsharpNamedBindings(importNode);
|
|
63
|
+
}
|
|
64
|
+
if (language === SupportedLanguages.Java) {
|
|
65
|
+
return extractJavaNamedBindings(importNode);
|
|
66
|
+
}
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
// TS/JS: import_statement > import_clause > named_imports > import_specifier*
|
|
70
|
+
export function extractTsNamedBindings(importNode) {
|
|
71
|
+
const importClause = findChild(importNode, 'import_clause');
|
|
72
|
+
if (importClause) {
|
|
73
|
+
const namedImports = findChild(importClause, 'named_imports');
|
|
74
|
+
if (!namedImports)
|
|
75
|
+
return undefined;
|
|
76
|
+
const bindings = [];
|
|
77
|
+
for (let i = 0; i < namedImports.namedChildCount; i++) {
|
|
78
|
+
const specifier = namedImports.namedChild(i);
|
|
79
|
+
if (specifier?.type !== 'import_specifier')
|
|
80
|
+
continue;
|
|
81
|
+
const identifiers = [];
|
|
82
|
+
for (let j = 0; j < specifier.namedChildCount; j++) {
|
|
83
|
+
const child = specifier.namedChild(j);
|
|
84
|
+
if (child?.type === 'identifier')
|
|
85
|
+
identifiers.push(child.text);
|
|
86
|
+
}
|
|
87
|
+
if (identifiers.length === 1) {
|
|
88
|
+
bindings.push({ local: identifiers[0], exported: identifiers[0] });
|
|
89
|
+
}
|
|
90
|
+
else if (identifiers.length === 2) {
|
|
91
|
+
// import { Foo as Bar } -> exported='Foo', local='Bar'
|
|
92
|
+
bindings.push({ local: identifiers[1], exported: identifiers[0] });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return bindings.length > 0 ? bindings : undefined;
|
|
96
|
+
}
|
|
97
|
+
// Re-export: export { X } from './y'
|
|
98
|
+
const exportClause = findChild(importNode, 'export_clause');
|
|
99
|
+
if (exportClause) {
|
|
100
|
+
const bindings = [];
|
|
101
|
+
for (let i = 0; i < exportClause.namedChildCount; i++) {
|
|
102
|
+
const specifier = exportClause.namedChild(i);
|
|
103
|
+
if (specifier?.type !== 'export_specifier')
|
|
104
|
+
continue;
|
|
105
|
+
const identifiers = [];
|
|
106
|
+
for (let j = 0; j < specifier.namedChildCount; j++) {
|
|
107
|
+
const child = specifier.namedChild(j);
|
|
108
|
+
if (child?.type === 'identifier')
|
|
109
|
+
identifiers.push(child.text);
|
|
110
|
+
}
|
|
111
|
+
if (identifiers.length === 1) {
|
|
112
|
+
bindings.push({ local: identifiers[0], exported: identifiers[0] });
|
|
113
|
+
}
|
|
114
|
+
else if (identifiers.length === 2) {
|
|
115
|
+
// export { Repo as Repository } -> first is source name, second is exported alias
|
|
116
|
+
bindings.push({ local: identifiers[1], exported: identifiers[0] });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return bindings.length > 0 ? bindings : undefined;
|
|
120
|
+
}
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
// Python: from_import_statement only (not plain import_statement)
|
|
124
|
+
export function extractPythonNamedBindings(importNode) {
|
|
125
|
+
if (importNode.type !== 'import_from_statement')
|
|
126
|
+
return undefined;
|
|
127
|
+
const bindings = [];
|
|
128
|
+
for (let i = 0; i < importNode.namedChildCount; i++) {
|
|
129
|
+
const child = importNode.namedChild(i);
|
|
130
|
+
if (!child)
|
|
131
|
+
continue;
|
|
132
|
+
if (child.type === 'dotted_name') {
|
|
133
|
+
// Skip the module_name (first dotted_name)
|
|
134
|
+
const fieldName = importNode.childForFieldName?.('module_name');
|
|
135
|
+
if (fieldName && child.startIndex === fieldName.startIndex)
|
|
136
|
+
continue;
|
|
137
|
+
// This is an imported name: from x import User
|
|
138
|
+
const name = child.text;
|
|
139
|
+
if (name)
|
|
140
|
+
bindings.push({ local: name, exported: name });
|
|
141
|
+
}
|
|
142
|
+
if (child.type === 'aliased_import') {
|
|
143
|
+
// from x import Repo as R
|
|
144
|
+
const dottedName = findChild(child, 'dotted_name');
|
|
145
|
+
const aliasIdent = findChild(child, 'identifier');
|
|
146
|
+
if (dottedName && aliasIdent) {
|
|
147
|
+
bindings.push({ local: aliasIdent.text, exported: dottedName.text });
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return bindings.length > 0 ? bindings : undefined;
|
|
152
|
+
}
|
|
153
|
+
// Kotlin: import_header > identifier + import_alias > simple_identifier
|
|
154
|
+
export function extractKotlinNamedBindings(importNode) {
|
|
155
|
+
if (importNode.type !== 'import_header')
|
|
156
|
+
return undefined;
|
|
157
|
+
const fullIdent = findChild(importNode, 'identifier');
|
|
158
|
+
if (!fullIdent)
|
|
159
|
+
return undefined;
|
|
160
|
+
const fullText = fullIdent.text;
|
|
161
|
+
const exportedName = fullText.includes('.') ? fullText.split('.').pop() : fullText;
|
|
162
|
+
const importAlias = findChild(importNode, 'import_alias');
|
|
163
|
+
if (importAlias) {
|
|
164
|
+
// Aliased: import com.example.User as U
|
|
165
|
+
const aliasIdent = findChild(importAlias, 'simple_identifier');
|
|
166
|
+
if (!aliasIdent)
|
|
167
|
+
return undefined;
|
|
168
|
+
return [{ local: aliasIdent.text, exported: exportedName }];
|
|
169
|
+
}
|
|
170
|
+
// Non-aliased: import com.example.User -> local="User", exported="User"
|
|
171
|
+
if (fullText.endsWith('.*') || fullText.endsWith('*'))
|
|
172
|
+
return undefined;
|
|
173
|
+
// Skip lowercase last segments — member/function imports would collide in NamedImportMap
|
|
174
|
+
if (exportedName[0] && exportedName[0] === exportedName[0].toLowerCase())
|
|
175
|
+
return undefined;
|
|
176
|
+
return [{ local: exportedName, exported: exportedName }];
|
|
177
|
+
}
|
|
178
|
+
// Rust: use_declaration may contain use_as_clause at any depth
|
|
179
|
+
export function extractRustNamedBindings(importNode) {
|
|
180
|
+
if (importNode.type !== 'use_declaration')
|
|
181
|
+
return undefined;
|
|
182
|
+
const bindings = [];
|
|
183
|
+
collectRustBindings(importNode, bindings);
|
|
184
|
+
return bindings.length > 0 ? bindings : undefined;
|
|
185
|
+
}
|
|
186
|
+
function collectRustBindings(node, bindings) {
|
|
187
|
+
if (node.type === 'use_as_clause') {
|
|
188
|
+
// First identifier = exported name, second identifier = local alias
|
|
189
|
+
const idents = [];
|
|
190
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
191
|
+
const child = node.namedChild(i);
|
|
192
|
+
if (child?.type === 'identifier')
|
|
193
|
+
idents.push(child.text);
|
|
194
|
+
// For scoped_identifier, extract the last segment
|
|
195
|
+
if (child?.type === 'scoped_identifier') {
|
|
196
|
+
const nameNode = child.childForFieldName?.('name');
|
|
197
|
+
if (nameNode)
|
|
198
|
+
idents.push(nameNode.text);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (idents.length === 2) {
|
|
202
|
+
bindings.push({ local: idents[1], exported: idents[0] });
|
|
203
|
+
}
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
// Terminal identifier in a use_list: use crate::models::{User, Repo}
|
|
207
|
+
if (node.type === 'identifier' && node.parent?.type === 'use_list') {
|
|
208
|
+
bindings.push({ local: node.text, exported: node.text });
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
// Skip path prefix in scoped_use_list (e.g. "crate::models" in use crate::models::{User, Repo})
|
|
212
|
+
if (node.type === 'scoped_identifier' && node.parent?.type === 'scoped_use_list') {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
// Terminal scoped_identifier (leaf with no deeper use_list/use_as_clause)
|
|
216
|
+
if (node.type === 'scoped_identifier') {
|
|
217
|
+
let hasDeeper = false;
|
|
218
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
219
|
+
const child = node.namedChild(i);
|
|
220
|
+
if (child?.type === 'use_list' || child?.type === 'use_as_clause' || child?.type === 'scoped_use_list') {
|
|
221
|
+
hasDeeper = true;
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (!hasDeeper) {
|
|
226
|
+
const nameNode = node.childForFieldName?.('name');
|
|
227
|
+
if (nameNode) {
|
|
228
|
+
bindings.push({ local: nameNode.text, exported: nameNode.text });
|
|
229
|
+
}
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// Recurse into children
|
|
234
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
235
|
+
const child = node.namedChild(i);
|
|
236
|
+
if (child)
|
|
237
|
+
collectRustBindings(child, bindings);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
// PHP: namespace_use_declaration > namespace_use_clause* (flat or grouped)
|
|
241
|
+
export function extractPhpNamedBindings(importNode) {
|
|
242
|
+
if (importNode.type !== 'namespace_use_declaration')
|
|
243
|
+
return undefined;
|
|
244
|
+
const bindings = [];
|
|
245
|
+
// Collect all clauses — from direct children AND from namespace_use_group
|
|
246
|
+
const clauses = [];
|
|
247
|
+
for (let i = 0; i < importNode.namedChildCount; i++) {
|
|
248
|
+
const child = importNode.namedChild(i);
|
|
249
|
+
if (child?.type === 'namespace_use_clause') {
|
|
250
|
+
clauses.push(child);
|
|
251
|
+
}
|
|
252
|
+
else if (child?.type === 'namespace_use_group') {
|
|
253
|
+
for (let j = 0; j < child.namedChildCount; j++) {
|
|
254
|
+
const groupChild = child.namedChild(j);
|
|
255
|
+
if (groupChild?.type === 'namespace_use_clause')
|
|
256
|
+
clauses.push(groupChild);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
for (const clause of clauses) {
|
|
261
|
+
// Flat imports: qualified_name + name (alias)
|
|
262
|
+
let qualifiedName = null;
|
|
263
|
+
const names = [];
|
|
264
|
+
for (let j = 0; j < clause.namedChildCount; j++) {
|
|
265
|
+
const child = clause.namedChild(j);
|
|
266
|
+
if (child?.type === 'qualified_name')
|
|
267
|
+
qualifiedName = child;
|
|
268
|
+
else if (child?.type === 'name')
|
|
269
|
+
names.push(child);
|
|
270
|
+
}
|
|
271
|
+
if (qualifiedName && names.length > 0) {
|
|
272
|
+
// Flat aliased import: use App\Models\Repo as R;
|
|
273
|
+
const fullText = qualifiedName.text;
|
|
274
|
+
const exportedName = fullText.includes('\\') ? fullText.split('\\').pop() : fullText;
|
|
275
|
+
bindings.push({ local: names[0].text, exported: exportedName });
|
|
276
|
+
}
|
|
277
|
+
else if (qualifiedName && names.length === 0) {
|
|
278
|
+
// Flat non-aliased import: use App\Models\User;
|
|
279
|
+
const fullText = qualifiedName.text;
|
|
280
|
+
const lastSegment = fullText.includes('\\') ? fullText.split('\\').pop() : fullText;
|
|
281
|
+
bindings.push({ local: lastSegment, exported: lastSegment });
|
|
282
|
+
}
|
|
283
|
+
else if (!qualifiedName && names.length >= 2) {
|
|
284
|
+
// Grouped aliased import: {Repo as R} — first name = exported, second = alias
|
|
285
|
+
bindings.push({ local: names[1].text, exported: names[0].text });
|
|
286
|
+
}
|
|
287
|
+
else if (!qualifiedName && names.length === 1) {
|
|
288
|
+
// Grouped non-aliased import: {User} in use App\Models\{User, Repo as R}
|
|
289
|
+
bindings.push({ local: names[0].text, exported: names[0].text });
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return bindings.length > 0 ? bindings : undefined;
|
|
293
|
+
}
|
|
294
|
+
// C#: using_directive with identifier (alias) + qualified_name (target)
|
|
295
|
+
export function extractCsharpNamedBindings(importNode) {
|
|
296
|
+
if (importNode.type !== 'using_directive')
|
|
297
|
+
return undefined;
|
|
298
|
+
let aliasIdent = null;
|
|
299
|
+
let qualifiedName = null;
|
|
300
|
+
for (let i = 0; i < importNode.namedChildCount; i++) {
|
|
301
|
+
const child = importNode.namedChild(i);
|
|
302
|
+
if (child?.type === 'identifier' && !aliasIdent)
|
|
303
|
+
aliasIdent = child;
|
|
304
|
+
else if (child?.type === 'qualified_name')
|
|
305
|
+
qualifiedName = child;
|
|
306
|
+
}
|
|
307
|
+
if (!aliasIdent || !qualifiedName)
|
|
308
|
+
return undefined;
|
|
309
|
+
const fullText = qualifiedName.text;
|
|
310
|
+
const exportedName = fullText.includes('.') ? fullText.split('.').pop() : fullText;
|
|
311
|
+
return [{ local: aliasIdent.text, exported: exportedName }];
|
|
312
|
+
}
|
|
313
|
+
// Java: import_declaration > scoped_identifier (wildcards produce no bindings)
|
|
314
|
+
export function extractJavaNamedBindings(importNode) {
|
|
315
|
+
if (importNode.type !== 'import_declaration')
|
|
316
|
+
return undefined;
|
|
317
|
+
// Skip wildcard imports
|
|
318
|
+
for (let i = 0; i < importNode.childCount; i++) {
|
|
319
|
+
const child = importNode.child(i);
|
|
320
|
+
if (child?.type === 'asterisk')
|
|
321
|
+
return undefined;
|
|
322
|
+
}
|
|
323
|
+
const scopedId = findChild(importNode, 'scoped_identifier');
|
|
324
|
+
if (!scopedId)
|
|
325
|
+
return undefined;
|
|
326
|
+
const fullText = scopedId.text;
|
|
327
|
+
const lastDot = fullText.lastIndexOf('.');
|
|
328
|
+
if (lastDot === -1)
|
|
329
|
+
return undefined;
|
|
330
|
+
const className = fullText.slice(lastDot + 1);
|
|
331
|
+
// Skip lowercase names (package imports, not class imports)
|
|
332
|
+
if (className[0] && className[0] === className[0].toLowerCase())
|
|
333
|
+
return undefined;
|
|
334
|
+
return [{ local: className, exported: className }];
|
|
335
|
+
}
|
|
336
|
+
function findChild(node, type) {
|
|
337
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
338
|
+
const child = node.namedChild(i);
|
|
339
|
+
if (child?.type === type)
|
|
340
|
+
return child;
|
|
341
|
+
}
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** @file parsing-processor.ts @description Parses source files via tree-sitter, extracts code structure nodes (classes, functions, methods, etc), populates the symbol table, and emits DEFINES/HAS_METHOD graph relationships. Supports parallel worker pools with sequential fallback */
|
|
2
|
+
import { KnowledgeGraph } from '../graph/types.js';
|
|
3
|
+
import { SymbolTable } from './symbol-table.js';
|
|
4
|
+
import { ASTCache } from './ast-cache.js';
|
|
5
|
+
import { WorkerPool } from './workers/worker-pool.js';
|
|
6
|
+
import type { ExtractedImport, ExtractedCall, ExtractedHeritage, ExtractedRoute, FileConstructorBindings } from './workers/parse-worker.js';
|
|
7
|
+
export type FileProgressCallback = (current: number, total: number, filePath: string) => void;
|
|
8
|
+
export interface WorkerExtractedData {
|
|
9
|
+
imports: ExtractedImport[];
|
|
10
|
+
calls: ExtractedCall[];
|
|
11
|
+
heritage: ExtractedHeritage[];
|
|
12
|
+
routes: ExtractedRoute[];
|
|
13
|
+
constructorBindings: FileConstructorBindings[];
|
|
14
|
+
}
|
|
15
|
+
export { isNodeExported } from './export-detection.js';
|
|
16
|
+
/** Parse files using worker pool with sequential fallback */
|
|
17
|
+
export declare const processParsing: (graph: KnowledgeGraph, files: {
|
|
18
|
+
path: string;
|
|
19
|
+
content: string;
|
|
20
|
+
}[], symbolTable: SymbolTable, astCache: ASTCache, onFileProgress?: FileProgressCallback, workerPool?: WorkerPool) => Promise<WorkerExtractedData | null>;
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
// code-mapper/src/core/ingestion/parsing-processor.ts
|
|
2
|
+
/** @file parsing-processor.ts @description Parses source files via tree-sitter, extracts code structure nodes (classes, functions, methods, etc), populates the symbol table, and emits DEFINES/HAS_METHOD graph relationships. Supports parallel worker pools with sequential fallback */
|
|
3
|
+
import Parser from 'tree-sitter';
|
|
4
|
+
import { loadParser, loadLanguage, isLanguageAvailable } from '../tree-sitter/parser-loader.js';
|
|
5
|
+
import { LANGUAGE_QUERIES } from './tree-sitter-queries.js';
|
|
6
|
+
import { generateId } from '../../lib/utils.js';
|
|
7
|
+
import { getLanguageFromFilename, yieldToEventLoop, getDefinitionNodeFromCaptures, findEnclosingClassId, extractMethodSignature } from './utils.js';
|
|
8
|
+
import { isNodeExported } from './export-detection.js';
|
|
9
|
+
import { detectFrameworkFromAST } from './framework-detection.js';
|
|
10
|
+
import { typeConfigs } from './type-extractors/index.js';
|
|
11
|
+
import { getTreeSitterBufferSize, TREE_SITTER_MAX_BUFFER } from './constants.js';
|
|
12
|
+
export { isNodeExported } from './export-detection.js';
|
|
13
|
+
// Worker-based parallel parsing
|
|
14
|
+
const processParsingWithWorkers = async (graph, files, symbolTable, astCache, workerPool, onFileProgress) => {
|
|
15
|
+
// Filter to parseable files only
|
|
16
|
+
const parseableFiles = [];
|
|
17
|
+
for (const file of files) {
|
|
18
|
+
const lang = getLanguageFromFilename(file.path);
|
|
19
|
+
if (lang)
|
|
20
|
+
parseableFiles.push({ path: file.path, content: file.content });
|
|
21
|
+
}
|
|
22
|
+
if (parseableFiles.length === 0)
|
|
23
|
+
return { imports: [], calls: [], heritage: [], routes: [], constructorBindings: [] };
|
|
24
|
+
const total = files.length;
|
|
25
|
+
// Dispatch to worker pool
|
|
26
|
+
const chunkResults = await workerPool.dispatch(parseableFiles, (filesProcessed) => {
|
|
27
|
+
onFileProgress?.(Math.min(filesProcessed, total), total, 'Parsing...');
|
|
28
|
+
});
|
|
29
|
+
// Merge worker results into graph and symbol table
|
|
30
|
+
const allImports = [];
|
|
31
|
+
const allCalls = [];
|
|
32
|
+
const allHeritage = [];
|
|
33
|
+
const allRoutes = [];
|
|
34
|
+
const allConstructorBindings = [];
|
|
35
|
+
for (const result of chunkResults) {
|
|
36
|
+
for (const node of result.nodes) {
|
|
37
|
+
graph.addNode({
|
|
38
|
+
id: node.id,
|
|
39
|
+
label: node.label,
|
|
40
|
+
properties: node.properties,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
for (const rel of result.relationships) {
|
|
44
|
+
graph.addRelationship(rel);
|
|
45
|
+
}
|
|
46
|
+
for (const sym of result.symbols) {
|
|
47
|
+
symbolTable.add(sym.filePath, sym.name, sym.nodeId, sym.type, {
|
|
48
|
+
parameterCount: sym.parameterCount,
|
|
49
|
+
returnType: sym.returnType,
|
|
50
|
+
ownerId: sym.ownerId,
|
|
51
|
+
parameterTypes: sym.parameterTypes,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
allImports.push(...result.imports);
|
|
55
|
+
allCalls.push(...result.calls);
|
|
56
|
+
allHeritage.push(...result.heritage);
|
|
57
|
+
allRoutes.push(...result.routes);
|
|
58
|
+
allConstructorBindings.push(...result.constructorBindings);
|
|
59
|
+
}
|
|
60
|
+
// Merge and log skipped languages
|
|
61
|
+
const skippedLanguages = new Map();
|
|
62
|
+
for (const result of chunkResults) {
|
|
63
|
+
for (const [lang, count] of Object.entries(result.skippedLanguages)) {
|
|
64
|
+
skippedLanguages.set(lang, (skippedLanguages.get(lang) || 0) + count);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (skippedLanguages.size > 0) {
|
|
68
|
+
const summary = Array.from(skippedLanguages.entries())
|
|
69
|
+
.map(([lang, count]) => `${lang}: ${count}`)
|
|
70
|
+
.join(', ');
|
|
71
|
+
console.warn(` Skipped unsupported languages: ${summary}`);
|
|
72
|
+
}
|
|
73
|
+
// Final progress report
|
|
74
|
+
onFileProgress?.(total, total, 'done');
|
|
75
|
+
return { imports: allImports, calls: allCalls, heritage: allHeritage, routes: allRoutes, constructorBindings: allConstructorBindings };
|
|
76
|
+
};
|
|
77
|
+
// Sequential fallback (single-threaded)
|
|
78
|
+
const processParsingSequential = async (graph, files, symbolTable, astCache, onFileProgress) => {
|
|
79
|
+
const parser = await loadParser();
|
|
80
|
+
const total = files.length;
|
|
81
|
+
const skippedLanguages = new Map();
|
|
82
|
+
for (let i = 0; i < files.length; i++) {
|
|
83
|
+
const file = files[i];
|
|
84
|
+
onFileProgress?.(i + 1, total, file.path);
|
|
85
|
+
if (i % 20 === 0)
|
|
86
|
+
await yieldToEventLoop();
|
|
87
|
+
const language = getLanguageFromFilename(file.path);
|
|
88
|
+
if (!language)
|
|
89
|
+
continue;
|
|
90
|
+
// Skip unsupported languages
|
|
91
|
+
if (!isLanguageAvailable(language)) {
|
|
92
|
+
skippedLanguages.set(language, (skippedLanguages.get(language) || 0) + 1);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
// Skip files larger than the max tree-sitter buffer
|
|
96
|
+
if (file.content.length > TREE_SITTER_MAX_BUFFER)
|
|
97
|
+
continue;
|
|
98
|
+
try {
|
|
99
|
+
await loadLanguage(language, file.path);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
let tree;
|
|
105
|
+
try {
|
|
106
|
+
tree = parser.parse(file.content, undefined, { bufferSize: getTreeSitterBufferSize(file.content.length) });
|
|
107
|
+
}
|
|
108
|
+
catch (parseError) {
|
|
109
|
+
console.warn(`Skipping unparseable file: ${file.path}`);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
astCache.set(file.path, tree);
|
|
113
|
+
const queryString = LANGUAGE_QUERIES[language];
|
|
114
|
+
if (!queryString) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
let query;
|
|
118
|
+
let matches;
|
|
119
|
+
try {
|
|
120
|
+
const language = parser.getLanguage();
|
|
121
|
+
query = new Parser.Query(language, queryString);
|
|
122
|
+
matches = query.matches(tree.rootNode);
|
|
123
|
+
}
|
|
124
|
+
catch (queryError) {
|
|
125
|
+
console.warn(`Query error for ${file.path}:`, queryError);
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
matches.forEach(match => {
|
|
129
|
+
const captureMap = {};
|
|
130
|
+
match.captures.forEach(c => {
|
|
131
|
+
captureMap[c.name] = c.node;
|
|
132
|
+
});
|
|
133
|
+
if (captureMap['import']) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (captureMap['call']) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const nameNode = captureMap['name'];
|
|
140
|
+
// Synthesize name for constructors without explicit @name capture (e.g. Swift init)
|
|
141
|
+
if (!nameNode && !captureMap['definition.constructor'])
|
|
142
|
+
return;
|
|
143
|
+
const nodeName = nameNode ? nameNode.text : 'init';
|
|
144
|
+
let nodeLabel = 'CodeElement';
|
|
145
|
+
if (captureMap['definition.function'])
|
|
146
|
+
nodeLabel = 'Function';
|
|
147
|
+
else if (captureMap['definition.class'])
|
|
148
|
+
nodeLabel = 'Class';
|
|
149
|
+
else if (captureMap['definition.interface'])
|
|
150
|
+
nodeLabel = 'Interface';
|
|
151
|
+
else if (captureMap['definition.method'])
|
|
152
|
+
nodeLabel = 'Method';
|
|
153
|
+
else if (captureMap['definition.struct'])
|
|
154
|
+
nodeLabel = 'Struct';
|
|
155
|
+
else if (captureMap['definition.enum'])
|
|
156
|
+
nodeLabel = 'Enum';
|
|
157
|
+
else if (captureMap['definition.namespace'])
|
|
158
|
+
nodeLabel = 'Namespace';
|
|
159
|
+
else if (captureMap['definition.module'])
|
|
160
|
+
nodeLabel = 'Module';
|
|
161
|
+
else if (captureMap['definition.trait'])
|
|
162
|
+
nodeLabel = 'Trait';
|
|
163
|
+
else if (captureMap['definition.impl'])
|
|
164
|
+
nodeLabel = 'Impl';
|
|
165
|
+
else if (captureMap['definition.type'])
|
|
166
|
+
nodeLabel = 'TypeAlias';
|
|
167
|
+
else if (captureMap['definition.const'])
|
|
168
|
+
nodeLabel = 'Const';
|
|
169
|
+
else if (captureMap['definition.static'])
|
|
170
|
+
nodeLabel = 'Static';
|
|
171
|
+
else if (captureMap['definition.typedef'])
|
|
172
|
+
nodeLabel = 'Typedef';
|
|
173
|
+
else if (captureMap['definition.macro'])
|
|
174
|
+
nodeLabel = 'Macro';
|
|
175
|
+
else if (captureMap['definition.union'])
|
|
176
|
+
nodeLabel = 'Union';
|
|
177
|
+
else if (captureMap['definition.property'])
|
|
178
|
+
nodeLabel = 'Property';
|
|
179
|
+
else if (captureMap['definition.record'])
|
|
180
|
+
nodeLabel = 'Record';
|
|
181
|
+
else if (captureMap['definition.delegate'])
|
|
182
|
+
nodeLabel = 'Delegate';
|
|
183
|
+
else if (captureMap['definition.annotation'])
|
|
184
|
+
nodeLabel = 'Annotation';
|
|
185
|
+
else if (captureMap['definition.constructor'])
|
|
186
|
+
nodeLabel = 'Constructor';
|
|
187
|
+
else if (captureMap['definition.template'])
|
|
188
|
+
nodeLabel = 'Template';
|
|
189
|
+
const definitionNodeForRange = getDefinitionNodeFromCaptures(captureMap);
|
|
190
|
+
const startLine = definitionNodeForRange ? definitionNodeForRange.startPosition.row : (nameNode ? nameNode.startPosition.row : 0);
|
|
191
|
+
const nodeId = generateId(nodeLabel, `${file.path}:${nodeName}`);
|
|
192
|
+
const definitionNode = getDefinitionNodeFromCaptures(captureMap);
|
|
193
|
+
const frameworkHint = definitionNode
|
|
194
|
+
? detectFrameworkFromAST(language, (definitionNode.text || '').slice(0, 300))
|
|
195
|
+
: null;
|
|
196
|
+
// Extract method signature for Method/Constructor nodes
|
|
197
|
+
const methodSig = (nodeLabel === 'Function' || nodeLabel === 'Method' || nodeLabel === 'Constructor')
|
|
198
|
+
? extractMethodSignature(definitionNode)
|
|
199
|
+
: undefined;
|
|
200
|
+
// Language-specific return type fallback (e.g. Ruby YARD @return)
|
|
201
|
+
if (methodSig && !methodSig.returnType && definitionNode) {
|
|
202
|
+
const tc = typeConfigs[language];
|
|
203
|
+
if (tc?.extractReturnType) {
|
|
204
|
+
methodSig.returnType = tc.extractReturnType(definitionNode);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
const node = {
|
|
208
|
+
id: nodeId,
|
|
209
|
+
label: nodeLabel,
|
|
210
|
+
properties: {
|
|
211
|
+
name: nodeName,
|
|
212
|
+
filePath: file.path,
|
|
213
|
+
startLine: definitionNodeForRange ? definitionNodeForRange.startPosition.row : startLine,
|
|
214
|
+
endLine: definitionNodeForRange ? definitionNodeForRange.endPosition.row : startLine,
|
|
215
|
+
language: language,
|
|
216
|
+
isExported: isNodeExported(nameNode || definitionNodeForRange, nodeName, language),
|
|
217
|
+
...(frameworkHint ? {
|
|
218
|
+
astFrameworkMultiplier: frameworkHint.entryPointMultiplier,
|
|
219
|
+
astFrameworkReason: frameworkHint.reason,
|
|
220
|
+
} : {}),
|
|
221
|
+
...(methodSig ? {
|
|
222
|
+
parameterCount: methodSig.parameterCount,
|
|
223
|
+
returnType: methodSig.returnType,
|
|
224
|
+
} : {}),
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
graph.addNode(node);
|
|
228
|
+
// Compute enclosing class for Method/Constructor/Property/Function
|
|
229
|
+
// Function included because Kotlin/Rust/Python capture class methods as Function nodes
|
|
230
|
+
const needsOwner = nodeLabel === 'Method' || nodeLabel === 'Constructor' || nodeLabel === 'Property' || nodeLabel === 'Function';
|
|
231
|
+
const enclosingClassId = needsOwner ? findEnclosingClassId(nameNode || definitionNodeForRange, file.path) : null;
|
|
232
|
+
symbolTable.add(file.path, nodeName, nodeId, nodeLabel, {
|
|
233
|
+
parameterCount: methodSig?.parameterCount,
|
|
234
|
+
returnType: methodSig?.returnType,
|
|
235
|
+
parameterTypes: methodSig?.parameterTypes,
|
|
236
|
+
ownerId: enclosingClassId ?? undefined,
|
|
237
|
+
});
|
|
238
|
+
const fileId = generateId('File', file.path);
|
|
239
|
+
const relId = generateId('DEFINES', `${fileId}->${nodeId}`);
|
|
240
|
+
const relationship = {
|
|
241
|
+
id: relId,
|
|
242
|
+
sourceId: fileId,
|
|
243
|
+
targetId: nodeId,
|
|
244
|
+
type: 'DEFINES',
|
|
245
|
+
confidence: 1.0,
|
|
246
|
+
reason: '',
|
|
247
|
+
};
|
|
248
|
+
graph.addRelationship(relationship);
|
|
249
|
+
// HAS_METHOD: link method/constructor/property to enclosing class
|
|
250
|
+
if (enclosingClassId) {
|
|
251
|
+
graph.addRelationship({
|
|
252
|
+
id: generateId('HAS_METHOD', `${enclosingClassId}->${nodeId}`),
|
|
253
|
+
sourceId: enclosingClassId,
|
|
254
|
+
targetId: nodeId,
|
|
255
|
+
type: 'HAS_METHOD',
|
|
256
|
+
confidence: 1.0,
|
|
257
|
+
reason: '',
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
if (skippedLanguages.size > 0) {
|
|
263
|
+
const summary = Array.from(skippedLanguages.entries())
|
|
264
|
+
.map(([lang, count]) => `${lang}: ${count}`)
|
|
265
|
+
.join(', ');
|
|
266
|
+
console.warn(` Skipped unsupported languages: ${summary}`);
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
/** Parse files using worker pool with sequential fallback */
|
|
270
|
+
export const processParsing = async (graph, files, symbolTable, astCache, onFileProgress, workerPool) => {
|
|
271
|
+
if (workerPool) {
|
|
272
|
+
try {
|
|
273
|
+
return await processParsingWithWorkers(graph, files, symbolTable, astCache, workerPool, onFileProgress);
|
|
274
|
+
}
|
|
275
|
+
catch (err) {
|
|
276
|
+
console.warn('Worker pool parsing failed, falling back to sequential:', err instanceof Error ? err.message : err);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// Fallback: sequential parsing
|
|
280
|
+
await processParsingSequential(graph, files, symbolTable, astCache, onFileProgress);
|
|
281
|
+
return null;
|
|
282
|
+
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/** @file pipeline.ts @description Main ingestion pipeline that orchestrates scanning, parsing, resolution, community detection, and process detection across chunked file batches */
|
|
2
|
+
import { PipelineProgress, PipelineResult } from '../../types/pipeline.js';
|
|
3
|
+
export declare const runPipelineFromRepo: (repoPath: string, onProgress: (progress: PipelineProgress) => void) => Promise<PipelineResult>;
|