@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,31 @@
|
|
|
1
|
+
/** @file import-processor.ts @description Resolves import statements across all supported languages, builds the ImportMap/PackageMap/NamedImportMap, and emits IMPORTS graph edges. Supports language-specific resolution for TS, Go, C#, PHP, Swift, Ruby, Rust, and JVM languages */
|
|
2
|
+
import { KnowledgeGraph } from '../graph/types.js';
|
|
3
|
+
import { ASTCache } from './ast-cache.js';
|
|
4
|
+
import type { ExtractedImport } from './workers/parse-worker.js';
|
|
5
|
+
import type { ResolutionContext } from './resolution-context.js';
|
|
6
|
+
import type { SuffixIndex } from './resolvers/index.js';
|
|
7
|
+
export type { SuffixIndex, TsconfigPaths, GoModuleConfig, CSharpProjectConfig, ComposerConfig } from './resolvers/index.js';
|
|
8
|
+
export type ImportMap = Map<string, Set<string>>;
|
|
9
|
+
export type PackageMap = Map<string, Set<string>>;
|
|
10
|
+
export interface NamedImportBinding {
|
|
11
|
+
sourcePath: string;
|
|
12
|
+
exportedName: string;
|
|
13
|
+
}
|
|
14
|
+
export type NamedImportMap = Map<string, Map<string, NamedImportBinding>>;
|
|
15
|
+
/** Check if a file path is directly inside a package directory identified by its suffix */
|
|
16
|
+
export declare function isFileInPackageDir(filePath: string, dirSuffix: string): boolean;
|
|
17
|
+
export interface ImportResolutionContext {
|
|
18
|
+
allFilePaths: Set<string>;
|
|
19
|
+
allFileList: string[];
|
|
20
|
+
normalizedFileList: string[];
|
|
21
|
+
suffixIndex: SuffixIndex;
|
|
22
|
+
resolveCache: Map<string, string | null>;
|
|
23
|
+
}
|
|
24
|
+
export declare function buildImportResolutionContext(allPaths: string[]): ImportResolutionContext;
|
|
25
|
+
export declare const processImports: (graph: KnowledgeGraph, files: {
|
|
26
|
+
path: string;
|
|
27
|
+
content: string;
|
|
28
|
+
}[], astCache: ASTCache, ctx: ResolutionContext, onProgress?: (current: number, total: number) => void, repoRoot?: string, allPaths?: string[]) => Promise<void>;
|
|
29
|
+
export declare const processImportsFromExtracted: (graph: KnowledgeGraph, files: {
|
|
30
|
+
path: string;
|
|
31
|
+
}[], extractedImports: ExtractedImport[], ctx: ResolutionContext, onProgress?: (current: number, total: number) => void, repoRoot?: string, prebuiltCtx?: ImportResolutionContext) => Promise<void>;
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
// code-mapper/src/core/ingestion/import-processor.ts
|
|
2
|
+
/** @file import-processor.ts @description Resolves import statements across all supported languages, builds the ImportMap/PackageMap/NamedImportMap, and emits IMPORTS graph edges. Supports language-specific resolution for TS, Go, C#, PHP, Swift, Ruby, Rust, and JVM languages */
|
|
3
|
+
import Parser from 'tree-sitter';
|
|
4
|
+
import { isLanguageAvailable, loadParser, loadLanguage } 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, isVerboseIngestionEnabled, yieldToEventLoop } from './utils.js';
|
|
8
|
+
import { SupportedLanguages } from '../../config/supported-languages.js';
|
|
9
|
+
import { extractNamedBindings } from './named-binding-extraction.js';
|
|
10
|
+
import { getTreeSitterBufferSize } from './constants.js';
|
|
11
|
+
import { loadTsconfigPaths, loadGoModulePath, loadComposerConfig, loadCSharpProjectConfig, loadSwiftPackageConfig, } from './language-config.js';
|
|
12
|
+
import { buildSuffixIndex, resolveImportPath, appendKotlinWildcard, KOTLIN_EXTENSIONS, resolveJvmWildcard, resolveJvmMemberImport, resolveGoPackageDir, resolveGoPackage, resolveCSharpImport, resolveCSharpNamespaceDir, resolvePhpImport, resolveRustImport, resolveRubyImport, } from './resolvers/index.js';
|
|
13
|
+
import { callRouters } from './call-routing.js';
|
|
14
|
+
const isDev = process.env.NODE_ENV === 'development';
|
|
15
|
+
/** Check if a file path is directly inside a package directory identified by its suffix */
|
|
16
|
+
export function isFileInPackageDir(filePath, dirSuffix) {
|
|
17
|
+
// Prepend '/' so paths like "internal/auth/service.go" match suffix "/internal/auth/"
|
|
18
|
+
const normalized = '/' + filePath.replace(/\\/g, '/');
|
|
19
|
+
if (!normalized.includes(dirSuffix))
|
|
20
|
+
return false;
|
|
21
|
+
const afterDir = normalized.substring(normalized.indexOf(dirSuffix) + dirSuffix.length);
|
|
22
|
+
return !afterDir.includes('/');
|
|
23
|
+
}
|
|
24
|
+
export function buildImportResolutionContext(allPaths) {
|
|
25
|
+
const allFileList = allPaths;
|
|
26
|
+
const normalizedFileList = allFileList.map(p => p.replace(/\\/g, '/'));
|
|
27
|
+
const allFilePaths = new Set(allFileList);
|
|
28
|
+
const suffixIndex = buildSuffixIndex(normalizedFileList, allFileList);
|
|
29
|
+
return { allFilePaths, allFileList, normalizedFileList, suffixIndex, resolveCache: new Map() };
|
|
30
|
+
}
|
|
31
|
+
/** Shared language dispatch for import resolution, used by both AST and fast paths */
|
|
32
|
+
function resolveLanguageImport(filePath, rawImportPath, language, configs, ctx) {
|
|
33
|
+
const { allFilePaths, allFileList, normalizedFileList, index, resolveCache } = ctx;
|
|
34
|
+
const { tsconfigPaths, goModule, composerConfig, swiftPackageConfig, csharpConfigs } = configs;
|
|
35
|
+
// JVM languages (Java + Kotlin): handle wildcards and member imports
|
|
36
|
+
if (language === SupportedLanguages.Java || language === SupportedLanguages.Kotlin) {
|
|
37
|
+
const exts = language === SupportedLanguages.Java ? ['.java'] : KOTLIN_EXTENSIONS;
|
|
38
|
+
if (rawImportPath.endsWith('.*')) {
|
|
39
|
+
const matchedFiles = resolveJvmWildcard(rawImportPath, normalizedFileList, allFileList, exts, index);
|
|
40
|
+
if (matchedFiles.length === 0 && language === SupportedLanguages.Kotlin) {
|
|
41
|
+
const javaMatches = resolveJvmWildcard(rawImportPath, normalizedFileList, allFileList, ['.java'], index);
|
|
42
|
+
if (javaMatches.length > 0)
|
|
43
|
+
return { kind: 'files', files: javaMatches };
|
|
44
|
+
}
|
|
45
|
+
if (matchedFiles.length > 0)
|
|
46
|
+
return { kind: 'files', files: matchedFiles };
|
|
47
|
+
// Fall through to standard resolution
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
let memberResolved = resolveJvmMemberImport(rawImportPath, normalizedFileList, allFileList, exts, index);
|
|
51
|
+
if (!memberResolved && language === SupportedLanguages.Kotlin) {
|
|
52
|
+
memberResolved = resolveJvmMemberImport(rawImportPath, normalizedFileList, allFileList, ['.java'], index);
|
|
53
|
+
}
|
|
54
|
+
if (memberResolved)
|
|
55
|
+
return { kind: 'files', files: [memberResolved] };
|
|
56
|
+
// Fall through to standard resolution
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Go: handle package-level imports
|
|
60
|
+
if (language === SupportedLanguages.Go && goModule && rawImportPath.startsWith(goModule.modulePath)) {
|
|
61
|
+
const pkgSuffix = resolveGoPackageDir(rawImportPath, goModule);
|
|
62
|
+
if (pkgSuffix) {
|
|
63
|
+
const pkgFiles = resolveGoPackage(rawImportPath, goModule, normalizedFileList, allFileList);
|
|
64
|
+
if (pkgFiles.length > 0) {
|
|
65
|
+
return { kind: 'package', files: pkgFiles, dirSuffix: pkgSuffix };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Fall through if no files found (package might be external)
|
|
69
|
+
}
|
|
70
|
+
// C#: handle namespace-based imports (using directives)
|
|
71
|
+
if (language === SupportedLanguages.CSharp && csharpConfigs.length > 0) {
|
|
72
|
+
const resolvedFiles = resolveCSharpImport(rawImportPath, csharpConfigs, normalizedFileList, allFileList, index);
|
|
73
|
+
if (resolvedFiles.length > 1) {
|
|
74
|
+
const dirSuffix = resolveCSharpNamespaceDir(rawImportPath, csharpConfigs);
|
|
75
|
+
if (dirSuffix) {
|
|
76
|
+
return { kind: 'package', files: resolvedFiles, dirSuffix };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (resolvedFiles.length > 0)
|
|
80
|
+
return { kind: 'files', files: resolvedFiles };
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
// PHP: namespace-based imports (use statements)
|
|
84
|
+
if (language === SupportedLanguages.PHP) {
|
|
85
|
+
const resolved = resolvePhpImport(rawImportPath, composerConfig, allFilePaths, normalizedFileList, allFileList, index);
|
|
86
|
+
return resolved ? { kind: 'files', files: [resolved] } : null;
|
|
87
|
+
}
|
|
88
|
+
// Swift: handle module imports
|
|
89
|
+
if (language === SupportedLanguages.Swift && swiftPackageConfig) {
|
|
90
|
+
const targetDir = swiftPackageConfig.targets.get(rawImportPath);
|
|
91
|
+
if (targetDir) {
|
|
92
|
+
const dirPrefix = targetDir + '/';
|
|
93
|
+
const files = [];
|
|
94
|
+
for (let i = 0; i < normalizedFileList.length; i++) {
|
|
95
|
+
if (normalizedFileList[i].startsWith(dirPrefix) && normalizedFileList[i].endsWith('.swift')) {
|
|
96
|
+
files.push(allFileList[i]);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (files.length > 0)
|
|
100
|
+
return { kind: 'files', files };
|
|
101
|
+
}
|
|
102
|
+
return null; // External framework (Foundation, UIKit, etc)
|
|
103
|
+
}
|
|
104
|
+
// Ruby: require / require_relative
|
|
105
|
+
if (language === SupportedLanguages.Ruby) {
|
|
106
|
+
const resolved = resolveRubyImport(rawImportPath, normalizedFileList, allFileList, index);
|
|
107
|
+
return resolved ? { kind: 'files', files: [resolved] } : null;
|
|
108
|
+
}
|
|
109
|
+
// Rust: expand top-level grouped imports — use {crate::a, crate::b}
|
|
110
|
+
if (language === SupportedLanguages.Rust && rawImportPath.startsWith('{') && rawImportPath.endsWith('}')) {
|
|
111
|
+
const inner = rawImportPath.slice(1, -1);
|
|
112
|
+
const parts = inner.split(',').map(p => p.trim()).filter(Boolean);
|
|
113
|
+
const resolved = [];
|
|
114
|
+
for (const part of parts) {
|
|
115
|
+
const r = resolveRustImport(filePath, part, allFilePaths);
|
|
116
|
+
if (r)
|
|
117
|
+
resolved.push(r);
|
|
118
|
+
}
|
|
119
|
+
return resolved.length > 0 ? { kind: 'files', files: resolved } : null;
|
|
120
|
+
}
|
|
121
|
+
// Standard single-file resolution
|
|
122
|
+
const resolvedPath = resolveImportPath(filePath, rawImportPath, allFilePaths, allFileList, normalizedFileList, resolveCache, language, tsconfigPaths, index);
|
|
123
|
+
return resolvedPath ? { kind: 'files', files: [resolvedPath] } : null;
|
|
124
|
+
}
|
|
125
|
+
/** Apply an ImportResult — emit graph edges, update ImportMap/PackageMap and NamedImportMap */
|
|
126
|
+
function applyImportResult(result, filePath, importMap, packageMap, addImportEdge, addImportGraphEdge, namedBindings, namedImportMap) {
|
|
127
|
+
if (!result)
|
|
128
|
+
return;
|
|
129
|
+
if (result.kind === 'package' && packageMap) {
|
|
130
|
+
// Store directory suffix in PackageMap (skip ImportMap expansion)
|
|
131
|
+
for (const resolvedFile of result.files) {
|
|
132
|
+
addImportGraphEdge(filePath, resolvedFile);
|
|
133
|
+
}
|
|
134
|
+
if (!packageMap.has(filePath))
|
|
135
|
+
packageMap.set(filePath, new Set());
|
|
136
|
+
packageMap.get(filePath).add(result.dirSuffix);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
// 'files' kind or 'package' without PackageMap — use ImportMap directly
|
|
140
|
+
const files = result.files;
|
|
141
|
+
for (const resolvedFile of files) {
|
|
142
|
+
addImportEdge(filePath, resolvedFile);
|
|
143
|
+
}
|
|
144
|
+
// Record named bindings for precise Tier 2a resolution
|
|
145
|
+
if (namedBindings && namedImportMap && files.length === 1) {
|
|
146
|
+
const resolvedFile = files[0];
|
|
147
|
+
if (!namedImportMap.has(filePath))
|
|
148
|
+
namedImportMap.set(filePath, new Map());
|
|
149
|
+
const fileBindings = namedImportMap.get(filePath);
|
|
150
|
+
for (const binding of namedBindings) {
|
|
151
|
+
fileBindings.set(binding.local, { sourcePath: resolvedFile, exportedName: binding.exported });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Create transitive IMPORTS edges by walking re-export chains in the NamedImportMap
|
|
158
|
+
*
|
|
159
|
+
* When A imports {User} from B and B re-exports {User} from C, adds edge A→C
|
|
160
|
+
* Must be called AFTER all imports are processed
|
|
161
|
+
*/
|
|
162
|
+
function addTransitiveImportEdges(namedImportMap, importMap, addImportEdge) {
|
|
163
|
+
for (const [filePath, bindings] of namedImportMap) {
|
|
164
|
+
const existingImports = importMap.get(filePath);
|
|
165
|
+
for (const [localName, binding] of bindings) {
|
|
166
|
+
// Walk the re-export chain to find the ultimate source file
|
|
167
|
+
let sourcePath = binding.sourcePath;
|
|
168
|
+
const visited = new Set([`${filePath}:${localName}`]);
|
|
169
|
+
let depth = 0;
|
|
170
|
+
while (depth < 5) {
|
|
171
|
+
const sourceBindings = namedImportMap.get(sourcePath);
|
|
172
|
+
if (!sourceBindings)
|
|
173
|
+
break;
|
|
174
|
+
const nextBinding = sourceBindings.get(binding.exportedName);
|
|
175
|
+
if (!nextBinding)
|
|
176
|
+
break;
|
|
177
|
+
const key = `${nextBinding.sourcePath}:${nextBinding.exportedName}`;
|
|
178
|
+
if (visited.has(key))
|
|
179
|
+
break;
|
|
180
|
+
visited.add(key);
|
|
181
|
+
sourcePath = nextBinding.sourcePath;
|
|
182
|
+
depth++;
|
|
183
|
+
}
|
|
184
|
+
// If the chain resolved to a different file, create a transitive edge
|
|
185
|
+
if (depth > 0 && (!existingImports || !existingImports.has(sourcePath))) {
|
|
186
|
+
addImportEdge(filePath, sourcePath);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
export const processImports = async (graph, files, astCache, ctx, onProgress, repoRoot, allPaths) => {
|
|
192
|
+
const importMap = ctx.importMap;
|
|
193
|
+
const packageMap = ctx.packageMap;
|
|
194
|
+
const namedImportMap = ctx.namedImportMap;
|
|
195
|
+
// Use allPaths (full repo) when available for cross-chunk resolution
|
|
196
|
+
const allFileList = allPaths ?? files.map(f => f.path);
|
|
197
|
+
const allFilePaths = new Set(allFileList);
|
|
198
|
+
const parser = await loadParser();
|
|
199
|
+
const logSkipped = isVerboseIngestionEnabled();
|
|
200
|
+
const skippedByLang = logSkipped ? new Map() : null;
|
|
201
|
+
const resolveCache = new Map();
|
|
202
|
+
// Pre-compute normalized file list once
|
|
203
|
+
const normalizedFileList = allFileList.map(p => p.replace(/\\/g, '/'));
|
|
204
|
+
// Build suffix index for O(1) lookups
|
|
205
|
+
const index = buildSuffixIndex(normalizedFileList, allFileList);
|
|
206
|
+
// Track import statistics
|
|
207
|
+
let totalImportsFound = 0;
|
|
208
|
+
let totalImportsResolved = 0;
|
|
209
|
+
// Load language-specific configs once before the file loop
|
|
210
|
+
const effectiveRoot = repoRoot || '';
|
|
211
|
+
const configs = {
|
|
212
|
+
tsconfigPaths: await loadTsconfigPaths(effectiveRoot),
|
|
213
|
+
goModule: await loadGoModulePath(effectiveRoot),
|
|
214
|
+
composerConfig: await loadComposerConfig(effectiveRoot),
|
|
215
|
+
swiftPackageConfig: await loadSwiftPackageConfig(effectiveRoot),
|
|
216
|
+
csharpConfigs: await loadCSharpProjectConfig(effectiveRoot),
|
|
217
|
+
};
|
|
218
|
+
const resolveCtx = { allFilePaths, allFileList, normalizedFileList, index, resolveCache };
|
|
219
|
+
// Add IMPORTS edge to graph only (no ImportMap update)
|
|
220
|
+
const addImportGraphEdge = (filePath, resolvedPath) => {
|
|
221
|
+
const sourceId = generateId('File', filePath);
|
|
222
|
+
const targetId = generateId('File', resolvedPath);
|
|
223
|
+
const relId = generateId('IMPORTS', `${filePath}->${resolvedPath}`);
|
|
224
|
+
totalImportsResolved++;
|
|
225
|
+
graph.addRelationship({
|
|
226
|
+
id: relId,
|
|
227
|
+
sourceId,
|
|
228
|
+
targetId,
|
|
229
|
+
type: 'IMPORTS',
|
|
230
|
+
confidence: 1.0,
|
|
231
|
+
reason: '',
|
|
232
|
+
});
|
|
233
|
+
};
|
|
234
|
+
// Add IMPORTS edge + update ImportMap
|
|
235
|
+
const addImportEdge = (filePath, resolvedPath) => {
|
|
236
|
+
addImportGraphEdge(filePath, resolvedPath);
|
|
237
|
+
if (!importMap.has(filePath)) {
|
|
238
|
+
importMap.set(filePath, new Set());
|
|
239
|
+
}
|
|
240
|
+
importMap.get(filePath).add(resolvedPath);
|
|
241
|
+
};
|
|
242
|
+
for (let i = 0; i < files.length; i++) {
|
|
243
|
+
const file = files[i];
|
|
244
|
+
onProgress?.(i + 1, files.length);
|
|
245
|
+
if (i % 20 === 0)
|
|
246
|
+
await yieldToEventLoop();
|
|
247
|
+
// Check language support
|
|
248
|
+
const language = getLanguageFromFilename(file.path);
|
|
249
|
+
if (!language)
|
|
250
|
+
continue;
|
|
251
|
+
if (!isLanguageAvailable(language)) {
|
|
252
|
+
if (skippedByLang) {
|
|
253
|
+
skippedByLang.set(language, (skippedByLang.get(language) ?? 0) + 1);
|
|
254
|
+
}
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
const queryStr = LANGUAGE_QUERIES[language];
|
|
258
|
+
if (!queryStr)
|
|
259
|
+
continue;
|
|
260
|
+
// Load the language before querying (parser is stateful)
|
|
261
|
+
await loadLanguage(language, file.path);
|
|
262
|
+
// Get AST (try cache first)
|
|
263
|
+
let tree = astCache.get(file.path);
|
|
264
|
+
let wasReparsed = false;
|
|
265
|
+
if (!tree) {
|
|
266
|
+
try {
|
|
267
|
+
tree = parser.parse(file.content, undefined, { bufferSize: getTreeSitterBufferSize(file.content.length) });
|
|
268
|
+
}
|
|
269
|
+
catch (parseError) {
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
wasReparsed = true;
|
|
273
|
+
// Cache re-parsed tree for call/heritage phases
|
|
274
|
+
astCache.set(file.path, tree);
|
|
275
|
+
}
|
|
276
|
+
let query;
|
|
277
|
+
let matches;
|
|
278
|
+
try {
|
|
279
|
+
const lang = parser.getLanguage();
|
|
280
|
+
query = new Parser.Query(lang, queryStr);
|
|
281
|
+
matches = query.matches(tree.rootNode);
|
|
282
|
+
}
|
|
283
|
+
catch (queryError) {
|
|
284
|
+
if (isDev) {
|
|
285
|
+
console.group(`🔴 Query Error: ${file.path}`);
|
|
286
|
+
console.log('Language:', language);
|
|
287
|
+
console.log('Query (first 200 chars):', queryStr.substring(0, 200) + '...');
|
|
288
|
+
console.log('Error:', queryError?.message || queryError);
|
|
289
|
+
console.log('File content (first 300 chars):', file.content.substring(0, 300));
|
|
290
|
+
console.log('AST root type:', tree.rootNode?.type);
|
|
291
|
+
console.log('AST has errors:', tree.rootNode?.hasError);
|
|
292
|
+
console.groupEnd();
|
|
293
|
+
}
|
|
294
|
+
if (wasReparsed)
|
|
295
|
+
tree.delete?.();
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
matches.forEach(match => {
|
|
299
|
+
const captureMap = {};
|
|
300
|
+
match.captures.forEach(c => captureMap[c.name] = c.node);
|
|
301
|
+
if (captureMap['import']) {
|
|
302
|
+
const sourceNode = captureMap['import.source'];
|
|
303
|
+
if (!sourceNode) {
|
|
304
|
+
if (isDev) {
|
|
305
|
+
console.log(`⚠️ Import captured but no source node in ${file.path}`);
|
|
306
|
+
}
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
// Clean path — remove quotes and angle brackets for C/C++ includes
|
|
310
|
+
const rawImportPath = language === SupportedLanguages.Kotlin
|
|
311
|
+
? appendKotlinWildcard(sourceNode.text.replace(/['"<>]/g, ''), captureMap['import'])
|
|
312
|
+
: sourceNode.text.replace(/['"<>]/g, '');
|
|
313
|
+
totalImportsFound++;
|
|
314
|
+
const result = resolveLanguageImport(file.path, rawImportPath, language, configs, resolveCtx);
|
|
315
|
+
const bindings = namedImportMap ? extractNamedBindings(captureMap['import'], language) : undefined;
|
|
316
|
+
applyImportResult(result, file.path, importMap, packageMap, addImportEdge, addImportGraphEdge, bindings, namedImportMap);
|
|
317
|
+
}
|
|
318
|
+
// Language-specific call-as-import routing (Ruby require, etc)
|
|
319
|
+
if (captureMap['call']) {
|
|
320
|
+
const callNameNode = captureMap['call.name'];
|
|
321
|
+
if (callNameNode) {
|
|
322
|
+
const callRouter = callRouters[language];
|
|
323
|
+
const routed = callRouter(callNameNode.text, captureMap['call']);
|
|
324
|
+
if (routed && routed.kind === 'import') {
|
|
325
|
+
totalImportsFound++;
|
|
326
|
+
const result = resolveLanguageImport(file.path, routed.importPath, language, configs, resolveCtx);
|
|
327
|
+
applyImportResult(result, file.path, importMap, packageMap, addImportEdge, addImportGraphEdge);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
// Tree owned by the LRU cache — no manual delete needed
|
|
333
|
+
}
|
|
334
|
+
if (skippedByLang && skippedByLang.size > 0) {
|
|
335
|
+
for (const [lang, count] of skippedByLang.entries()) {
|
|
336
|
+
console.warn(`[ingestion] Skipped ${count} ${lang} file(s) in import processing — ${lang} parser not available.`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
// Create transitive IMPORTS edges for re-export chains (barrel files)
|
|
340
|
+
addTransitiveImportEdges(namedImportMap, importMap, addImportEdge);
|
|
341
|
+
if (isDev) {
|
|
342
|
+
console.log(`Import processing complete: ${totalImportsResolved}/${totalImportsFound} imports resolved to graph edges`);
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
// Fast path: resolve pre-extracted imports (no AST parsing needed)
|
|
346
|
+
export const processImportsFromExtracted = async (graph, files, extractedImports, ctx, onProgress, repoRoot, prebuiltCtx) => {
|
|
347
|
+
const importMap = ctx.importMap;
|
|
348
|
+
const packageMap = ctx.packageMap;
|
|
349
|
+
const namedImportMap = ctx.namedImportMap;
|
|
350
|
+
const importCtx = prebuiltCtx ?? buildImportResolutionContext(files.map(f => f.path));
|
|
351
|
+
const { allFilePaths, allFileList, normalizedFileList, suffixIndex: index, resolveCache } = importCtx;
|
|
352
|
+
let totalImportsFound = 0;
|
|
353
|
+
let totalImportsResolved = 0;
|
|
354
|
+
const effectiveRoot = repoRoot || '';
|
|
355
|
+
const configs = {
|
|
356
|
+
tsconfigPaths: await loadTsconfigPaths(effectiveRoot),
|
|
357
|
+
goModule: await loadGoModulePath(effectiveRoot),
|
|
358
|
+
composerConfig: await loadComposerConfig(effectiveRoot),
|
|
359
|
+
swiftPackageConfig: await loadSwiftPackageConfig(effectiveRoot),
|
|
360
|
+
csharpConfigs: await loadCSharpProjectConfig(effectiveRoot),
|
|
361
|
+
};
|
|
362
|
+
const resolveCtx = { allFilePaths, allFileList, normalizedFileList, index, resolveCache };
|
|
363
|
+
// Add IMPORTS edge to graph only (no ImportMap update)
|
|
364
|
+
const addImportGraphEdge = (filePath, resolvedPath) => {
|
|
365
|
+
const sourceId = generateId('File', filePath);
|
|
366
|
+
const targetId = generateId('File', resolvedPath);
|
|
367
|
+
const relId = generateId('IMPORTS', `${filePath}->${resolvedPath}`);
|
|
368
|
+
totalImportsResolved++;
|
|
369
|
+
graph.addRelationship({
|
|
370
|
+
id: relId,
|
|
371
|
+
sourceId,
|
|
372
|
+
targetId,
|
|
373
|
+
type: 'IMPORTS',
|
|
374
|
+
confidence: 1.0,
|
|
375
|
+
reason: '',
|
|
376
|
+
});
|
|
377
|
+
};
|
|
378
|
+
// Add IMPORTS edge + update ImportMap
|
|
379
|
+
const addImportEdge = (filePath, resolvedPath) => {
|
|
380
|
+
addImportGraphEdge(filePath, resolvedPath);
|
|
381
|
+
if (!importMap.has(filePath)) {
|
|
382
|
+
importMap.set(filePath, new Set());
|
|
383
|
+
}
|
|
384
|
+
importMap.get(filePath).add(resolvedPath);
|
|
385
|
+
};
|
|
386
|
+
// Group by file for progress reporting
|
|
387
|
+
const importsByFile = new Map();
|
|
388
|
+
for (const imp of extractedImports) {
|
|
389
|
+
let list = importsByFile.get(imp.filePath);
|
|
390
|
+
if (!list) {
|
|
391
|
+
list = [];
|
|
392
|
+
importsByFile.set(imp.filePath, list);
|
|
393
|
+
}
|
|
394
|
+
list.push(imp);
|
|
395
|
+
}
|
|
396
|
+
const totalFiles = importsByFile.size;
|
|
397
|
+
let filesProcessed = 0;
|
|
398
|
+
for (const [filePath, fileImports] of importsByFile) {
|
|
399
|
+
filesProcessed++;
|
|
400
|
+
if (filesProcessed % 100 === 0) {
|
|
401
|
+
onProgress?.(filesProcessed, totalFiles);
|
|
402
|
+
await yieldToEventLoop();
|
|
403
|
+
}
|
|
404
|
+
for (const imp of fileImports) {
|
|
405
|
+
totalImportsFound++;
|
|
406
|
+
const result = resolveLanguageImport(filePath, imp.rawImportPath, imp.language, configs, resolveCtx);
|
|
407
|
+
applyImportResult(result, filePath, importMap, packageMap, addImportEdge, addImportGraphEdge, imp.namedBindings, namedImportMap);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
onProgress?.(totalFiles, totalFiles);
|
|
411
|
+
// Create transitive IMPORTS edges for re-export chains (barrel files)
|
|
412
|
+
addTransitiveImportEdges(namedImportMap, importMap, addImportEdge);
|
|
413
|
+
if (isDev) {
|
|
414
|
+
console.log(`Import processing (fast path): ${totalImportsResolved}/${totalImportsFound} imports resolved to graph edges`);
|
|
415
|
+
}
|
|
416
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** @file language-config.ts @description Loads language-specific project configuration (tsconfig.json, go.mod, composer.json, .csproj, Package.swift) for import path resolution */
|
|
2
|
+
export interface TsconfigPaths {
|
|
3
|
+
aliases: Map<string, string>;
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
}
|
|
6
|
+
export interface GoModuleConfig {
|
|
7
|
+
modulePath: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ComposerConfig {
|
|
10
|
+
psr4: Map<string, string>;
|
|
11
|
+
}
|
|
12
|
+
export interface CSharpProjectConfig {
|
|
13
|
+
rootNamespace: string;
|
|
14
|
+
projectDir: string;
|
|
15
|
+
}
|
|
16
|
+
export interface SwiftPackageConfig {
|
|
17
|
+
targets: Map<string, string>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Parse tsconfig.json to extract path aliases
|
|
21
|
+
*
|
|
22
|
+
* Tries tsconfig.json, tsconfig.app.json, tsconfig.base.json in order
|
|
23
|
+
*/
|
|
24
|
+
export declare function loadTsconfigPaths(repoRoot: string): Promise<TsconfigPaths | null>;
|
|
25
|
+
/** Parse go.mod to extract module path */
|
|
26
|
+
export declare function loadGoModulePath(repoRoot: string): Promise<GoModuleConfig | null>;
|
|
27
|
+
/** Parse composer.json to extract PSR-4 autoload mappings (including autoload-dev) */
|
|
28
|
+
export declare function loadComposerConfig(repoRoot: string): Promise<ComposerConfig | null>;
|
|
29
|
+
/** Parse .csproj files to extract RootNamespace via BFS scan up to 5 levels deep */
|
|
30
|
+
export declare function loadCSharpProjectConfig(repoRoot: string): Promise<CSharpProjectConfig[]>;
|
|
31
|
+
/** Load Swift Package Manager target map from Sources/ directories */
|
|
32
|
+
export declare function loadSwiftPackageConfig(repoRoot: string): Promise<SwiftPackageConfig | null>;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
// code-mapper/src/core/ingestion/language-config.ts
|
|
2
|
+
/** @file language-config.ts @description Loads language-specific project configuration (tsconfig.json, go.mod, composer.json, .csproj, Package.swift) for import path resolution */
|
|
3
|
+
import fs from 'fs/promises';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
const isDev = process.env.NODE_ENV === 'development';
|
|
6
|
+
/**
|
|
7
|
+
* Parse tsconfig.json to extract path aliases
|
|
8
|
+
*
|
|
9
|
+
* Tries tsconfig.json, tsconfig.app.json, tsconfig.base.json in order
|
|
10
|
+
*/
|
|
11
|
+
export async function loadTsconfigPaths(repoRoot) {
|
|
12
|
+
const candidates = ['tsconfig.json', 'tsconfig.app.json', 'tsconfig.base.json'];
|
|
13
|
+
for (const filename of candidates) {
|
|
14
|
+
try {
|
|
15
|
+
const tsconfigPath = path.join(repoRoot, filename);
|
|
16
|
+
const raw = await fs.readFile(tsconfigPath, 'utf-8');
|
|
17
|
+
// Strip JSON comments (// and /* */ style)
|
|
18
|
+
const stripped = raw.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, '');
|
|
19
|
+
const tsconfig = JSON.parse(stripped);
|
|
20
|
+
const compilerOptions = tsconfig.compilerOptions;
|
|
21
|
+
if (!compilerOptions?.paths)
|
|
22
|
+
continue;
|
|
23
|
+
const baseUrl = compilerOptions.baseUrl || '.';
|
|
24
|
+
const aliases = new Map();
|
|
25
|
+
for (const [pattern, targets] of Object.entries(compilerOptions.paths)) {
|
|
26
|
+
if (!Array.isArray(targets) || targets.length === 0)
|
|
27
|
+
continue;
|
|
28
|
+
const target = targets[0];
|
|
29
|
+
// Convert glob patterns: "@/*" -> "@/", "src/*" -> "src/"
|
|
30
|
+
const aliasPrefix = pattern.endsWith('/*') ? pattern.slice(0, -1) : pattern;
|
|
31
|
+
const targetPrefix = target.endsWith('/*') ? target.slice(0, -1) : target;
|
|
32
|
+
aliases.set(aliasPrefix, targetPrefix);
|
|
33
|
+
}
|
|
34
|
+
if (aliases.size > 0) {
|
|
35
|
+
if (isDev) {
|
|
36
|
+
console.log(`📦 Loaded ${aliases.size} path aliases from ${filename}`);
|
|
37
|
+
}
|
|
38
|
+
return { aliases, baseUrl };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// File doesn't exist or isn't valid JSON — try next
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
/** Parse go.mod to extract module path */
|
|
48
|
+
export async function loadGoModulePath(repoRoot) {
|
|
49
|
+
try {
|
|
50
|
+
const goModPath = path.join(repoRoot, 'go.mod');
|
|
51
|
+
const content = await fs.readFile(goModPath, 'utf-8');
|
|
52
|
+
const match = content.match(/^module\s+(\S+)/m);
|
|
53
|
+
if (match) {
|
|
54
|
+
if (isDev) {
|
|
55
|
+
console.log(`📦 Loaded Go module path: ${match[1]}`);
|
|
56
|
+
}
|
|
57
|
+
return { modulePath: match[1] };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// No go.mod
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
/** Parse composer.json to extract PSR-4 autoload mappings (including autoload-dev) */
|
|
66
|
+
export async function loadComposerConfig(repoRoot) {
|
|
67
|
+
try {
|
|
68
|
+
const composerPath = path.join(repoRoot, 'composer.json');
|
|
69
|
+
const raw = await fs.readFile(composerPath, 'utf-8');
|
|
70
|
+
const composer = JSON.parse(raw);
|
|
71
|
+
const psr4Raw = composer.autoload?.['psr-4'] ?? {};
|
|
72
|
+
const psr4Dev = composer['autoload-dev']?.['psr-4'] ?? {};
|
|
73
|
+
const merged = { ...psr4Raw, ...psr4Dev };
|
|
74
|
+
const psr4 = new Map();
|
|
75
|
+
for (const [ns, dir] of Object.entries(merged)) {
|
|
76
|
+
const nsNorm = ns.replace(/\\+$/, '');
|
|
77
|
+
const dirNorm = dir.replace(/\\/g, '/').replace(/\/+$/, '');
|
|
78
|
+
psr4.set(nsNorm, dirNorm);
|
|
79
|
+
}
|
|
80
|
+
if (isDev) {
|
|
81
|
+
console.log(`📦 Loaded ${psr4.size} PSR-4 mappings from composer.json`);
|
|
82
|
+
}
|
|
83
|
+
return { psr4 };
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** Parse .csproj files to extract RootNamespace via BFS scan up to 5 levels deep */
|
|
90
|
+
export async function loadCSharpProjectConfig(repoRoot) {
|
|
91
|
+
const configs = [];
|
|
92
|
+
// BFS scan for .csproj files, capped at 100 dirs
|
|
93
|
+
const scanQueue = [{ dir: repoRoot, depth: 0 }];
|
|
94
|
+
const maxDepth = 5;
|
|
95
|
+
const maxDirs = 100;
|
|
96
|
+
let dirsScanned = 0;
|
|
97
|
+
while (scanQueue.length > 0 && dirsScanned < maxDirs) {
|
|
98
|
+
const { dir, depth } = scanQueue.shift();
|
|
99
|
+
dirsScanned++;
|
|
100
|
+
try {
|
|
101
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
102
|
+
for (const entry of entries) {
|
|
103
|
+
if (entry.isDirectory() && depth < maxDepth) {
|
|
104
|
+
// Skip common non-project directories
|
|
105
|
+
if (entry.name === 'node_modules' || entry.name === '.git' || entry.name === 'bin' || entry.name === 'obj')
|
|
106
|
+
continue;
|
|
107
|
+
scanQueue.push({ dir: path.join(dir, entry.name), depth: depth + 1 });
|
|
108
|
+
}
|
|
109
|
+
if (entry.isFile() && entry.name.endsWith('.csproj')) {
|
|
110
|
+
try {
|
|
111
|
+
const csprojPath = path.join(dir, entry.name);
|
|
112
|
+
const content = await fs.readFile(csprojPath, 'utf-8');
|
|
113
|
+
const nsMatch = content.match(/<RootNamespace>\s*([^<]+)\s*<\/RootNamespace>/);
|
|
114
|
+
const rootNamespace = nsMatch
|
|
115
|
+
? nsMatch[1].trim()
|
|
116
|
+
: entry.name.replace(/\.csproj$/, '');
|
|
117
|
+
const projectDir = path.relative(repoRoot, dir).replace(/\\/g, '/');
|
|
118
|
+
configs.push({ rootNamespace, projectDir });
|
|
119
|
+
if (isDev) {
|
|
120
|
+
console.log(`📦 Loaded C# project: ${entry.name} (namespace: ${rootNamespace}, dir: ${projectDir})`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
// Can't read .csproj
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// Can't read directory
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return configs;
|
|
134
|
+
}
|
|
135
|
+
/** Load Swift Package Manager target map from Sources/ directories */
|
|
136
|
+
export async function loadSwiftPackageConfig(repoRoot) {
|
|
137
|
+
// SPM convention: Sources/<TargetName>/ or Package/Sources/<TargetName>/
|
|
138
|
+
const targets = new Map();
|
|
139
|
+
const sourceDirs = ['Sources', 'Package/Sources', 'src'];
|
|
140
|
+
for (const sourceDir of sourceDirs) {
|
|
141
|
+
try {
|
|
142
|
+
const fullPath = path.join(repoRoot, sourceDir);
|
|
143
|
+
const entries = await fs.readdir(fullPath, { withFileTypes: true });
|
|
144
|
+
for (const entry of entries) {
|
|
145
|
+
if (entry.isDirectory()) {
|
|
146
|
+
targets.set(entry.name, sourceDir + '/' + entry.name);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// Directory doesn't exist
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (targets.size > 0) {
|
|
155
|
+
if (isDev) {
|
|
156
|
+
console.log(`📦 Loaded ${targets.size} Swift package targets`);
|
|
157
|
+
}
|
|
158
|
+
return { targets };
|
|
159
|
+
}
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file mro-processor.ts
|
|
3
|
+
* @description Walks the inheritance DAG, collects methods from ancestors via HAS_METHOD
|
|
4
|
+
* edges, detects method-name collisions, and emits OVERRIDES edges using language-specific
|
|
5
|
+
* resolution rules (C++ leftmost base, C#/Java class-over-interface, Python C3 linearization,
|
|
6
|
+
* Rust requires qualified syntax). OVERRIDES edge direction: Class -> winning ancestor Method
|
|
7
|
+
*/
|
|
8
|
+
import { KnowledgeGraph } from '../graph/types.js';
|
|
9
|
+
import { SupportedLanguages } from '../../config/supported-languages.js';
|
|
10
|
+
export interface MROEntry {
|
|
11
|
+
classId: string;
|
|
12
|
+
className: string;
|
|
13
|
+
language: SupportedLanguages;
|
|
14
|
+
mro: string[];
|
|
15
|
+
ambiguities: MethodAmbiguity[];
|
|
16
|
+
}
|
|
17
|
+
export interface MethodAmbiguity {
|
|
18
|
+
methodName: string;
|
|
19
|
+
definedIn: Array<{
|
|
20
|
+
classId: string;
|
|
21
|
+
className: string;
|
|
22
|
+
methodId: string;
|
|
23
|
+
}>;
|
|
24
|
+
resolvedTo: string | null;
|
|
25
|
+
reason: string;
|
|
26
|
+
}
|
|
27
|
+
export interface MROResult {
|
|
28
|
+
entries: MROEntry[];
|
|
29
|
+
overrideEdges: number;
|
|
30
|
+
ambiguityCount: number;
|
|
31
|
+
}
|
|
32
|
+
export declare function computeMRO(graph: KnowledgeGraph): MROResult;
|