gitnexus 1.4.7 → 1.4.8
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 +22 -1
- package/dist/cli/ai-context.d.ts +1 -1
- package/dist/cli/ai-context.js +1 -1
- package/dist/cli/analyze.d.ts +2 -0
- package/dist/cli/analyze.js +54 -21
- package/dist/cli/index.js +2 -1
- package/dist/cli/setup.js +78 -1
- package/dist/config/supported-languages.d.ts +30 -0
- package/dist/config/supported-languages.js +30 -0
- package/dist/core/embeddings/embedder.d.ts +6 -1
- package/dist/core/embeddings/embedder.js +65 -5
- package/dist/core/embeddings/embedding-pipeline.js +11 -9
- package/dist/core/embeddings/http-client.d.ts +31 -0
- package/dist/core/embeddings/http-client.js +179 -0
- package/dist/core/embeddings/index.d.ts +1 -0
- package/dist/core/embeddings/index.js +1 -0
- package/dist/core/embeddings/types.d.ts +1 -1
- package/dist/core/graph/types.d.ts +2 -1
- package/dist/core/ingestion/ast-helpers.d.ts +80 -0
- package/dist/core/ingestion/ast-helpers.js +738 -0
- package/dist/core/ingestion/call-analysis.d.ts +73 -0
- package/dist/core/ingestion/call-analysis.js +490 -0
- package/dist/core/ingestion/call-processor.d.ts +48 -1
- package/dist/core/ingestion/call-processor.js +368 -7
- package/dist/core/ingestion/call-routing.d.ts +6 -0
- package/dist/core/ingestion/entry-point-scoring.js +36 -26
- package/dist/core/ingestion/framework-detection.d.ts +10 -2
- package/dist/core/ingestion/framework-detection.js +49 -12
- package/dist/core/ingestion/heritage-processor.js +47 -49
- package/dist/core/ingestion/import-processor.d.ts +1 -1
- package/dist/core/ingestion/import-processor.js +103 -194
- package/dist/core/ingestion/import-resolution.d.ts +101 -0
- package/dist/core/ingestion/import-resolution.js +251 -0
- package/dist/core/ingestion/language-config.d.ts +3 -0
- package/dist/core/ingestion/language-config.js +13 -0
- package/dist/core/ingestion/markdown-processor.d.ts +17 -0
- package/dist/core/ingestion/markdown-processor.js +124 -0
- package/dist/core/ingestion/mro-processor.js +8 -3
- package/dist/core/ingestion/named-binding-extraction.d.ts +9 -43
- package/dist/core/ingestion/named-binding-extraction.js +89 -79
- package/dist/core/ingestion/parsing-processor.d.ts +2 -2
- package/dist/core/ingestion/parsing-processor.js +14 -73
- package/dist/core/ingestion/pipeline.d.ts +10 -0
- package/dist/core/ingestion/pipeline.js +421 -4
- package/dist/core/ingestion/resolution-context.d.ts +5 -0
- package/dist/core/ingestion/resolution-context.js +7 -4
- package/dist/core/ingestion/resolvers/index.d.ts +1 -1
- package/dist/core/ingestion/resolvers/index.js +1 -1
- package/dist/core/ingestion/resolvers/jvm.d.ts +2 -1
- package/dist/core/ingestion/resolvers/jvm.js +25 -9
- package/dist/core/ingestion/resolvers/php.d.ts +14 -0
- package/dist/core/ingestion/resolvers/php.js +43 -3
- package/dist/core/ingestion/resolvers/utils.d.ts +5 -0
- package/dist/core/ingestion/resolvers/utils.js +16 -0
- package/dist/core/ingestion/symbol-table.d.ts +16 -0
- package/dist/core/ingestion/symbol-table.js +20 -6
- package/dist/core/ingestion/tree-sitter-queries.d.ts +4 -4
- package/dist/core/ingestion/tree-sitter-queries.js +43 -2
- package/dist/core/ingestion/type-env.d.ts +28 -1
- package/dist/core/ingestion/type-env.js +419 -96
- package/dist/core/ingestion/type-extractors/c-cpp.d.ts +5 -0
- package/dist/core/ingestion/type-extractors/c-cpp.js +119 -0
- package/dist/core/ingestion/type-extractors/csharp.js +149 -16
- package/dist/core/ingestion/type-extractors/index.d.ts +1 -1
- package/dist/core/ingestion/type-extractors/index.js +1 -1
- package/dist/core/ingestion/type-extractors/jvm.js +169 -66
- package/dist/core/ingestion/type-extractors/rust.js +35 -1
- package/dist/core/ingestion/type-extractors/shared.d.ts +0 -2
- package/dist/core/ingestion/type-extractors/shared.js +5 -10
- package/dist/core/ingestion/type-extractors/swift.js +7 -6
- package/dist/core/ingestion/type-extractors/types.d.ts +37 -7
- package/dist/core/ingestion/type-extractors/typescript.js +141 -9
- package/dist/core/ingestion/utils.d.ts +2 -120
- package/dist/core/ingestion/utils.js +3 -1051
- package/dist/core/ingestion/workers/parse-worker.d.ts +13 -4
- package/dist/core/ingestion/workers/parse-worker.js +66 -87
- package/dist/core/lbug/csv-generator.js +18 -1
- package/dist/core/lbug/lbug-adapter.d.ts +10 -0
- package/dist/core/lbug/lbug-adapter.js +69 -4
- package/dist/core/lbug/schema.d.ts +5 -3
- package/dist/core/lbug/schema.js +26 -2
- package/dist/mcp/core/embedder.js +11 -3
- package/dist/mcp/core/lbug-adapter.js +12 -1
- package/dist/mcp/local/local-backend.d.ts +22 -0
- package/dist/mcp/local/local-backend.js +133 -29
- package/dist/mcp/resources.js +2 -0
- package/dist/mcp/tools.js +2 -2
- package/dist/server/api.d.ts +19 -1
- package/dist/server/api.js +66 -6
- package/dist/storage/git.d.ts +12 -0
- package/dist/storage/git.js +21 -0
- package/package.json +10 -2
|
@@ -3,12 +3,11 @@ import { isLanguageAvailable, loadParser, loadLanguage } from '../tree-sitter/pa
|
|
|
3
3
|
import { LANGUAGE_QUERIES } from './tree-sitter-queries.js';
|
|
4
4
|
import { generateId } from '../../lib/utils.js';
|
|
5
5
|
import { getLanguageFromFilename, isVerboseIngestionEnabled, yieldToEventLoop } from './utils.js';
|
|
6
|
-
import { SupportedLanguages } from '../../config/supported-languages.js';
|
|
7
|
-
import { extractNamedBindings } from './named-binding-extraction.js';
|
|
8
6
|
import { getTreeSitterBufferSize } from './constants.js';
|
|
9
|
-
import {
|
|
10
|
-
import { buildSuffixIndex
|
|
7
|
+
import { loadImportConfigs } from './language-config.js';
|
|
8
|
+
import { buildSuffixIndex } from './resolvers/index.js';
|
|
11
9
|
import { callRouters } from './call-routing.js';
|
|
10
|
+
import { importResolvers, namedBindingExtractors, preprocessImportPath } from './import-resolution.js';
|
|
12
11
|
const isDev = process.env.NODE_ENV === 'development';
|
|
13
12
|
/**
|
|
14
13
|
* Check if a file path is directly inside a package directory identified by its suffix.
|
|
@@ -26,121 +25,36 @@ export function buildImportResolutionContext(allPaths) {
|
|
|
26
25
|
const allFileList = allPaths;
|
|
27
26
|
const normalizedFileList = allFileList.map(p => p.replace(/\\/g, '/'));
|
|
28
27
|
const allFilePaths = new Set(allFileList);
|
|
29
|
-
const
|
|
30
|
-
return { allFilePaths, allFileList, normalizedFileList,
|
|
28
|
+
const index = buildSuffixIndex(normalizedFileList, allFileList);
|
|
29
|
+
return { allFilePaths, allFileList, normalizedFileList, index, resolveCache: new Map() };
|
|
31
30
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
// Fall through to standard resolution
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
let memberResolved = resolveJvmMemberImport(rawImportPath, normalizedFileList, allFileList, exts, index);
|
|
55
|
-
if (!memberResolved && language === SupportedLanguages.Kotlin) {
|
|
56
|
-
memberResolved = resolveJvmMemberImport(rawImportPath, normalizedFileList, allFileList, ['.java'], index);
|
|
57
|
-
}
|
|
58
|
-
if (memberResolved)
|
|
59
|
-
return { kind: 'files', files: [memberResolved] };
|
|
60
|
-
// Fall through to standard resolution
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
// Go: handle package-level imports
|
|
64
|
-
if (language === SupportedLanguages.Go && goModule && rawImportPath.startsWith(goModule.modulePath)) {
|
|
65
|
-
const pkgSuffix = resolveGoPackageDir(rawImportPath, goModule);
|
|
66
|
-
if (pkgSuffix) {
|
|
67
|
-
const pkgFiles = resolveGoPackage(rawImportPath, goModule, normalizedFileList, allFileList);
|
|
68
|
-
if (pkgFiles.length > 0) {
|
|
69
|
-
return { kind: 'package', files: pkgFiles, dirSuffix: pkgSuffix };
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
// Fall through if no files found (package might be external)
|
|
73
|
-
}
|
|
74
|
-
// C#: handle namespace-based imports (using directives)
|
|
75
|
-
if (language === SupportedLanguages.CSharp && csharpConfigs.length > 0) {
|
|
76
|
-
const resolvedFiles = resolveCSharpImport(rawImportPath, csharpConfigs, normalizedFileList, allFileList, index);
|
|
77
|
-
if (resolvedFiles.length > 1) {
|
|
78
|
-
const dirSuffix = resolveCSharpNamespaceDir(rawImportPath, csharpConfigs);
|
|
79
|
-
if (dirSuffix) {
|
|
80
|
-
return { kind: 'package', files: resolvedFiles, dirSuffix };
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
if (resolvedFiles.length > 0)
|
|
84
|
-
return { kind: 'files', files: resolvedFiles };
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
// PHP: handle namespace-based imports (use statements)
|
|
88
|
-
if (language === SupportedLanguages.PHP) {
|
|
89
|
-
const resolved = resolvePhpImport(rawImportPath, composerConfig, allFilePaths, normalizedFileList, allFileList, index);
|
|
90
|
-
return resolved ? { kind: 'files', files: [resolved] } : null;
|
|
91
|
-
}
|
|
92
|
-
// Swift: handle module imports
|
|
93
|
-
if (language === SupportedLanguages.Swift && swiftPackageConfig) {
|
|
94
|
-
const targetDir = swiftPackageConfig.targets.get(rawImportPath);
|
|
95
|
-
if (targetDir) {
|
|
96
|
-
const dirPrefix = targetDir + '/';
|
|
97
|
-
const files = [];
|
|
98
|
-
for (let i = 0; i < normalizedFileList.length; i++) {
|
|
99
|
-
if (normalizedFileList[i].startsWith(dirPrefix) && normalizedFileList[i].endsWith('.swift')) {
|
|
100
|
-
files.push(allFileList[i]);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
if (files.length > 0)
|
|
104
|
-
return { kind: 'files', files };
|
|
105
|
-
}
|
|
106
|
-
return null; // External framework (Foundation, UIKit, etc.)
|
|
107
|
-
}
|
|
108
|
-
// Python: relative imports (PEP 328) + proximity-based bare imports
|
|
109
|
-
// Falls through to standard suffix resolution when proximity finds no match.
|
|
110
|
-
if (language === SupportedLanguages.Python) {
|
|
111
|
-
const resolved = resolvePythonImport(filePath, rawImportPath, allFilePaths);
|
|
112
|
-
if (resolved)
|
|
113
|
-
return { kind: 'files', files: [resolved] };
|
|
114
|
-
if (rawImportPath.startsWith('.'))
|
|
115
|
-
return null; // relative but unresolved — don't suffix-match
|
|
116
|
-
}
|
|
117
|
-
// Ruby: require / require_relative
|
|
118
|
-
if (language === SupportedLanguages.Ruby) {
|
|
119
|
-
const resolved = resolveRubyImport(rawImportPath, normalizedFileList, allFileList, index);
|
|
120
|
-
return resolved ? { kind: 'files', files: [resolved] } : null;
|
|
121
|
-
}
|
|
122
|
-
// Rust: expand top-level grouped imports: use {crate::a, crate::b}
|
|
123
|
-
if (language === SupportedLanguages.Rust && rawImportPath.startsWith('{') && rawImportPath.endsWith('}')) {
|
|
124
|
-
const inner = rawImportPath.slice(1, -1);
|
|
125
|
-
const parts = inner.split(',').map(p => p.trim()).filter(Boolean);
|
|
126
|
-
const resolved = [];
|
|
127
|
-
for (const part of parts) {
|
|
128
|
-
const r = resolveRustImport(filePath, part, allFilePaths);
|
|
129
|
-
if (r)
|
|
130
|
-
resolved.push(r);
|
|
131
|
-
}
|
|
132
|
-
return resolved.length > 0 ? { kind: 'files', files: resolved } : null;
|
|
133
|
-
}
|
|
134
|
-
// Standard single-file resolution
|
|
135
|
-
const resolvedPath = resolveImportPath(filePath, rawImportPath, allFilePaths, allFileList, normalizedFileList, resolveCache, language, tsconfigPaths, index);
|
|
136
|
-
return resolvedPath ? { kind: 'files', files: [resolvedPath] } : null;
|
|
31
|
+
// Config loaders extracted to ./language-config.ts (Phase 2 refactor)
|
|
32
|
+
// Resolver dispatch tables are in ./import-resolution.ts — imported above
|
|
33
|
+
/** Create IMPORTS edge helpers that share a resolved-count tracker. */
|
|
34
|
+
function createImportEdgeHelpers(graph, importMap) {
|
|
35
|
+
let totalImportsResolved = 0;
|
|
36
|
+
const addImportGraphEdge = (filePath, resolvedPath) => {
|
|
37
|
+
const sourceId = generateId('File', filePath);
|
|
38
|
+
const targetId = generateId('File', resolvedPath);
|
|
39
|
+
const relId = generateId('IMPORTS', `${filePath}->${resolvedPath}`);
|
|
40
|
+
totalImportsResolved++;
|
|
41
|
+
graph.addRelationship({ id: relId, sourceId, targetId, type: 'IMPORTS', confidence: 1.0, reason: '' });
|
|
42
|
+
};
|
|
43
|
+
const addImportEdge = (filePath, resolvedPath) => {
|
|
44
|
+
addImportGraphEdge(filePath, resolvedPath);
|
|
45
|
+
if (!importMap.has(filePath))
|
|
46
|
+
importMap.set(filePath, new Set());
|
|
47
|
+
importMap.get(filePath).add(resolvedPath);
|
|
48
|
+
};
|
|
49
|
+
return { addImportEdge, addImportGraphEdge, getResolvedCount: () => totalImportsResolved };
|
|
137
50
|
}
|
|
138
51
|
/**
|
|
139
52
|
* Apply an ImportResult: emit graph edges and update ImportMap/PackageMap.
|
|
140
53
|
* If namedBindings are provided and the import resolves to a single file,
|
|
141
54
|
* also populate the NamedImportMap for precise Tier 2a resolution.
|
|
55
|
+
* Bindings tagged with `isModuleAlias` are routed to moduleAliasMap instead.
|
|
142
56
|
*/
|
|
143
|
-
function applyImportResult(result, filePath, importMap, packageMap, addImportEdge, addImportGraphEdge, namedBindings, namedImportMap) {
|
|
57
|
+
function applyImportResult(result, filePath, importMap, packageMap, addImportEdge, addImportGraphEdge, namedBindings, namedImportMap, moduleAliasMap) {
|
|
144
58
|
if (!result)
|
|
145
59
|
return;
|
|
146
60
|
if (result.kind === 'package' && packageMap) {
|
|
@@ -158,14 +72,66 @@ function applyImportResult(result, filePath, importMap, packageMap, addImportEdg
|
|
|
158
72
|
for (const resolvedFile of files) {
|
|
159
73
|
addImportEdge(filePath, resolvedFile);
|
|
160
74
|
}
|
|
161
|
-
//
|
|
162
|
-
|
|
75
|
+
// Route module aliases (import X as Y) directly to moduleAliasMap.
|
|
76
|
+
// These are module-level aliases, not symbol bindings — they don't belong in namedImportMap.
|
|
77
|
+
if (namedBindings && moduleAliasMap && files.length === 1) {
|
|
163
78
|
const resolvedFile = files[0];
|
|
79
|
+
for (const binding of namedBindings) {
|
|
80
|
+
if (!binding.isModuleAlias)
|
|
81
|
+
continue;
|
|
82
|
+
let aliasMap = moduleAliasMap.get(filePath);
|
|
83
|
+
if (!aliasMap) {
|
|
84
|
+
aliasMap = new Map();
|
|
85
|
+
moduleAliasMap.set(filePath, aliasMap);
|
|
86
|
+
}
|
|
87
|
+
aliasMap.set(binding.local, resolvedFile);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Record named bindings for precise Tier 2a resolution.
|
|
91
|
+
// If the same local name is imported from multiple files (e.g., Java static imports
|
|
92
|
+
// of overloaded methods), remove the entry so resolution falls through to Tier 2a
|
|
93
|
+
// import-scoped which sees all candidates and can apply arity narrowing.
|
|
94
|
+
if (namedBindings && namedImportMap) {
|
|
164
95
|
if (!namedImportMap.has(filePath))
|
|
165
96
|
namedImportMap.set(filePath, new Map());
|
|
166
97
|
const fileBindings = namedImportMap.get(filePath);
|
|
167
|
-
|
|
168
|
-
|
|
98
|
+
if (files.length === 1) {
|
|
99
|
+
const resolvedFile = files[0];
|
|
100
|
+
for (const binding of namedBindings) {
|
|
101
|
+
if (binding.isModuleAlias)
|
|
102
|
+
continue; // already routed to moduleAliasMap
|
|
103
|
+
const existing = fileBindings.get(binding.local);
|
|
104
|
+
if (existing && existing.sourcePath !== resolvedFile) {
|
|
105
|
+
fileBindings.delete(binding.local);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
fileBindings.set(binding.local, { sourcePath: resolvedFile, exportedName: binding.exported });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// Multi-file resolution (e.g., Rust `use crate::models::{User, Repo}`).
|
|
114
|
+
// Match each binding to a resolved file by comparing the lowercase binding name
|
|
115
|
+
// to the file's basename (without extension). If no match, skip the binding.
|
|
116
|
+
for (const binding of namedBindings) {
|
|
117
|
+
if (binding.isModuleAlias)
|
|
118
|
+
continue;
|
|
119
|
+
const lowerName = binding.exported.toLowerCase();
|
|
120
|
+
const matchedFile = files.find(f => {
|
|
121
|
+
const base = f.replace(/\\/g, '/').split('/').pop() ?? '';
|
|
122
|
+
const nameWithoutExt = base.substring(0, base.lastIndexOf('.')).toLowerCase();
|
|
123
|
+
return nameWithoutExt === lowerName;
|
|
124
|
+
});
|
|
125
|
+
if (matchedFile) {
|
|
126
|
+
const existing = fileBindings.get(binding.local);
|
|
127
|
+
if (existing && existing.sourcePath !== matchedFile) {
|
|
128
|
+
fileBindings.delete(binding.local);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
fileBindings.set(binding.local, { sourcePath: matchedFile, exportedName: binding.exported });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
169
135
|
}
|
|
170
136
|
}
|
|
171
137
|
}
|
|
@@ -177,6 +143,7 @@ export const processImports = async (graph, files, astCache, ctx, onProgress, re
|
|
|
177
143
|
const importMap = ctx.importMap;
|
|
178
144
|
const packageMap = ctx.packageMap;
|
|
179
145
|
const namedImportMap = ctx.namedImportMap;
|
|
146
|
+
const moduleAliasMap = ctx.moduleAliasMap;
|
|
180
147
|
// Use allPaths (full repo) when available for cross-chunk resolution, else fall back to chunk files
|
|
181
148
|
const allFileList = allPaths ?? files.map(f => f.path);
|
|
182
149
|
const allFilePaths = new Set(allFileList);
|
|
@@ -190,40 +157,10 @@ export const processImports = async (graph, files, astCache, ctx, onProgress, re
|
|
|
190
157
|
const index = buildSuffixIndex(normalizedFileList, allFileList);
|
|
191
158
|
// Track import statistics
|
|
192
159
|
let totalImportsFound = 0;
|
|
193
|
-
let totalImportsResolved = 0;
|
|
194
160
|
// Load language-specific configs once before the file loop
|
|
195
|
-
const
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
goModule: await loadGoModulePath(effectiveRoot),
|
|
199
|
-
composerConfig: await loadComposerConfig(effectiveRoot),
|
|
200
|
-
swiftPackageConfig: await loadSwiftPackageConfig(effectiveRoot),
|
|
201
|
-
csharpConfigs: await loadCSharpProjectConfig(effectiveRoot),
|
|
202
|
-
};
|
|
203
|
-
const resolveCtx = { allFilePaths, allFileList, normalizedFileList, index, resolveCache };
|
|
204
|
-
// Helper: add an IMPORTS edge to the graph only (no ImportMap update)
|
|
205
|
-
const addImportGraphEdge = (filePath, resolvedPath) => {
|
|
206
|
-
const sourceId = generateId('File', filePath);
|
|
207
|
-
const targetId = generateId('File', resolvedPath);
|
|
208
|
-
const relId = generateId('IMPORTS', `${filePath}->${resolvedPath}`);
|
|
209
|
-
totalImportsResolved++;
|
|
210
|
-
graph.addRelationship({
|
|
211
|
-
id: relId,
|
|
212
|
-
sourceId,
|
|
213
|
-
targetId,
|
|
214
|
-
type: 'IMPORTS',
|
|
215
|
-
confidence: 1.0,
|
|
216
|
-
reason: '',
|
|
217
|
-
});
|
|
218
|
-
};
|
|
219
|
-
// Helper: add an IMPORTS edge + update import map
|
|
220
|
-
const addImportEdge = (filePath, resolvedPath) => {
|
|
221
|
-
addImportGraphEdge(filePath, resolvedPath);
|
|
222
|
-
if (!importMap.has(filePath)) {
|
|
223
|
-
importMap.set(filePath, new Set());
|
|
224
|
-
}
|
|
225
|
-
importMap.get(filePath).add(resolvedPath);
|
|
226
|
-
};
|
|
161
|
+
const configs = await loadImportConfigs(repoRoot || '');
|
|
162
|
+
const resolveCtx = { allFilePaths, allFileList, normalizedFileList, index, resolveCache, configs };
|
|
163
|
+
const { addImportEdge, addImportGraphEdge, getResolvedCount } = createImportEdgeHelpers(graph, importMap);
|
|
227
164
|
for (let i = 0; i < files.length; i++) {
|
|
228
165
|
const file = files[i];
|
|
229
166
|
onProgress?.(i + 1, files.length);
|
|
@@ -291,14 +228,14 @@ export const processImports = async (graph, files, astCache, ctx, onProgress, re
|
|
|
291
228
|
}
|
|
292
229
|
return;
|
|
293
230
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
: sourceNode.text.replace(/['"<>]/g, '');
|
|
231
|
+
const rawImportPath = preprocessImportPath(sourceNode.text, captureMap['import'], language);
|
|
232
|
+
if (!rawImportPath)
|
|
233
|
+
return;
|
|
298
234
|
totalImportsFound++;
|
|
299
|
-
const result =
|
|
300
|
-
const
|
|
301
|
-
|
|
235
|
+
const result = importResolvers[language](rawImportPath, file.path, resolveCtx);
|
|
236
|
+
const extractor = namedBindingExtractors[language];
|
|
237
|
+
const bindings = namedImportMap && extractor ? extractor(captureMap['import']) : undefined;
|
|
238
|
+
applyImportResult(result, file.path, importMap, packageMap, addImportEdge, addImportGraphEdge, bindings, namedImportMap, moduleAliasMap);
|
|
302
239
|
}
|
|
303
240
|
// ---- Language-specific call-as-import routing (Ruby require, etc.) ----
|
|
304
241
|
if (captureMap['call']) {
|
|
@@ -308,7 +245,7 @@ export const processImports = async (graph, files, astCache, ctx, onProgress, re
|
|
|
308
245
|
const routed = callRouter(callNameNode.text, captureMap['call']);
|
|
309
246
|
if (routed && routed.kind === 'import') {
|
|
310
247
|
totalImportsFound++;
|
|
311
|
-
const result =
|
|
248
|
+
const result = importResolvers[language](routed.importPath, file.path, resolveCtx);
|
|
312
249
|
applyImportResult(result, file.path, importMap, packageMap, addImportEdge, addImportGraphEdge);
|
|
313
250
|
}
|
|
314
251
|
}
|
|
@@ -322,7 +259,7 @@ export const processImports = async (graph, files, astCache, ctx, onProgress, re
|
|
|
322
259
|
}
|
|
323
260
|
}
|
|
324
261
|
if (isDev) {
|
|
325
|
-
console.log(`📊 Import processing complete: ${
|
|
262
|
+
console.log(`📊 Import processing complete: ${getResolvedCount()}/${totalImportsFound} imports resolved to graph edges`);
|
|
326
263
|
}
|
|
327
264
|
};
|
|
328
265
|
// ============================================================================
|
|
@@ -332,41 +269,13 @@ export const processImportsFromExtracted = async (graph, files, extractedImports
|
|
|
332
269
|
const importMap = ctx.importMap;
|
|
333
270
|
const packageMap = ctx.packageMap;
|
|
334
271
|
const namedImportMap = ctx.namedImportMap;
|
|
272
|
+
const moduleAliasMap = ctx.moduleAliasMap;
|
|
335
273
|
const importCtx = prebuiltCtx ?? buildImportResolutionContext(files.map(f => f.path));
|
|
336
|
-
const { allFilePaths, allFileList, normalizedFileList,
|
|
274
|
+
const { allFilePaths, allFileList, normalizedFileList, index, resolveCache } = importCtx;
|
|
337
275
|
let totalImportsFound = 0;
|
|
338
|
-
|
|
339
|
-
const
|
|
340
|
-
const
|
|
341
|
-
tsconfigPaths: await loadTsconfigPaths(effectiveRoot),
|
|
342
|
-
goModule: await loadGoModulePath(effectiveRoot),
|
|
343
|
-
composerConfig: await loadComposerConfig(effectiveRoot),
|
|
344
|
-
swiftPackageConfig: await loadSwiftPackageConfig(effectiveRoot),
|
|
345
|
-
csharpConfigs: await loadCSharpProjectConfig(effectiveRoot),
|
|
346
|
-
};
|
|
347
|
-
const resolveCtx = { allFilePaths, allFileList, normalizedFileList, index, resolveCache };
|
|
348
|
-
// Helper: add an IMPORTS edge to the graph only (no ImportMap update)
|
|
349
|
-
const addImportGraphEdge = (filePath, resolvedPath) => {
|
|
350
|
-
const sourceId = generateId('File', filePath);
|
|
351
|
-
const targetId = generateId('File', resolvedPath);
|
|
352
|
-
const relId = generateId('IMPORTS', `${filePath}->${resolvedPath}`);
|
|
353
|
-
totalImportsResolved++;
|
|
354
|
-
graph.addRelationship({
|
|
355
|
-
id: relId,
|
|
356
|
-
sourceId,
|
|
357
|
-
targetId,
|
|
358
|
-
type: 'IMPORTS',
|
|
359
|
-
confidence: 1.0,
|
|
360
|
-
reason: '',
|
|
361
|
-
});
|
|
362
|
-
};
|
|
363
|
-
const addImportEdge = (filePath, resolvedPath) => {
|
|
364
|
-
addImportGraphEdge(filePath, resolvedPath);
|
|
365
|
-
if (!importMap.has(filePath)) {
|
|
366
|
-
importMap.set(filePath, new Set());
|
|
367
|
-
}
|
|
368
|
-
importMap.get(filePath).add(resolvedPath);
|
|
369
|
-
};
|
|
276
|
+
const configs = await loadImportConfigs(repoRoot || '');
|
|
277
|
+
const resolveCtx = { allFilePaths, allFileList, normalizedFileList, index, resolveCache, configs };
|
|
278
|
+
const { addImportEdge, addImportGraphEdge, getResolvedCount } = createImportEdgeHelpers(graph, importMap);
|
|
370
279
|
// Group by file for progress reporting (users see file count, not import count)
|
|
371
280
|
const importsByFile = new Map();
|
|
372
281
|
for (const imp of extractedImports) {
|
|
@@ -387,12 +296,12 @@ export const processImportsFromExtracted = async (graph, files, extractedImports
|
|
|
387
296
|
}
|
|
388
297
|
for (const imp of fileImports) {
|
|
389
298
|
totalImportsFound++;
|
|
390
|
-
const result =
|
|
391
|
-
applyImportResult(result, filePath, importMap, packageMap, addImportEdge, addImportGraphEdge, imp.namedBindings, namedImportMap);
|
|
299
|
+
const result = importResolvers[imp.language](imp.rawImportPath, filePath, resolveCtx);
|
|
300
|
+
applyImportResult(result, filePath, importMap, packageMap, addImportEdge, addImportGraphEdge, imp.namedBindings, namedImportMap, moduleAliasMap);
|
|
392
301
|
}
|
|
393
302
|
}
|
|
394
303
|
onProgress?.(totalFiles, totalFiles);
|
|
395
304
|
if (isDev) {
|
|
396
|
-
console.log(`📊 Import processing (fast path): ${
|
|
305
|
+
console.log(`📊 Import processing (fast path): ${getResolvedCount()}/${totalImportsFound} imports resolved to graph edges`);
|
|
397
306
|
}
|
|
398
307
|
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Import Resolution Dispatch
|
|
3
|
+
*
|
|
4
|
+
* Per-language dispatch table for import resolution and named binding extraction.
|
|
5
|
+
* Replaces the 120-line if-chain in resolveLanguageImport() and the 7-branch
|
|
6
|
+
* dispatch in extractNamedBindings() with a single table lookup each.
|
|
7
|
+
*
|
|
8
|
+
* Follows the existing ExportChecker / CallRouter pattern:
|
|
9
|
+
* - Function aliases (not interfaces) to avoid megamorphic inline-cache issues
|
|
10
|
+
* - `satisfies Record<SupportedLanguages, ...>` for compile-time exhaustiveness
|
|
11
|
+
* - Const dispatch table — configs are accessed via ctx.configs at call time
|
|
12
|
+
*/
|
|
13
|
+
import { SupportedLanguages } from '../../config/supported-languages.js';
|
|
14
|
+
import type { SyntaxNode } from './utils.js';
|
|
15
|
+
import type { TsconfigPaths, GoModuleConfig, CSharpProjectConfig, ComposerConfig } from './resolvers/index.js';
|
|
16
|
+
import type { SwiftPackageConfig } from './language-config.js';
|
|
17
|
+
import { extractTsNamedBindings, extractPythonNamedBindings, extractKotlinNamedBindings, extractRustNamedBindings, extractPhpNamedBindings, extractCsharpNamedBindings, extractJavaNamedBindings } from './named-binding-extraction.js';
|
|
18
|
+
import type { ImportResolutionContext } from './import-processor.js';
|
|
19
|
+
/**
|
|
20
|
+
* Result of resolving an import via language-specific dispatch.
|
|
21
|
+
* - 'files': resolved to one or more files -> add to ImportMap
|
|
22
|
+
* - 'package': resolved to a directory -> add graph edges + store dirSuffix in PackageMap
|
|
23
|
+
* - null: no resolution (external dependency, etc.)
|
|
24
|
+
*/
|
|
25
|
+
export type ImportResult = {
|
|
26
|
+
kind: 'files';
|
|
27
|
+
files: string[];
|
|
28
|
+
} | {
|
|
29
|
+
kind: 'package';
|
|
30
|
+
files: string[];
|
|
31
|
+
dirSuffix: string;
|
|
32
|
+
} | null;
|
|
33
|
+
/** Bundled language-specific configs loaded once per ingestion run. */
|
|
34
|
+
export interface ImportConfigs {
|
|
35
|
+
tsconfigPaths: TsconfigPaths | null;
|
|
36
|
+
goModule: GoModuleConfig | null;
|
|
37
|
+
composerConfig: ComposerConfig | null;
|
|
38
|
+
swiftPackageConfig: SwiftPackageConfig | null;
|
|
39
|
+
csharpConfigs: CSharpProjectConfig[];
|
|
40
|
+
}
|
|
41
|
+
/** Full context for import resolution: file lookups + language configs. */
|
|
42
|
+
export interface ResolveCtx extends ImportResolutionContext {
|
|
43
|
+
configs: ImportConfigs;
|
|
44
|
+
}
|
|
45
|
+
/** Per-language import resolver -- function alias matching ExportChecker/CallRouter pattern. */
|
|
46
|
+
export type ImportResolverFn = (rawImportPath: string, filePath: string, resolveCtx: ResolveCtx) => ImportResult;
|
|
47
|
+
/** A single named import binding: local name in the importing file and exported name from the source.
|
|
48
|
+
* When `isModuleAlias` is true, the binding represents a Python `import X as Y` module alias
|
|
49
|
+
* and is routed to moduleAliasMap instead of namedImportMap during import processing. */
|
|
50
|
+
export interface NamedBinding {
|
|
51
|
+
local: string;
|
|
52
|
+
exported: string;
|
|
53
|
+
isModuleAlias?: boolean;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Clean and preprocess a raw import source text into a resolved import path.
|
|
57
|
+
* Strips quotes/angle brackets (universal) and applies language-specific
|
|
58
|
+
* transformations (currently only Kotlin wildcard import detection).
|
|
59
|
+
*/
|
|
60
|
+
export declare function preprocessImportPath(sourceText: string, importNode: SyntaxNode, language: SupportedLanguages): string | null;
|
|
61
|
+
/**
|
|
62
|
+
* Per-language import resolver dispatch table.
|
|
63
|
+
* Configs are accessed via ctx.configs at call time — no factory closure needed.
|
|
64
|
+
* Each resolver encapsulates the full resolution flow for its language, including
|
|
65
|
+
* fallthrough to standard resolution where appropriate.
|
|
66
|
+
*/
|
|
67
|
+
export declare const importResolvers: {
|
|
68
|
+
javascript: (raw: string, fp: string, ctx: ResolveCtx) => ImportResult;
|
|
69
|
+
typescript: (raw: string, fp: string, ctx: ResolveCtx) => ImportResult;
|
|
70
|
+
python: (raw: string, fp: string, ctx: ResolveCtx) => ImportResult;
|
|
71
|
+
java: (raw: string, fp: string, ctx: ResolveCtx) => ImportResult;
|
|
72
|
+
c: (raw: string, fp: string, ctx: ResolveCtx) => ImportResult;
|
|
73
|
+
cpp: (raw: string, fp: string, ctx: ResolveCtx) => ImportResult;
|
|
74
|
+
csharp: (raw: string, fp: string, ctx: ResolveCtx) => ImportResult;
|
|
75
|
+
go: (raw: string, fp: string, ctx: ResolveCtx) => ImportResult;
|
|
76
|
+
ruby: (raw: string, fp: string, ctx: ResolveCtx) => ImportResult;
|
|
77
|
+
rust: (raw: string, fp: string, ctx: ResolveCtx) => ImportResult;
|
|
78
|
+
php: (raw: string, fp: string, ctx: ResolveCtx) => ImportResult;
|
|
79
|
+
kotlin: (raw: string, fp: string, ctx: ResolveCtx) => ImportResult;
|
|
80
|
+
swift: (raw: string, fp: string, ctx: ResolveCtx) => ImportResult;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Per-language named binding extractor dispatch table.
|
|
84
|
+
* Languages with whole-module import semantics (Go, Ruby, C/C++, Swift) return undefined --
|
|
85
|
+
* their bindings are synthesized post-parse by synthesizeWildcardImportBindings() in pipeline.ts.
|
|
86
|
+
*/
|
|
87
|
+
export declare const namedBindingExtractors: {
|
|
88
|
+
javascript: typeof extractTsNamedBindings;
|
|
89
|
+
typescript: typeof extractTsNamedBindings;
|
|
90
|
+
python: typeof extractPythonNamedBindings;
|
|
91
|
+
java: typeof extractJavaNamedBindings;
|
|
92
|
+
c: any;
|
|
93
|
+
cpp: any;
|
|
94
|
+
csharp: typeof extractCsharpNamedBindings;
|
|
95
|
+
go: any;
|
|
96
|
+
ruby: any;
|
|
97
|
+
rust: typeof extractRustNamedBindings;
|
|
98
|
+
php: typeof extractPhpNamedBindings;
|
|
99
|
+
kotlin: typeof extractKotlinNamedBindings;
|
|
100
|
+
swift: any;
|
|
101
|
+
};
|