@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
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TypeScript/JavaScript import resolver.
|
|
3
|
-
*
|
|
4
|
-
* Handles:
|
|
5
|
-
* - Relative imports with extension probing (.ts, .tsx, .js, .jsx)
|
|
6
|
-
* - ESM .js → .ts remapping
|
|
7
|
-
* - Directory index files
|
|
8
|
-
* - tsconfig path aliases
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { existsSync, readFileSync } from 'fs';
|
|
12
|
-
import { dirname, join, resolve as resolvePath } from 'path';
|
|
13
|
-
import { log } from '../../shared/logger';
|
|
14
|
-
|
|
15
|
-
const TS_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx'];
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Resolve a TypeScript/JavaScript relative import to an absolute file path.
|
|
19
|
-
* Returns null for non-relative (external package) imports.
|
|
20
|
-
*/
|
|
21
|
-
export function resolve(fromAbsFile: string, modulePath: string, _repoRoot: string): string | null {
|
|
22
|
-
if (!modulePath.startsWith('.')) return null;
|
|
23
|
-
|
|
24
|
-
let base = join(dirname(fromAbsFile), modulePath);
|
|
25
|
-
|
|
26
|
-
// ESM convention: .js in import -> .ts on disk
|
|
27
|
-
if (modulePath.endsWith('.js')) base = base.slice(0, -3);
|
|
28
|
-
|
|
29
|
-
// Try direct with extension
|
|
30
|
-
for (const ext of TS_EXTENSIONS) {
|
|
31
|
-
const candidate = base + ext;
|
|
32
|
-
if (existsSync(candidate)) return resolvePath(candidate);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Try index file in directory
|
|
36
|
-
for (const ext of TS_EXTENSIONS) {
|
|
37
|
-
const candidate = join(base, `index${ext}`);
|
|
38
|
-
if (existsSync(candidate)) return resolvePath(candidate);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Strip comments and trailing commas from JSON (tsconfig-compatible).
|
|
46
|
-
* Handles strings correctly -- won't strip // inside "url://..." etc.
|
|
47
|
-
*/
|
|
48
|
-
function stripJsonComments(str: string): string {
|
|
49
|
-
let result = '';
|
|
50
|
-
let i = 0;
|
|
51
|
-
const len = str.length;
|
|
52
|
-
|
|
53
|
-
while (i < len) {
|
|
54
|
-
// String literal -- copy as-is
|
|
55
|
-
if (str[i] === '"') {
|
|
56
|
-
let j = i + 1;
|
|
57
|
-
while (j < len && str[j] !== '"') {
|
|
58
|
-
if (str[j] === '\\') j++; // skip escaped char
|
|
59
|
-
j++;
|
|
60
|
-
}
|
|
61
|
-
result += str.substring(i, j + 1);
|
|
62
|
-
i = j + 1;
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Single-line comment
|
|
67
|
-
if (str[i] === '/' && str[i + 1] === '/') {
|
|
68
|
-
while (i < len && str[i] !== '\n') i++;
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Block comment
|
|
73
|
-
if (str[i] === '/' && str[i + 1] === '*') {
|
|
74
|
-
i += 2;
|
|
75
|
-
while (i < len && !(str[i] === '*' && str[i + 1] === '/')) i++;
|
|
76
|
-
i += 2;
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Trailing comma: comma followed by optional whitespace + closing bracket
|
|
81
|
-
if (str[i] === ',') {
|
|
82
|
-
let j = i + 1;
|
|
83
|
-
while (j < len && (str[j] === ' ' || str[j] === '\t' || str[j] === '\n' || str[j] === '\r')) j++;
|
|
84
|
-
if (str[j] === '}' || str[j] === ']') {
|
|
85
|
-
i++;
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
result += str[i];
|
|
91
|
-
i++;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return result;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Load and parse tsconfig.json path aliases.
|
|
99
|
-
*
|
|
100
|
-
* Tries tsconfig.json first, then tsconfig.base.json.
|
|
101
|
-
* Converts alias patterns like "@libs/*" into prefix → resolved dirs.
|
|
102
|
-
*/
|
|
103
|
-
export function loadTsconfigAliases(repoRoot: string): Map<string, string[]> {
|
|
104
|
-
const aliases = new Map<string, string[]>();
|
|
105
|
-
|
|
106
|
-
for (const filename of ['tsconfig.json', 'tsconfig.base.json']) {
|
|
107
|
-
const tsconfigPath = join(repoRoot, filename);
|
|
108
|
-
if (!existsSync(tsconfigPath)) continue;
|
|
109
|
-
|
|
110
|
-
try {
|
|
111
|
-
const content = readFileSync(tsconfigPath, 'utf-8');
|
|
112
|
-
const cleaned = stripJsonComments(content);
|
|
113
|
-
const config = JSON.parse(cleaned);
|
|
114
|
-
const paths = config?.compilerOptions?.paths;
|
|
115
|
-
const baseUrl = config?.compilerOptions?.baseUrl || '.';
|
|
116
|
-
const baseDir = join(repoRoot, baseUrl);
|
|
117
|
-
|
|
118
|
-
if (paths) {
|
|
119
|
-
for (const [alias, targets] of Object.entries(paths)) {
|
|
120
|
-
// Convert alias pattern: "@libs/*" -> prefix "@libs/"
|
|
121
|
-
const prefix = alias.replace('/*', '/').replace('*', '');
|
|
122
|
-
const resolvedTargets = (targets as string[]).map((t) => {
|
|
123
|
-
const targetPath = t.replace('/*', '').replace('*', '');
|
|
124
|
-
return join(baseDir, targetPath);
|
|
125
|
-
});
|
|
126
|
-
aliases.set(prefix, resolvedTargets);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
} catch (err) {
|
|
130
|
-
log.warn('Failed to parse tsconfig', { file: tsconfigPath, error: String(err) });
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return aliases;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Resolve an import path using tsconfig aliases.
|
|
139
|
-
*
|
|
140
|
-
* Tries each alias prefix, and for matches, probes extensions and index files.
|
|
141
|
-
*/
|
|
142
|
-
export function resolveWithAliases(
|
|
143
|
-
modulePath: string,
|
|
144
|
-
aliases: Map<string, string[]>,
|
|
145
|
-
_repoRoot: string,
|
|
146
|
-
): string | null {
|
|
147
|
-
for (const [prefix, targets] of aliases) {
|
|
148
|
-
if (modulePath.startsWith(prefix)) {
|
|
149
|
-
const rest = modulePath.slice(prefix.length);
|
|
150
|
-
|
|
151
|
-
for (const targetBase of targets) {
|
|
152
|
-
const base = join(targetBase, rest);
|
|
153
|
-
|
|
154
|
-
for (const ext of TS_EXTENSIONS) {
|
|
155
|
-
if (existsSync(base + ext)) return resolvePath(base + ext);
|
|
156
|
-
}
|
|
157
|
-
for (const ext of TS_EXTENSIONS) {
|
|
158
|
-
const idx = join(base, `index${ext}`);
|
|
159
|
-
if (existsSync(idx)) return resolvePath(idx);
|
|
160
|
-
}
|
|
161
|
-
// Try exact match (for directories with index)
|
|
162
|
-
if (existsSync(base)) return resolvePath(base);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return null;
|
|
168
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Re-export (barrel) resolver.
|
|
3
|
-
*
|
|
4
|
-
* Follows `export { X } from './module'` chains so that the import map
|
|
5
|
-
* points to the file where the symbol is actually defined, not the
|
|
6
|
-
* barrel index file.
|
|
7
|
-
*
|
|
8
|
-
* Without this, an import like `import { Foo } from '@lib'` resolves
|
|
9
|
-
* to `@lib/index.ts`, but `Foo` is defined in `@lib/foo.ts`.
|
|
10
|
-
* The call resolver can't find `Foo` in the barrel's symbol table and
|
|
11
|
-
* falls to lower-confidence tiers.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { relative, resolve } from 'path';
|
|
15
|
-
import { resolveImport } from './import-resolver';
|
|
16
|
-
|
|
17
|
-
interface RawReExport {
|
|
18
|
-
module: string;
|
|
19
|
-
file: string;
|
|
20
|
-
line: number;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Build a map: barrel file (relative) → list of resolved re-export target files (relative).
|
|
25
|
-
*
|
|
26
|
-
* Follows one level of re-exports (covers >95% of real-world barrel patterns).
|
|
27
|
-
*/
|
|
28
|
-
export function buildReExportMap(
|
|
29
|
-
reExports: RawReExport[],
|
|
30
|
-
repoDir: string,
|
|
31
|
-
tsconfigAliases?: Map<string, string[]>,
|
|
32
|
-
): Map<string, string[]> {
|
|
33
|
-
const barrelMap = new Map<string, string[]>();
|
|
34
|
-
|
|
35
|
-
for (const re of reExports) {
|
|
36
|
-
const absFrom = resolve(repoDir, re.file);
|
|
37
|
-
const resolved = resolveImport(absFrom, re.module, 'typescript', repoDir, tsconfigAliases);
|
|
38
|
-
if (!resolved) continue;
|
|
39
|
-
|
|
40
|
-
const resolvedRel = relative(repoDir, resolved);
|
|
41
|
-
const list = barrelMap.get(re.file);
|
|
42
|
-
if (list) {
|
|
43
|
-
if (!list.includes(resolvedRel)) list.push(resolvedRel);
|
|
44
|
-
} else {
|
|
45
|
-
barrelMap.set(re.file, [resolvedRel]);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Follow one extra level: if a re-export target is itself a barrel, flatten
|
|
50
|
-
for (const [barrel, targets] of barrelMap) {
|
|
51
|
-
const extra: string[] = [];
|
|
52
|
-
for (const target of targets) {
|
|
53
|
-
const nested = barrelMap.get(target);
|
|
54
|
-
if (nested) {
|
|
55
|
-
for (const n of nested) {
|
|
56
|
-
if (n !== barrel && !targets.includes(n) && !extra.includes(n)) {
|
|
57
|
-
extra.push(n);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
if (extra.length > 0) targets.push(...extra);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return barrelMap;
|
|
66
|
-
}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Symbol table with dual-index lookup (GitNexus pattern).
|
|
3
|
-
*
|
|
4
|
-
* Provides both exact (file + name) and global (name-only) lookups.
|
|
5
|
-
* The exact lookup is high-confidence; global lookup is lower-confidence
|
|
6
|
-
* but useful when import resolution fails.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export interface SymbolTable {
|
|
10
|
-
add(file: string, name: string, qualified: string): void;
|
|
11
|
-
lookupExact(file: string, name: string): string | null;
|
|
12
|
-
lookupInFile(file: string, name: string, className: string): string | null;
|
|
13
|
-
isUnique(name: string): boolean;
|
|
14
|
-
lookupGlobal(name: string): string[];
|
|
15
|
-
readonly size: number;
|
|
16
|
-
readonly fileCount: number;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function createSymbolTable(): SymbolTable {
|
|
20
|
-
const byFile = new Map<string, Map<string, string[]>>();
|
|
21
|
-
const byName = new Map<string, string[]>();
|
|
22
|
-
|
|
23
|
-
return {
|
|
24
|
-
add(file, name, qualified) {
|
|
25
|
-
if (!byFile.has(file)) byFile.set(file, new Map());
|
|
26
|
-
const fileMap = byFile.get(file)!;
|
|
27
|
-
if (!fileMap.has(name)) fileMap.set(name, []);
|
|
28
|
-
fileMap.get(name)!.push(qualified);
|
|
29
|
-
|
|
30
|
-
if (!byName.has(name)) byName.set(name, []);
|
|
31
|
-
byName.get(name)!.push(qualified);
|
|
32
|
-
},
|
|
33
|
-
|
|
34
|
-
lookupExact(file, name) {
|
|
35
|
-
const candidates = byFile.get(file)?.get(name);
|
|
36
|
-
if (!candidates || candidates.length === 0) return null;
|
|
37
|
-
// Only return if unambiguous within this file
|
|
38
|
-
return candidates.length === 1 ? candidates[0] : null;
|
|
39
|
-
},
|
|
40
|
-
|
|
41
|
-
lookupInFile(file, name, className) {
|
|
42
|
-
const candidates = byFile.get(file)?.get(name);
|
|
43
|
-
if (!candidates || candidates.length === 0) return null;
|
|
44
|
-
return candidates.find((q) => q.includes(`::${className}.${name}`)) ?? null;
|
|
45
|
-
},
|
|
46
|
-
|
|
47
|
-
isUnique(name) {
|
|
48
|
-
return (byName.get(name)?.length ?? 0) === 1;
|
|
49
|
-
},
|
|
50
|
-
|
|
51
|
-
lookupGlobal(name) {
|
|
52
|
-
return byName.get(name) ?? [];
|
|
53
|
-
},
|
|
54
|
-
|
|
55
|
-
get size() {
|
|
56
|
-
let count = 0;
|
|
57
|
-
for (const m of byFile.values()) {
|
|
58
|
-
for (const arr of m.values()) count += arr.length;
|
|
59
|
-
}
|
|
60
|
-
return count;
|
|
61
|
-
},
|
|
62
|
-
|
|
63
|
-
get fileCount() {
|
|
64
|
-
return byFile.size;
|
|
65
|
-
},
|
|
66
|
-
};
|
|
67
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import type { SgNode } from '@ast-grep/napi';
|
|
2
|
-
import type { RawCallSite } from '../graph/types';
|
|
3
|
-
import { NOISE } from './filters';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Language-specific configuration for call extraction.
|
|
7
|
-
* Each language provides its self/super patterns and how to find class context in the AST.
|
|
8
|
-
*/
|
|
9
|
-
export interface CallExtractionConfig {
|
|
10
|
-
/** Prefixes indicating a self-reference (e.g., 'self.', 'this.') */
|
|
11
|
-
selfPrefixes: string[];
|
|
12
|
-
/** Prefixes indicating a super-reference (e.g., 'super().', 'super.', 'base.') */
|
|
13
|
-
superPrefixes: string[];
|
|
14
|
-
/** Find the enclosing class/module/impl node from a call site */
|
|
15
|
-
findEnclosingClass: (node: SgNode) => SgNode | null;
|
|
16
|
-
/** Extract the parent class name from a class node (for super resolution) */
|
|
17
|
-
getParentClass?: (classNode: SgNode) => string | undefined;
|
|
18
|
-
/** Skip this callee entirely (e.g., TS skips this.field.method — handled by DI) */
|
|
19
|
-
skipCallee?: (callee: string) => boolean;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Shared call extraction for all languages.
|
|
24
|
-
*
|
|
25
|
-
* Parses `$CALLEE($$$ARGS)` pattern, detects self/super references
|
|
26
|
-
* based on language config, and populates resolveInClass for
|
|
27
|
-
* class-aware resolution downstream.
|
|
28
|
-
*/
|
|
29
|
-
export function extractCalls(
|
|
30
|
-
rootNode: SgNode,
|
|
31
|
-
fp: string,
|
|
32
|
-
config: CallExtractionConfig,
|
|
33
|
-
calls: RawCallSite[],
|
|
34
|
-
): void {
|
|
35
|
-
for (const m of rootNode.findAll('$CALLEE($$$ARGS)')) {
|
|
36
|
-
const callee = m.getMatch('CALLEE')?.text();
|
|
37
|
-
if (!callee) continue;
|
|
38
|
-
if (config.skipCallee?.(callee)) continue;
|
|
39
|
-
|
|
40
|
-
const callName = callee.includes('.') ? callee.split('.').pop()! : callee;
|
|
41
|
-
if (NOISE.has(callName)) continue;
|
|
42
|
-
|
|
43
|
-
let resolveInClass: string | undefined;
|
|
44
|
-
|
|
45
|
-
// Check self-reference: callee must be exactly `prefix + methodName` (no further chaining)
|
|
46
|
-
for (const prefix of config.selfPrefixes) {
|
|
47
|
-
if (!callee.startsWith(prefix)) continue;
|
|
48
|
-
const rest = callee.substring(prefix.length);
|
|
49
|
-
if (rest.includes('.')) break; // chained access (e.g., this.field.method) — not a self call
|
|
50
|
-
const classNode = config.findEnclosingClass(m);
|
|
51
|
-
resolveInClass = classNode?.field('name')?.text();
|
|
52
|
-
break;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Check super-reference if no self match
|
|
56
|
-
if (!resolveInClass) {
|
|
57
|
-
for (const prefix of config.superPrefixes) {
|
|
58
|
-
const matches = callee === prefix || (callee.startsWith(prefix) && !callee.substring(prefix.length).includes('.'));
|
|
59
|
-
if (!matches) continue;
|
|
60
|
-
const classNode = config.findEnclosingClass(m);
|
|
61
|
-
if (classNode && config.getParentClass) {
|
|
62
|
-
resolveInClass = config.getParentClass(classNode);
|
|
63
|
-
}
|
|
64
|
-
break;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
calls.push({
|
|
69
|
-
source: fp,
|
|
70
|
-
callName,
|
|
71
|
-
line: m.range().start.line,
|
|
72
|
-
...(resolveInClass ? { resolveInClass } : {}),
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
}
|
package/src/shared/file-hash.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { createHash } from 'crypto';
|
|
2
|
-
import { readFileSync } from 'fs';
|
|
3
|
-
|
|
4
|
-
export function computeFileHash(filePath: string): string {
|
|
5
|
-
const content = readFileSync(filePath);
|
|
6
|
-
return createHash('sha256').update(content).digest('hex');
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/** Hash a node's source text (function body, class body, etc.) */
|
|
10
|
-
export function computeContentHash(sourceText: string): string {
|
|
11
|
-
return createHash('sha256').update(sourceText).digest('hex');
|
|
12
|
-
}
|
package/src/shared/filters.ts
DELETED
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
export const SKIP_DIRS = new Set([
|
|
2
|
-
'node_modules',
|
|
3
|
-
'.git',
|
|
4
|
-
'dist',
|
|
5
|
-
'build',
|
|
6
|
-
'.next',
|
|
7
|
-
'coverage',
|
|
8
|
-
'vendor',
|
|
9
|
-
'__pycache__',
|
|
10
|
-
'.venv',
|
|
11
|
-
'venv',
|
|
12
|
-
'target',
|
|
13
|
-
'.turbo',
|
|
14
|
-
'.cache',
|
|
15
|
-
'.output',
|
|
16
|
-
'out',
|
|
17
|
-
'.nuxt',
|
|
18
|
-
'.svelte-kit',
|
|
19
|
-
'.idea',
|
|
20
|
-
'.mypy_cache',
|
|
21
|
-
'.tox',
|
|
22
|
-
'.pytest_cache',
|
|
23
|
-
'.eggs',
|
|
24
|
-
'bower_components',
|
|
25
|
-
]);
|
|
26
|
-
|
|
27
|
-
/** File name patterns to skip during discovery (minified, bundled, vendored) */
|
|
28
|
-
const SKIP_FILE_PATTERNS: RegExp[] = [
|
|
29
|
-
/\.min\.\w+$/, // *.min.js, *.min.css
|
|
30
|
-
/[.-]bundle\.\w+$/, // *.bundle.js, *-bundle.js
|
|
31
|
-
/\.chunk\.\w+$/, // *.chunk.js (webpack)
|
|
32
|
-
/\.packed\.\w+$/, // *.packed.js
|
|
33
|
-
];
|
|
34
|
-
|
|
35
|
-
export function isSkippableFile(fileName: string): boolean {
|
|
36
|
-
return SKIP_FILE_PATTERNS.some((p) => p.test(fileName));
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export const NOISE = new Set([
|
|
40
|
-
// JS/TS builtins
|
|
41
|
-
'log',
|
|
42
|
-
'error',
|
|
43
|
-
'warn',
|
|
44
|
-
'info',
|
|
45
|
-
'debug',
|
|
46
|
-
'trace',
|
|
47
|
-
'push',
|
|
48
|
-
'pop',
|
|
49
|
-
'shift',
|
|
50
|
-
'unshift',
|
|
51
|
-
'splice',
|
|
52
|
-
'slice',
|
|
53
|
-
'map',
|
|
54
|
-
'filter',
|
|
55
|
-
'reduce',
|
|
56
|
-
'forEach',
|
|
57
|
-
'find',
|
|
58
|
-
'findIndex',
|
|
59
|
-
'some',
|
|
60
|
-
'every',
|
|
61
|
-
'flat',
|
|
62
|
-
'flatMap',
|
|
63
|
-
'sort',
|
|
64
|
-
'reverse',
|
|
65
|
-
'join',
|
|
66
|
-
'split',
|
|
67
|
-
'trim',
|
|
68
|
-
'replace',
|
|
69
|
-
'match',
|
|
70
|
-
'test',
|
|
71
|
-
'includes',
|
|
72
|
-
'indexOf',
|
|
73
|
-
'lastIndexOf',
|
|
74
|
-
'startsWith',
|
|
75
|
-
'endsWith',
|
|
76
|
-
'keys',
|
|
77
|
-
'values',
|
|
78
|
-
'entries',
|
|
79
|
-
'assign',
|
|
80
|
-
'freeze',
|
|
81
|
-
'create',
|
|
82
|
-
'stringify',
|
|
83
|
-
'parse',
|
|
84
|
-
'toString',
|
|
85
|
-
'toLowerCase',
|
|
86
|
-
'toUpperCase',
|
|
87
|
-
'concat',
|
|
88
|
-
'charAt',
|
|
89
|
-
'substring',
|
|
90
|
-
'parseInt',
|
|
91
|
-
'parseFloat',
|
|
92
|
-
'isNaN',
|
|
93
|
-
'isFinite',
|
|
94
|
-
'isArray',
|
|
95
|
-
'resolve',
|
|
96
|
-
'reject',
|
|
97
|
-
'all',
|
|
98
|
-
'allSettled',
|
|
99
|
-
'race',
|
|
100
|
-
'any',
|
|
101
|
-
'then',
|
|
102
|
-
'catch',
|
|
103
|
-
'finally',
|
|
104
|
-
'get',
|
|
105
|
-
'set',
|
|
106
|
-
'has',
|
|
107
|
-
'delete',
|
|
108
|
-
'clear',
|
|
109
|
-
'add',
|
|
110
|
-
'next',
|
|
111
|
-
'return',
|
|
112
|
-
'throw',
|
|
113
|
-
'setTimeout',
|
|
114
|
-
'clearTimeout',
|
|
115
|
-
'setInterval',
|
|
116
|
-
'clearInterval',
|
|
117
|
-
'require',
|
|
118
|
-
'length',
|
|
119
|
-
'call',
|
|
120
|
-
'apply',
|
|
121
|
-
'bind',
|
|
122
|
-
'createElement',
|
|
123
|
-
'useState',
|
|
124
|
-
'useEffect',
|
|
125
|
-
'useRef',
|
|
126
|
-
'useCallback',
|
|
127
|
-
'useMemo',
|
|
128
|
-
'useContext',
|
|
129
|
-
'useReducer',
|
|
130
|
-
'render',
|
|
131
|
-
// Test helpers
|
|
132
|
-
'expect',
|
|
133
|
-
'toBe',
|
|
134
|
-
'toEqual',
|
|
135
|
-
'toBeDefined',
|
|
136
|
-
'toBeNull',
|
|
137
|
-
'toBeUndefined',
|
|
138
|
-
'toBeTruthy',
|
|
139
|
-
'toBeFalsy',
|
|
140
|
-
'toContain',
|
|
141
|
-
'toHaveLength',
|
|
142
|
-
'toThrow',
|
|
143
|
-
'toHaveBeenCalled',
|
|
144
|
-
'toHaveBeenCalledWith',
|
|
145
|
-
'toMatchObject',
|
|
146
|
-
'toHaveBeenCalledTimes',
|
|
147
|
-
'toHaveProperty',
|
|
148
|
-
'describe',
|
|
149
|
-
'it',
|
|
150
|
-
'test',
|
|
151
|
-
'beforeEach',
|
|
152
|
-
'afterEach',
|
|
153
|
-
'beforeAll',
|
|
154
|
-
'afterAll',
|
|
155
|
-
'fn',
|
|
156
|
-
'spyOn',
|
|
157
|
-
'mock',
|
|
158
|
-
'mockResolvedValue',
|
|
159
|
-
'mockReturnValue',
|
|
160
|
-
'mockImplementation',
|
|
161
|
-
'mockReturnThis',
|
|
162
|
-
'now',
|
|
163
|
-
'toISOString',
|
|
164
|
-
'getTime',
|
|
165
|
-
// Globals
|
|
166
|
-
'console',
|
|
167
|
-
'Math',
|
|
168
|
-
'Date',
|
|
169
|
-
'JSON',
|
|
170
|
-
'Object',
|
|
171
|
-
'Array',
|
|
172
|
-
'String',
|
|
173
|
-
'Number',
|
|
174
|
-
'Boolean',
|
|
175
|
-
'Promise',
|
|
176
|
-
'Error',
|
|
177
|
-
'Map',
|
|
178
|
-
'Set',
|
|
179
|
-
'RegExp',
|
|
180
|
-
'Buffer',
|
|
181
|
-
'process',
|
|
182
|
-
// Python builtins
|
|
183
|
-
'print',
|
|
184
|
-
'len',
|
|
185
|
-
'range',
|
|
186
|
-
'enumerate',
|
|
187
|
-
'zip',
|
|
188
|
-
'isinstance',
|
|
189
|
-
'type',
|
|
190
|
-
'super',
|
|
191
|
-
'self',
|
|
192
|
-
'cls',
|
|
193
|
-
'None',
|
|
194
|
-
'True',
|
|
195
|
-
'False',
|
|
196
|
-
'append',
|
|
197
|
-
'extend',
|
|
198
|
-
'insert',
|
|
199
|
-
'remove',
|
|
200
|
-
'update',
|
|
201
|
-
'items',
|
|
202
|
-
'format',
|
|
203
|
-
'strip',
|
|
204
|
-
'upper',
|
|
205
|
-
'lower',
|
|
206
|
-
// Ruby builtins
|
|
207
|
-
'puts',
|
|
208
|
-
'raise',
|
|
209
|
-
'yield',
|
|
210
|
-
'each',
|
|
211
|
-
'do',
|
|
212
|
-
'end',
|
|
213
|
-
'attr_accessor',
|
|
214
|
-
'attr_reader',
|
|
215
|
-
'attr_writer',
|
|
216
|
-
'respond_to',
|
|
217
|
-
'render',
|
|
218
|
-
'redirect_to',
|
|
219
|
-
'before_action',
|
|
220
|
-
'after_action',
|
|
221
|
-
'validates',
|
|
222
|
-
'has_many',
|
|
223
|
-
'belongs_to',
|
|
224
|
-
'has_one',
|
|
225
|
-
'new',
|
|
226
|
-
'initialize',
|
|
227
|
-
// Go builtins
|
|
228
|
-
'fmt',
|
|
229
|
-
'Println',
|
|
230
|
-
'Printf',
|
|
231
|
-
'Sprintf',
|
|
232
|
-
'Errorf',
|
|
233
|
-
'make',
|
|
234
|
-
'panic',
|
|
235
|
-
'recover',
|
|
236
|
-
'defer',
|
|
237
|
-
// Java builtins
|
|
238
|
-
'System',
|
|
239
|
-
'println',
|
|
240
|
-
'equals',
|
|
241
|
-
'hashCode',
|
|
242
|
-
'getClass',
|
|
243
|
-
]);
|
package/src/shared/logger.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
// src/shared/logger.ts
|
|
2
|
-
export const log = {
|
|
3
|
-
info(msg: string, ctx?: Record<string, unknown>): void {
|
|
4
|
-
process.stderr.write(`[INFO] ${msg}${ctx ? ` ${JSON.stringify(ctx)}` : ''}\n`);
|
|
5
|
-
},
|
|
6
|
-
debug(msg: string, ctx?: Record<string, unknown>): void {
|
|
7
|
-
if (process.env.KODUS_GRAPH_DEBUG) {
|
|
8
|
-
process.stderr.write(`[DEBUG] ${msg}${ctx ? ` ${JSON.stringify(ctx)}` : ''}\n`);
|
|
9
|
-
}
|
|
10
|
-
},
|
|
11
|
-
warn(msg: string, ctx?: Record<string, unknown>): void {
|
|
12
|
-
process.stderr.write(`[WARN] ${msg}${ctx ? ` ${JSON.stringify(ctx)}` : ''}\n`);
|
|
13
|
-
},
|
|
14
|
-
error(msg: string, ctx?: Record<string, unknown>): void {
|
|
15
|
-
process.stderr.write(`[ERROR] ${msg}${ctx ? ` ${JSON.stringify(ctx)}` : ''}\n`);
|
|
16
|
-
},
|
|
17
|
-
};
|
package/src/shared/safe-path.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { realpathSync } from 'fs';
|
|
2
|
-
import { relative, resolve } from 'path';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Validate that a resolved path is within the repository root.
|
|
6
|
-
* Returns the validated absolute path.
|
|
7
|
-
* Throws if the path escapes the root.
|
|
8
|
-
*/
|
|
9
|
-
export function ensureWithinRoot(filePath: string, repoRoot: string): string {
|
|
10
|
-
let absRoot: string;
|
|
11
|
-
try {
|
|
12
|
-
absRoot = realpathSync(resolve(repoRoot));
|
|
13
|
-
} catch {
|
|
14
|
-
absRoot = resolve(repoRoot);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
let absPath: string;
|
|
18
|
-
try {
|
|
19
|
-
absPath = realpathSync(resolve(absRoot, filePath));
|
|
20
|
-
} catch {
|
|
21
|
-
// File doesn't exist yet or is unreadable — use resolve without symlink follow
|
|
22
|
-
absPath = resolve(absRoot, filePath);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const rel = relative(absRoot, absPath);
|
|
26
|
-
if (rel.startsWith('..') || resolve(absRoot, rel) !== absPath) {
|
|
27
|
-
throw new Error(`Path escapes repository root: ${filePath}`);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return absPath;
|
|
31
|
-
}
|