@kodus/kodus-graph 0.2.8 → 0.2.10
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/LICENSE +21 -0
- package/README.md +252 -0
- package/dist/analysis/blast-radius.d.ts +2 -0
- package/dist/analysis/blast-radius.js +55 -0
- package/dist/analysis/communities.d.ts +28 -0
- package/dist/analysis/communities.js +100 -0
- package/dist/analysis/context-builder.d.ts +34 -0
- package/dist/analysis/context-builder.js +92 -0
- package/dist/analysis/diff.d.ts +41 -0
- package/dist/analysis/diff.js +155 -0
- package/dist/analysis/enrich.d.ts +5 -0
- package/dist/analysis/enrich.js +126 -0
- package/dist/analysis/flows.d.ts +27 -0
- package/dist/analysis/flows.js +86 -0
- package/dist/analysis/inheritance.d.ts +3 -0
- package/dist/analysis/inheritance.js +31 -0
- package/dist/analysis/prompt-formatter.d.ts +2 -0
- package/dist/analysis/prompt-formatter.js +173 -0
- package/dist/analysis/risk-score.d.ts +4 -0
- package/dist/analysis/risk-score.js +51 -0
- package/dist/analysis/search.d.ts +11 -0
- package/dist/analysis/search.js +64 -0
- package/dist/analysis/test-gaps.d.ts +2 -0
- package/dist/analysis/test-gaps.js +14 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +210 -0
- package/dist/commands/analyze.d.ts +9 -0
- package/dist/commands/analyze.js +116 -0
- package/dist/commands/communities.d.ts +8 -0
- package/dist/commands/communities.js +9 -0
- package/dist/commands/context.d.ts +12 -0
- package/dist/commands/context.js +130 -0
- package/dist/commands/diff.d.ts +9 -0
- package/dist/commands/diff.js +89 -0
- package/dist/commands/flows.d.ts +8 -0
- package/dist/commands/flows.js +9 -0
- package/dist/commands/parse.d.ts +11 -0
- package/dist/commands/parse.js +101 -0
- package/dist/commands/search.d.ts +12 -0
- package/dist/commands/search.js +27 -0
- package/dist/commands/update.d.ts +7 -0
- package/dist/commands/update.js +154 -0
- package/dist/graph/builder.d.ts +6 -0
- package/dist/graph/builder.js +248 -0
- package/dist/graph/edges.d.ts +23 -0
- package/dist/graph/edges.js +159 -0
- package/dist/graph/json-writer.d.ts +9 -0
- package/dist/graph/json-writer.js +38 -0
- package/dist/graph/loader.d.ts +13 -0
- package/dist/graph/loader.js +101 -0
- package/dist/graph/merger.d.ts +7 -0
- package/dist/graph/merger.js +18 -0
- package/dist/graph/types.d.ts +252 -0
- package/dist/graph/types.js +1 -0
- package/dist/parser/batch.d.ts +5 -0
- package/dist/parser/batch.js +93 -0
- package/dist/parser/discovery.d.ts +7 -0
- package/dist/parser/discovery.js +61 -0
- package/dist/parser/extractor.d.ts +4 -0
- package/dist/parser/extractor.js +33 -0
- package/dist/parser/extractors/generic.d.ts +8 -0
- package/dist/parser/extractors/generic.js +471 -0
- package/dist/parser/extractors/python.d.ts +8 -0
- package/dist/parser/extractors/python.js +133 -0
- package/dist/parser/extractors/ruby.d.ts +8 -0
- package/dist/parser/extractors/ruby.js +153 -0
- package/dist/parser/extractors/typescript.d.ts +10 -0
- package/dist/parser/extractors/typescript.js +365 -0
- package/dist/parser/languages.d.ts +32 -0
- package/dist/parser/languages.js +304 -0
- package/dist/resolver/call-resolver.d.ts +36 -0
- package/dist/resolver/call-resolver.js +178 -0
- package/dist/resolver/external-detector.d.ts +11 -0
- package/dist/resolver/external-detector.js +820 -0
- package/dist/resolver/fs-cache.d.ts +8 -0
- package/dist/resolver/fs-cache.js +36 -0
- package/dist/resolver/import-map.d.ts +12 -0
- package/dist/resolver/import-map.js +21 -0
- package/dist/resolver/import-resolver.d.ts +19 -0
- package/dist/resolver/import-resolver.js +310 -0
- package/dist/resolver/languages/csharp.d.ts +3 -0
- package/dist/resolver/languages/csharp.js +94 -0
- package/dist/resolver/languages/go.d.ts +3 -0
- package/dist/resolver/languages/go.js +197 -0
- package/dist/resolver/languages/java.d.ts +1 -0
- package/dist/resolver/languages/java.js +193 -0
- package/dist/resolver/languages/php.d.ts +3 -0
- package/dist/resolver/languages/php.js +75 -0
- package/dist/resolver/languages/python.d.ts +11 -0
- package/dist/resolver/languages/python.js +127 -0
- package/dist/resolver/languages/ruby.d.ts +24 -0
- package/dist/resolver/languages/ruby.js +110 -0
- package/dist/resolver/languages/rust.d.ts +1 -0
- package/dist/resolver/languages/rust.js +197 -0
- package/dist/resolver/languages/typescript.d.ts +35 -0
- package/dist/resolver/languages/typescript.js +416 -0
- package/dist/resolver/re-export-resolver.d.ts +24 -0
- package/dist/resolver/re-export-resolver.js +57 -0
- package/dist/resolver/symbol-table.d.ts +17 -0
- package/dist/resolver/symbol-table.js +60 -0
- package/dist/shared/extract-calls.d.ts +26 -0
- package/dist/shared/extract-calls.js +57 -0
- package/dist/shared/file-hash.d.ts +3 -0
- package/dist/shared/file-hash.js +10 -0
- package/dist/shared/filters.d.ts +3 -0
- package/dist/shared/filters.js +240 -0
- package/dist/shared/logger.d.ts +6 -0
- package/dist/shared/logger.js +17 -0
- package/dist/shared/qualified-name.d.ts +1 -0
- package/dist/shared/qualified-name.js +9 -0
- package/dist/shared/safe-path.d.ts +6 -0
- package/dist/shared/safe-path.js +29 -0
- package/dist/shared/schemas.d.ts +43 -0
- package/dist/shared/schemas.js +30 -0
- package/dist/shared/temp.d.ts +11 -0
- package/{src/shared/temp.ts → dist/shared/temp.js} +4 -5
- package/package.json +20 -6
- package/src/analysis/blast-radius.ts +0 -54
- package/src/analysis/communities.ts +0 -135
- package/src/analysis/context-builder.ts +0 -130
- package/src/analysis/diff.ts +0 -169
- package/src/analysis/enrich.ts +0 -110
- package/src/analysis/flows.ts +0 -112
- package/src/analysis/inheritance.ts +0 -34
- package/src/analysis/prompt-formatter.ts +0 -175
- package/src/analysis/risk-score.ts +0 -62
- package/src/analysis/search.ts +0 -76
- package/src/analysis/test-gaps.ts +0 -21
- package/src/cli.ts +0 -210
- package/src/commands/analyze.ts +0 -128
- package/src/commands/communities.ts +0 -19
- package/src/commands/context.ts +0 -182
- package/src/commands/diff.ts +0 -96
- package/src/commands/flows.ts +0 -19
- package/src/commands/parse.ts +0 -124
- package/src/commands/search.ts +0 -41
- package/src/commands/update.ts +0 -166
- package/src/graph/builder.ts +0 -209
- package/src/graph/edges.ts +0 -101
- package/src/graph/json-writer.ts +0 -43
- package/src/graph/loader.ts +0 -113
- package/src/graph/merger.ts +0 -25
- package/src/graph/types.ts +0 -283
- package/src/parser/batch.ts +0 -82
- package/src/parser/discovery.ts +0 -75
- package/src/parser/extractor.ts +0 -37
- package/src/parser/extractors/generic.ts +0 -132
- package/src/parser/extractors/python.ts +0 -133
- package/src/parser/extractors/ruby.ts +0 -147
- package/src/parser/extractors/typescript.ts +0 -350
- package/src/parser/languages.ts +0 -122
- package/src/resolver/call-resolver.ts +0 -244
- package/src/resolver/import-map.ts +0 -27
- package/src/resolver/import-resolver.ts +0 -72
- package/src/resolver/languages/csharp.ts +0 -7
- package/src/resolver/languages/go.ts +0 -7
- package/src/resolver/languages/java.ts +0 -7
- package/src/resolver/languages/php.ts +0 -7
- package/src/resolver/languages/python.ts +0 -35
- package/src/resolver/languages/ruby.ts +0 -21
- package/src/resolver/languages/rust.ts +0 -7
- package/src/resolver/languages/typescript.ts +0 -168
- package/src/resolver/re-export-resolver.ts +0 -66
- package/src/resolver/symbol-table.ts +0 -67
- package/src/shared/extract-calls.ts +0 -75
- package/src/shared/file-hash.ts +0 -12
- package/src/shared/filters.ts +0 -243
- package/src/shared/logger.ts +0 -17
- package/src/shared/qualified-name.ts +0 -5
- package/src/shared/safe-path.ts +0 -31
- package/src/shared/schemas.ts +0 -32
package/src/parser/languages.ts
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import csharp from '@ast-grep/lang-csharp';
|
|
2
|
-
import go from '@ast-grep/lang-go';
|
|
3
|
-
import java from '@ast-grep/lang-java';
|
|
4
|
-
import php from '@ast-grep/lang-php';
|
|
5
|
-
import python from '@ast-grep/lang-python';
|
|
6
|
-
import ruby from '@ast-grep/lang-ruby';
|
|
7
|
-
import rust from '@ast-grep/lang-rust';
|
|
8
|
-
import { Lang, registerDynamicLanguage } from '@ast-grep/napi';
|
|
9
|
-
|
|
10
|
-
// Register dynamic languages at import time (side effect).
|
|
11
|
-
// This must happen before parseAsync can parse these languages.
|
|
12
|
-
registerDynamicLanguage({ python, ruby, go, java, rust, php, csharp });
|
|
13
|
-
|
|
14
|
-
// Extension -> language identifier
|
|
15
|
-
// Built-in langs use Lang enum, dynamic langs use lowercase string
|
|
16
|
-
const EXT_TO_LANG: Record<string, Lang | string> = {
|
|
17
|
-
'.ts': Lang.TypeScript,
|
|
18
|
-
'.tsx': Lang.Tsx,
|
|
19
|
-
'.js': Lang.JavaScript,
|
|
20
|
-
'.jsx': Lang.JavaScript,
|
|
21
|
-
'.mjs': Lang.JavaScript,
|
|
22
|
-
'.cjs': Lang.JavaScript,
|
|
23
|
-
'.py': 'python',
|
|
24
|
-
'.rb': 'ruby',
|
|
25
|
-
'.go': 'go',
|
|
26
|
-
'.java': 'java',
|
|
27
|
-
'.rs': 'rust',
|
|
28
|
-
'.cs': 'csharp',
|
|
29
|
-
'.php': 'php',
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
export function getLanguage(ext: string): Lang | string | null {
|
|
33
|
-
return EXT_TO_LANG[ext] ?? null;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function getSupportedExtensions(): string[] {
|
|
37
|
-
return Object.keys(EXT_TO_LANG);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function getLanguageName(lang: Lang | string): string {
|
|
41
|
-
if (typeof lang === 'string') return lang;
|
|
42
|
-
if (lang === Lang.TypeScript || lang === Lang.Tsx) return 'typescript';
|
|
43
|
-
if (lang === Lang.JavaScript) return 'javascript';
|
|
44
|
-
return 'unknown';
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export function isTypeScriptLike(lang: Lang | string): boolean {
|
|
48
|
-
return lang === Lang.TypeScript || lang === Lang.Tsx || lang === Lang.JavaScript;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// AST node kinds per language for structural extraction
|
|
52
|
-
export const LANG_KINDS: Record<string, Record<string, string>> = {
|
|
53
|
-
typescript: {
|
|
54
|
-
class: 'class_declaration',
|
|
55
|
-
abstractClass: 'abstract_class_declaration',
|
|
56
|
-
method: 'method_definition',
|
|
57
|
-
function: 'function_declaration',
|
|
58
|
-
arrowContainer: 'variable_declarator',
|
|
59
|
-
arrowFunction: 'arrow_function',
|
|
60
|
-
interface: 'interface_declaration',
|
|
61
|
-
enum: 'enum_declaration',
|
|
62
|
-
import: 'import_statement',
|
|
63
|
-
export: 'export_statement',
|
|
64
|
-
methodSignature: 'method_signature',
|
|
65
|
-
},
|
|
66
|
-
python: {
|
|
67
|
-
class: 'class_definition',
|
|
68
|
-
method: 'function_definition',
|
|
69
|
-
function: 'function_definition',
|
|
70
|
-
import: 'import_from_statement',
|
|
71
|
-
importRegular: 'import_statement',
|
|
72
|
-
decorator: 'decorator',
|
|
73
|
-
},
|
|
74
|
-
ruby: {
|
|
75
|
-
class: 'class',
|
|
76
|
-
method: 'method',
|
|
77
|
-
singletonMethod: 'singleton_method',
|
|
78
|
-
module: 'module',
|
|
79
|
-
call: 'call',
|
|
80
|
-
},
|
|
81
|
-
go: {
|
|
82
|
-
function: 'function_declaration',
|
|
83
|
-
method: 'method_declaration',
|
|
84
|
-
struct: 'type_declaration',
|
|
85
|
-
interface: 'type_declaration',
|
|
86
|
-
import: 'import_declaration',
|
|
87
|
-
},
|
|
88
|
-
java: {
|
|
89
|
-
class: 'class_declaration',
|
|
90
|
-
interface: 'interface_declaration',
|
|
91
|
-
method: 'method_declaration',
|
|
92
|
-
constructor: 'constructor_declaration',
|
|
93
|
-
import: 'import_declaration',
|
|
94
|
-
enum: 'enum_declaration',
|
|
95
|
-
},
|
|
96
|
-
rust: {
|
|
97
|
-
function: 'function_item',
|
|
98
|
-
struct: 'struct_item',
|
|
99
|
-
impl: 'impl_item',
|
|
100
|
-
trait: 'trait_item',
|
|
101
|
-
enum: 'enum_item',
|
|
102
|
-
use: 'use_declaration',
|
|
103
|
-
},
|
|
104
|
-
csharp: {
|
|
105
|
-
class: 'class_declaration',
|
|
106
|
-
interface: 'interface_declaration',
|
|
107
|
-
method: 'method_declaration',
|
|
108
|
-
constructor: 'constructor_declaration',
|
|
109
|
-
using: 'using_directive',
|
|
110
|
-
enum: 'enum_declaration',
|
|
111
|
-
namespace: 'namespace_declaration',
|
|
112
|
-
},
|
|
113
|
-
php: {
|
|
114
|
-
class: 'class_declaration',
|
|
115
|
-
method: 'method_declaration',
|
|
116
|
-
function: 'function_definition',
|
|
117
|
-
namespace: 'namespace_definition',
|
|
118
|
-
use: 'namespace_use_declaration',
|
|
119
|
-
},
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
export { Lang };
|
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Call resolution with 5-tier confidence cascade.
|
|
3
|
-
*
|
|
4
|
-
* Cascade: DI (0.90-0.95) → same-file (0.85) → import-resolved (0.70-0.90)
|
|
5
|
-
* → unique-name (0.50) → ambiguous (0.30)
|
|
6
|
-
*
|
|
7
|
-
* Pure resolution logic — no file I/O, no parsing.
|
|
8
|
-
* Raw call sites are provided by the batch parser.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import type { RawCallEdge, RawCallSite } from '../graph/types';
|
|
12
|
-
import { NOISE } from '../shared/filters';
|
|
13
|
-
import type { ImportMap } from './import-map';
|
|
14
|
-
import type { SymbolTable } from './symbol-table';
|
|
15
|
-
|
|
16
|
-
// ── Types ──
|
|
17
|
-
|
|
18
|
-
interface ResolveResult {
|
|
19
|
-
target: string;
|
|
20
|
-
confidence: number;
|
|
21
|
-
strategy: 'di' | 'same' | 'import' | 'unique' | 'ambiguous';
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
interface CallResolverStats {
|
|
25
|
-
di: number;
|
|
26
|
-
same: number;
|
|
27
|
-
import: number;
|
|
28
|
-
unique: number;
|
|
29
|
-
ambiguous: number;
|
|
30
|
-
noise: number;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
interface ResolveAllResult {
|
|
34
|
-
callEdges: RawCallEdge[];
|
|
35
|
-
stats: CallResolverStats;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// ── Batch resolution (pure, no I/O) ──
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Resolve all raw call sites via the 5-tier cascade.
|
|
42
|
-
*
|
|
43
|
-
* Accepts pre-extracted RawCallSite[] from the batch parser.
|
|
44
|
-
* No file reads, no parseAsync — pure iteration + lookup.
|
|
45
|
-
*/
|
|
46
|
-
export function resolveAllCalls(
|
|
47
|
-
rawCalls: RawCallSite[],
|
|
48
|
-
diMaps: Map<string, Map<string, string>>,
|
|
49
|
-
symbolTable: SymbolTable,
|
|
50
|
-
importMap: ImportMap,
|
|
51
|
-
): ResolveAllResult {
|
|
52
|
-
const callEdges: RawCallEdge[] = [];
|
|
53
|
-
const stats: CallResolverStats = { di: 0, same: 0, import: 0, unique: 0, ambiguous: 0, noise: 0 };
|
|
54
|
-
|
|
55
|
-
for (const call of rawCalls) {
|
|
56
|
-
if (NOISE.has(call.callName)) {
|
|
57
|
-
stats.noise++;
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const fp = call.source;
|
|
62
|
-
const diMap = diMaps.get(fp);
|
|
63
|
-
|
|
64
|
-
// Try DI resolution first if diField is present
|
|
65
|
-
if (call.diField) {
|
|
66
|
-
const resolved = resolveDICall(call.diField, call.callName, fp, diMap, symbolTable);
|
|
67
|
-
if (resolved) {
|
|
68
|
-
callEdges.push({
|
|
69
|
-
source: fp,
|
|
70
|
-
target: resolved.target,
|
|
71
|
-
callName: call.callName,
|
|
72
|
-
line: call.line,
|
|
73
|
-
confidence: resolved.confidence,
|
|
74
|
-
});
|
|
75
|
-
stats.di++;
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Class-aware resolution for self.X() and super().X()
|
|
81
|
-
if (call.resolveInClass) {
|
|
82
|
-
const classResolved = resolveInClass(call.callName, fp, call.resolveInClass, symbolTable);
|
|
83
|
-
if (classResolved) {
|
|
84
|
-
callEdges.push({
|
|
85
|
-
source: fp,
|
|
86
|
-
target: classResolved.target,
|
|
87
|
-
callName: call.callName,
|
|
88
|
-
line: call.line,
|
|
89
|
-
confidence: classResolved.confidence,
|
|
90
|
-
});
|
|
91
|
-
stats[classResolved.strategy]++;
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Name-based cascade fallback
|
|
97
|
-
const resolved = resolveByName(call.callName, fp, symbolTable, importMap);
|
|
98
|
-
if (resolved) {
|
|
99
|
-
callEdges.push({
|
|
100
|
-
source: fp,
|
|
101
|
-
target: resolved.target,
|
|
102
|
-
callName: call.callName,
|
|
103
|
-
line: call.line,
|
|
104
|
-
confidence: resolved.confidence,
|
|
105
|
-
});
|
|
106
|
-
stats[resolved.strategy]++;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return { callEdges, stats };
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// ── Class-aware resolution (self./super.) ──
|
|
114
|
-
|
|
115
|
-
function resolveInClass(
|
|
116
|
-
callName: string,
|
|
117
|
-
currentFile: string,
|
|
118
|
-
className: string,
|
|
119
|
-
symbolTable: SymbolTable,
|
|
120
|
-
): ResolveResult | null {
|
|
121
|
-
// Try same-file class method first (self.method() or super().method())
|
|
122
|
-
const inFile = symbolTable.lookupInFile(currentFile, callName, className);
|
|
123
|
-
if (inFile) return { target: inFile, confidence: 0.9, strategy: 'same' };
|
|
124
|
-
|
|
125
|
-
// Class might be in another file (imported parent class for super())
|
|
126
|
-
const candidates = symbolTable.lookupGlobal(callName);
|
|
127
|
-
const match = candidates.find((q) => q.includes(`::${className}.${callName}`));
|
|
128
|
-
if (match) return { target: match, confidence: 0.85, strategy: 'import' };
|
|
129
|
-
|
|
130
|
-
return null;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// ── DI resolution ──
|
|
134
|
-
|
|
135
|
-
function resolveDICall(
|
|
136
|
-
fieldName: string,
|
|
137
|
-
methodName: string,
|
|
138
|
-
_currentFile: string,
|
|
139
|
-
diMap: Map<string, string> | undefined,
|
|
140
|
-
symbolTable: SymbolTable,
|
|
141
|
-
): ResolveResult | null {
|
|
142
|
-
if (!diMap?.has(fieldName)) return null;
|
|
143
|
-
|
|
144
|
-
const typeName = diMap.get(fieldName)!;
|
|
145
|
-
|
|
146
|
-
// Direct class match
|
|
147
|
-
const candidates = symbolTable.lookupGlobal(typeName);
|
|
148
|
-
if (candidates.length >= 1) {
|
|
149
|
-
const typeFile = candidates[0].split('::')[0];
|
|
150
|
-
return { target: `${typeFile}::${typeName}.${methodName}`, confidence: 0.95, strategy: 'di' };
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// ISomething → Something heuristic for interface → implementation
|
|
154
|
-
if (typeName.startsWith('I') && typeName[1] === typeName[1]?.toUpperCase()) {
|
|
155
|
-
const implName = typeName.substring(1);
|
|
156
|
-
const implCandidates = symbolTable.lookupGlobal(implName);
|
|
157
|
-
if (implCandidates.length >= 1) {
|
|
158
|
-
const implFile = implCandidates[0].split('::')[0];
|
|
159
|
-
return { target: `${implFile}::${implName}.${methodName}`, confidence: 0.9, strategy: 'di' };
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return null;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// ── Name-based resolution (4-tier cascade) ──
|
|
167
|
-
|
|
168
|
-
function resolveByName(
|
|
169
|
-
callName: string,
|
|
170
|
-
currentFile: string,
|
|
171
|
-
symbolTable: SymbolTable,
|
|
172
|
-
importMap: ImportMap,
|
|
173
|
-
): ResolveResult | null {
|
|
174
|
-
// Strategy 1: Same file (0.85)
|
|
175
|
-
const sameFile = symbolTable.lookupExact(currentFile, callName);
|
|
176
|
-
if (sameFile) return { target: sameFile, confidence: 0.85, strategy: 'same' };
|
|
177
|
-
|
|
178
|
-
// Strategy 2: Import-resolved (0.70-0.90)
|
|
179
|
-
const importedFrom = importMap.lookup(currentFile, callName);
|
|
180
|
-
if (importedFrom) {
|
|
181
|
-
const targetSym = symbolTable.lookupExact(importedFrom, callName);
|
|
182
|
-
if (targetSym) return { target: targetSym, confidence: 0.9, strategy: 'import' };
|
|
183
|
-
return { target: `${importedFrom}::${callName}`, confidence: 0.7, strategy: 'import' };
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Strategy 3: Unique global name (0.50)
|
|
187
|
-
if (symbolTable.isUnique(callName)) {
|
|
188
|
-
const candidates = symbolTable.lookupGlobal(callName);
|
|
189
|
-
return { target: candidates[0], confidence: 0.5, strategy: 'unique' };
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Strategy 4: Ambiguous (0.30) — pick closest candidate by directory proximity
|
|
193
|
-
const candidates = symbolTable.lookupGlobal(callName);
|
|
194
|
-
if (candidates.length > 1) {
|
|
195
|
-
const best = pickClosestCandidate(candidates, currentFile);
|
|
196
|
-
return { target: best, confidence: 0.3, strategy: 'ambiguous' };
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
return null;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// ── Proximity-based candidate selection ──
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Pick the candidate whose file path is closest to the caller's file.
|
|
206
|
-
* Counts shared leading path segments — more shared = closer.
|
|
207
|
-
*/
|
|
208
|
-
function pickClosestCandidate(candidates: string[], callerFile: string): string {
|
|
209
|
-
const callerParts = callerFile.split('/');
|
|
210
|
-
let best = candidates[0];
|
|
211
|
-
let bestScore = -1;
|
|
212
|
-
|
|
213
|
-
for (const candidate of candidates) {
|
|
214
|
-
const candidateFile = candidate.includes('::') ? candidate.split('::')[0] : candidate;
|
|
215
|
-
const parts = candidateFile.split('/');
|
|
216
|
-
let shared = 0;
|
|
217
|
-
for (let i = 0; i < Math.min(callerParts.length, parts.length); i++) {
|
|
218
|
-
if (callerParts[i] === parts[i]) shared++;
|
|
219
|
-
else break;
|
|
220
|
-
}
|
|
221
|
-
if (shared > bestScore) {
|
|
222
|
-
bestScore = shared;
|
|
223
|
-
best = candidate;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
return best;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// ── Public wrapper for unit testing ──
|
|
231
|
-
|
|
232
|
-
export function resolveCall(
|
|
233
|
-
callName: string,
|
|
234
|
-
currentFile: string,
|
|
235
|
-
symbolTable: SymbolTable,
|
|
236
|
-
importMap: ImportMap,
|
|
237
|
-
): { target: string; confidence: number } | null {
|
|
238
|
-
if (NOISE.has(callName)) return null;
|
|
239
|
-
|
|
240
|
-
const result = resolveByName(callName, currentFile, symbolTable, importMap);
|
|
241
|
-
if (!result) return null;
|
|
242
|
-
|
|
243
|
-
return { target: result.target, confidence: result.confidence };
|
|
244
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Import map: tracks which symbols are imported from where per file.
|
|
3
|
-
*
|
|
4
|
-
* For each importing file, maps symbol names to the resolved file path
|
|
5
|
-
* they were imported from. Used by the call resolver to connect
|
|
6
|
-
* function calls to their definitions across files.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export interface ImportMap {
|
|
10
|
-
add(file: string, name: string, targetFile: string): void;
|
|
11
|
-
lookup(file: string, name: string): string | null;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function createImportMap(): ImportMap {
|
|
15
|
-
const map = new Map<string, Map<string, string>>();
|
|
16
|
-
|
|
17
|
-
return {
|
|
18
|
-
add(file, name, targetFile) {
|
|
19
|
-
if (!map.has(file)) map.set(file, new Map());
|
|
20
|
-
map.get(file)!.set(name, targetFile);
|
|
21
|
-
},
|
|
22
|
-
|
|
23
|
-
lookup(file, name) {
|
|
24
|
-
return map.get(file)?.get(name) ?? null;
|
|
25
|
-
},
|
|
26
|
-
};
|
|
27
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Import resolver dispatcher.
|
|
3
|
-
*
|
|
4
|
-
* Routes import resolution to language-specific resolvers and
|
|
5
|
-
* falls back to tsconfig aliases for TypeScript/JavaScript.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { log } from '../shared/logger';
|
|
9
|
-
import { ensureWithinRoot } from '../shared/safe-path';
|
|
10
|
-
import { resolve as resolveCsImport } from './languages/csharp';
|
|
11
|
-
import { resolve as resolveGoImport } from './languages/go';
|
|
12
|
-
import { resolve as resolveJavaImport } from './languages/java';
|
|
13
|
-
import { resolve as resolvePhpImport } from './languages/php';
|
|
14
|
-
import { resolve as resolvePyImport } from './languages/python';
|
|
15
|
-
import { resolve as resolveRbImport } from './languages/ruby';
|
|
16
|
-
import { resolve as resolveRustImport } from './languages/rust';
|
|
17
|
-
import { loadTsconfigAliases, resolve as resolveTsImport, resolveWithAliases } from './languages/typescript';
|
|
18
|
-
|
|
19
|
-
const RESOLVERS: Record<string, (from: string, mod: string, root: string) => string | null> = {
|
|
20
|
-
ts: resolveTsImport,
|
|
21
|
-
javascript: resolveTsImport,
|
|
22
|
-
typescript: resolveTsImport,
|
|
23
|
-
python: resolvePyImport,
|
|
24
|
-
ruby: resolveRbImport,
|
|
25
|
-
go: resolveGoImport,
|
|
26
|
-
java: resolveJavaImport,
|
|
27
|
-
rust: resolveRustImport,
|
|
28
|
-
csharp: resolveCsImport,
|
|
29
|
-
php: resolvePhpImport,
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Resolve an import from one file to another.
|
|
34
|
-
*
|
|
35
|
-
* @param fromAbsFile - Absolute path of the importing file
|
|
36
|
-
* @param modulePath - The import specifier (e.g., './auth', 'express', '@/lib/db')
|
|
37
|
-
* @param lang - Language key (ts, javascript, typescript, python, ruby, etc.)
|
|
38
|
-
* @param repoRoot - Absolute path to the repository root
|
|
39
|
-
* @param tsconfigAliases - Optional pre-loaded tsconfig aliases for TS/JS
|
|
40
|
-
* @returns Absolute path to the resolved file, or null if unresolvable
|
|
41
|
-
*/
|
|
42
|
-
export function resolveImport(
|
|
43
|
-
fromAbsFile: string,
|
|
44
|
-
modulePath: string,
|
|
45
|
-
lang: string,
|
|
46
|
-
repoRoot: string,
|
|
47
|
-
tsconfigAliases?: Map<string, string[]>,
|
|
48
|
-
): string | null {
|
|
49
|
-
const resolver = RESOLVERS[lang];
|
|
50
|
-
if (!resolver) return null;
|
|
51
|
-
|
|
52
|
-
let result = resolver(fromAbsFile, modulePath, repoRoot);
|
|
53
|
-
|
|
54
|
-
// Fallback: tsconfig aliases for TS/JS
|
|
55
|
-
if (!result && (lang === 'ts' || lang === 'javascript' || lang === 'typescript') && tsconfigAliases?.size) {
|
|
56
|
-
result = resolveWithAliases(modulePath, tsconfigAliases, repoRoot);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Validate resolved path is within repo root
|
|
60
|
-
if (result) {
|
|
61
|
-
try {
|
|
62
|
-
ensureWithinRoot(result, repoRoot);
|
|
63
|
-
} catch {
|
|
64
|
-
log.warn('Import resolves outside repository root', { from: fromAbsFile, module: modulePath, resolved: result });
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return result;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export { loadTsconfigAliases };
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Python import resolver.
|
|
3
|
-
*
|
|
4
|
-
* Handles dotted module paths (e.g., "from x.y import z").
|
|
5
|
-
* Walks up directories to find packages.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { existsSync } from 'fs';
|
|
9
|
-
import { dirname, join, resolve as resolvePath } from 'path';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Resolve a Python dotted import to a file path.
|
|
13
|
-
* Walks up from the importing file's directory to find the module.
|
|
14
|
-
*/
|
|
15
|
-
export function resolve(fromAbsFile: string, modulePath: string, _repoRoot: string): string | null {
|
|
16
|
-
if (!modulePath || modulePath.startsWith('.')) {
|
|
17
|
-
// Relative import -- not handled yet
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const parts = modulePath.replace(/\./g, '/');
|
|
22
|
-
let current = dirname(fromAbsFile);
|
|
23
|
-
|
|
24
|
-
for (let i = 0; i < 10; i++) {
|
|
25
|
-
for (const candidate of [`${parts}.py`, `${parts}/__init__.py`]) {
|
|
26
|
-
const full = join(current, candidate);
|
|
27
|
-
if (existsSync(full)) return resolvePath(full);
|
|
28
|
-
}
|
|
29
|
-
const parent = dirname(current);
|
|
30
|
-
if (parent === current) break;
|
|
31
|
-
current = parent;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ruby import resolver.
|
|
3
|
-
*
|
|
4
|
-
* Handles require_relative paths.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { existsSync } from 'fs';
|
|
8
|
-
import { dirname, join, resolve as resolvePath } from 'path';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Resolve a Ruby require/require_relative to a file path.
|
|
12
|
-
*/
|
|
13
|
-
export function resolve(fromAbsFile: string, modulePath: string, _repoRoot: string): string | null {
|
|
14
|
-
if (!modulePath) return null;
|
|
15
|
-
|
|
16
|
-
const base = join(dirname(fromAbsFile), modulePath);
|
|
17
|
-
if (existsSync(`${base}.rb`)) return resolvePath(`${base}.rb`);
|
|
18
|
-
if (existsSync(base)) return resolvePath(base);
|
|
19
|
-
|
|
20
|
-
return null;
|
|
21
|
-
}
|