@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
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ruby import resolver.
|
|
3
|
+
*
|
|
4
|
+
* Handles require_relative paths, Gemfile path: gems, and Zeitwerk autoload.
|
|
5
|
+
*/
|
|
6
|
+
import { readFileSync } from 'fs';
|
|
7
|
+
import { dirname, join, resolve as resolvePath } from 'path';
|
|
8
|
+
import { cachedExists } from '../fs-cache';
|
|
9
|
+
/** Cache parsed Gemfile path gems per repo root. */
|
|
10
|
+
const gemfileCache = new Map();
|
|
11
|
+
/**
|
|
12
|
+
* Parse Gemfile for path: gems and return their lib directories (absolute).
|
|
13
|
+
*/
|
|
14
|
+
function getGemPathLibDirs(repoRoot) {
|
|
15
|
+
if (gemfileCache.has(repoRoot)) {
|
|
16
|
+
return gemfileCache.get(repoRoot);
|
|
17
|
+
}
|
|
18
|
+
const gemfilePath = join(repoRoot, 'Gemfile');
|
|
19
|
+
const libDirs = [];
|
|
20
|
+
if (cachedExists(gemfilePath)) {
|
|
21
|
+
const content = readFileSync(gemfilePath, 'utf-8');
|
|
22
|
+
// Match lines like: gem 'mylib', path: './libs/mylib'
|
|
23
|
+
const regex = /^\s*gem\s+['"][^'"]+['"]\s*,\s*path:\s*['"]([^'"]+)['"]/gm;
|
|
24
|
+
let match = regex.exec(content);
|
|
25
|
+
while (match !== null) {
|
|
26
|
+
const gemPath = match[1];
|
|
27
|
+
libDirs.push(resolvePath(join(repoRoot, gemPath, 'lib')));
|
|
28
|
+
match = regex.exec(content);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
gemfileCache.set(repoRoot, libDirs);
|
|
32
|
+
return libDirs;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Resolve a Ruby require/require_relative to a file path.
|
|
36
|
+
*/
|
|
37
|
+
export function resolve(fromAbsFile, modulePath, repoRoot) {
|
|
38
|
+
if (!modulePath) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
// 1. Try relative resolution (require_relative style)
|
|
42
|
+
const base = join(dirname(fromAbsFile), modulePath);
|
|
43
|
+
if (cachedExists(`${base}.rb`)) {
|
|
44
|
+
return resolvePath(`${base}.rb`);
|
|
45
|
+
}
|
|
46
|
+
if (cachedExists(base)) {
|
|
47
|
+
return resolvePath(base);
|
|
48
|
+
}
|
|
49
|
+
// 2. Try Gemfile path: gems
|
|
50
|
+
for (const libDir of getGemPathLibDirs(repoRoot)) {
|
|
51
|
+
const candidate = join(libDir, modulePath);
|
|
52
|
+
if (cachedExists(`${candidate}.rb`)) {
|
|
53
|
+
return resolvePath(`${candidate}.rb`);
|
|
54
|
+
}
|
|
55
|
+
if (cachedExists(candidate)) {
|
|
56
|
+
return resolvePath(candidate);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
/** Common Rails autoload paths that Zeitwerk watches. */
|
|
62
|
+
const ZEITWERK_AUTOLOAD_PATHS = [
|
|
63
|
+
'app/models',
|
|
64
|
+
'app/controllers',
|
|
65
|
+
'app/services',
|
|
66
|
+
'app/jobs',
|
|
67
|
+
'app/mailers',
|
|
68
|
+
'app/helpers',
|
|
69
|
+
'lib',
|
|
70
|
+
];
|
|
71
|
+
/**
|
|
72
|
+
* Convert a CamelCase segment to snake_case.
|
|
73
|
+
* E.g., "AuthService" → "auth_service", "UsersController" → "users_controller"
|
|
74
|
+
*/
|
|
75
|
+
function camelToSnake(name) {
|
|
76
|
+
return name
|
|
77
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
|
|
78
|
+
.replace(/([a-z\d])([A-Z])/g, '$1_$2')
|
|
79
|
+
.toLowerCase();
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Resolve a Ruby class/module constant name to a file path using Zeitwerk conventions.
|
|
83
|
+
*
|
|
84
|
+
* Zeitwerk maps constant names to file paths:
|
|
85
|
+
* - `User` → `user.rb`
|
|
86
|
+
* - `AuthService` → `auth_service.rb`
|
|
87
|
+
* - `Admin::UsersController` → `admin/users_controller.rb`
|
|
88
|
+
*
|
|
89
|
+
* This searches common Rails autoload paths for a matching file.
|
|
90
|
+
*
|
|
91
|
+
* @param className - The fully-qualified constant name (e.g., "Admin::UsersController")
|
|
92
|
+
* @param repoRoot - The root of the repository / Rails project
|
|
93
|
+
* @returns Absolute path to the resolved file, or null if not found
|
|
94
|
+
*/
|
|
95
|
+
export function resolveZeitwerk(className, repoRoot) {
|
|
96
|
+
if (!className) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
// Split on :: and convert each segment from CamelCase to snake_case
|
|
100
|
+
const segments = className.split('::');
|
|
101
|
+
const relativePath = segments.map(camelToSnake).join('/');
|
|
102
|
+
// Search each autoload path
|
|
103
|
+
for (const autoloadPath of ZEITWERK_AUTOLOAD_PATHS) {
|
|
104
|
+
const candidate = join(repoRoot, autoloadPath, `${relativePath}.rb`);
|
|
105
|
+
if (cachedExists(candidate)) {
|
|
106
|
+
return resolvePath(candidate);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function resolve(fromAbsFile: string, modulePath: string, repoRoot: string): string | null;
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import { basename, dirname, join, resolve as resolvePath } from 'path';
|
|
3
|
+
import { cachedExists } from '../fs-cache';
|
|
4
|
+
function probeRustPath(baseDir, relPath) {
|
|
5
|
+
const asFile = join(baseDir, `${relPath}.rs`);
|
|
6
|
+
if (cachedExists(asFile)) {
|
|
7
|
+
return resolvePath(asFile);
|
|
8
|
+
}
|
|
9
|
+
const asMod = join(baseDir, relPath, 'mod.rs');
|
|
10
|
+
if (cachedExists(asMod)) {
|
|
11
|
+
return resolvePath(asMod);
|
|
12
|
+
}
|
|
13
|
+
const asLib = join(baseDir, relPath, 'lib.rs');
|
|
14
|
+
if (cachedExists(asLib)) {
|
|
15
|
+
return resolvePath(asLib);
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
export function resolve(fromAbsFile, modulePath, repoRoot) {
|
|
20
|
+
if (modulePath.startsWith('std::')) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
if (modulePath.startsWith('crate::')) {
|
|
24
|
+
const rest = modulePath.slice('crate::'.length).replace(/::/g, '/');
|
|
25
|
+
return probeRustPath(join(repoRoot, 'src'), rest);
|
|
26
|
+
}
|
|
27
|
+
if (modulePath.startsWith('super::')) {
|
|
28
|
+
const rest = modulePath.slice('super::'.length).replace(/::/g, '/');
|
|
29
|
+
const fileName = basename(fromAbsFile);
|
|
30
|
+
// mod.rs and lib.rs represent their parent directory, so super:: goes up two levels
|
|
31
|
+
const parentDir = fileName === 'mod.rs' || fileName === 'lib.rs' ? dirname(dirname(fromAbsFile)) : dirname(fromAbsFile);
|
|
32
|
+
return probeRustPath(parentDir, rest);
|
|
33
|
+
}
|
|
34
|
+
if (modulePath.startsWith('self::')) {
|
|
35
|
+
const rest = modulePath.slice('self::'.length).replace(/::/g, '/');
|
|
36
|
+
return probeRustPath(dirname(fromAbsFile), rest);
|
|
37
|
+
}
|
|
38
|
+
// Try workspace path dependency resolution
|
|
39
|
+
const firstSep = modulePath.indexOf('::');
|
|
40
|
+
if (firstSep !== -1) {
|
|
41
|
+
const crateName = modulePath.slice(0, firstSep);
|
|
42
|
+
const rest = modulePath.slice(firstSep + 2).replace(/::/g, '/');
|
|
43
|
+
const depPath = resolveWorkspacePathDep(fromAbsFile, crateName);
|
|
44
|
+
if (depPath) {
|
|
45
|
+
const srcDir = join(depPath, 'src');
|
|
46
|
+
// Try the full path first, then progressively strip trailing segments
|
|
47
|
+
// (they may be items like functions/structs inside a module file)
|
|
48
|
+
const segments = rest.split('/');
|
|
49
|
+
for (let i = segments.length; i >= 1; i--) {
|
|
50
|
+
const partial = segments.slice(0, i).join('/');
|
|
51
|
+
const result = probeRustPath(srcDir, partial);
|
|
52
|
+
if (result) {
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// bin+lib same crate: if the first segment matches the local [package] name,
|
|
58
|
+
// treat it like a crate:: import (resolve from src/)
|
|
59
|
+
const localPkgName = findLocalPackageName(fromAbsFile);
|
|
60
|
+
if (localPkgName && crateName === localPkgName) {
|
|
61
|
+
const srcDir = join(findCrateDir(fromAbsFile), 'src');
|
|
62
|
+
const segments = rest.split('/');
|
|
63
|
+
for (let i = segments.length; i >= 1; i--) {
|
|
64
|
+
const partial = segments.slice(0, i).join('/');
|
|
65
|
+
const result = probeRustPath(srcDir, partial);
|
|
66
|
+
if (result) {
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
/** Cache: crate dir → parsed {depName → resolved absolute path} */
|
|
75
|
+
const pathDepCache = new Map();
|
|
76
|
+
/**
|
|
77
|
+
* Walk up from `fromAbsFile` to find the nearest Cargo.toml,
|
|
78
|
+
* parse its [dependencies] for `path = "..."` entries,
|
|
79
|
+
* and return the absolute path of the dependency crate if it matches `depName`.
|
|
80
|
+
*/
|
|
81
|
+
function resolveWorkspacePathDep(fromAbsFile, depName) {
|
|
82
|
+
const crateDir = findCrateDir(fromAbsFile);
|
|
83
|
+
if (!crateDir) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
let deps = pathDepCache.get(crateDir);
|
|
87
|
+
if (!deps) {
|
|
88
|
+
deps = parsePathDeps(crateDir);
|
|
89
|
+
pathDepCache.set(crateDir, deps);
|
|
90
|
+
}
|
|
91
|
+
return deps.get(depName) ?? null;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Walk up from a file to find the nearest directory containing Cargo.toml.
|
|
95
|
+
*/
|
|
96
|
+
function findCrateDir(fromAbsFile) {
|
|
97
|
+
let dir = dirname(fromAbsFile);
|
|
98
|
+
const root = resolvePath('/');
|
|
99
|
+
while (dir !== root) {
|
|
100
|
+
if (cachedExists(join(dir, 'Cargo.toml'))) {
|
|
101
|
+
return dir;
|
|
102
|
+
}
|
|
103
|
+
const parent = dirname(dir);
|
|
104
|
+
if (parent === dir) {
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
dir = parent;
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
/** Cache: crate dir → parsed package name */
|
|
112
|
+
const pkgNameCache = new Map();
|
|
113
|
+
/**
|
|
114
|
+
* Find the [package] name from the nearest Cargo.toml for the given file.
|
|
115
|
+
* Used to detect bin+lib same-crate imports (e.g. `use myapp::foo` from main.rs).
|
|
116
|
+
*/
|
|
117
|
+
function findLocalPackageName(fromAbsFile) {
|
|
118
|
+
const crateDir = findCrateDir(fromAbsFile);
|
|
119
|
+
if (!crateDir) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
const cached = pkgNameCache.get(crateDir);
|
|
123
|
+
if (cached !== undefined) {
|
|
124
|
+
return cached;
|
|
125
|
+
}
|
|
126
|
+
const cargoPath = join(crateDir, 'Cargo.toml');
|
|
127
|
+
if (!cachedExists(cargoPath)) {
|
|
128
|
+
pkgNameCache.set(crateDir, null);
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
const content = readFileSync(cargoPath, 'utf-8');
|
|
132
|
+
const match = content.match(/^\s*name\s*=\s*"([^"]+)"/m);
|
|
133
|
+
// Cargo crate names use hyphens but Rust imports use underscores
|
|
134
|
+
const name = match ? match[1].replace(/-/g, '_') : null;
|
|
135
|
+
pkgNameCache.set(crateDir, name);
|
|
136
|
+
return name;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Parse Cargo.toml in `crateDir` for path dependencies.
|
|
140
|
+
* Returns a map of dependency name → resolved absolute directory.
|
|
141
|
+
*
|
|
142
|
+
* Handles both inline table and multi-line table forms:
|
|
143
|
+
* shared = { path = "../shared" }
|
|
144
|
+
* [dependencies.shared]
|
|
145
|
+
* path = "../shared"
|
|
146
|
+
*/
|
|
147
|
+
function parsePathDeps(crateDir) {
|
|
148
|
+
const result = new Map();
|
|
149
|
+
const cargoPath = join(crateDir, 'Cargo.toml');
|
|
150
|
+
if (!cachedExists(cargoPath)) {
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
const content = readFileSync(cargoPath, 'utf-8');
|
|
154
|
+
const lines = content.split('\n');
|
|
155
|
+
let inDepsSection = false;
|
|
156
|
+
let depsTableDep = null; // for [dependencies.foo] style
|
|
157
|
+
for (const line of lines) {
|
|
158
|
+
const trimmed = line.trim();
|
|
159
|
+
// Detect section headers
|
|
160
|
+
if (trimmed.startsWith('[')) {
|
|
161
|
+
// Check for [dependencies.foo] form
|
|
162
|
+
const subMatch = trimmed.match(/^\[dependencies\.(\S+)\]$/);
|
|
163
|
+
if (subMatch) {
|
|
164
|
+
depsTableDep = subMatch[1];
|
|
165
|
+
inDepsSection = false;
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
depsTableDep = null;
|
|
169
|
+
if (trimmed === '[dependencies]') {
|
|
170
|
+
inDepsSection = true;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
// Any other section header ends [dependencies]
|
|
174
|
+
inDepsSection = false;
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
// Inside [dependencies.foo], look for path = "..."
|
|
178
|
+
if (depsTableDep) {
|
|
179
|
+
const pathMatch = trimmed.match(/^path\s*=\s*"([^"]+)"/);
|
|
180
|
+
if (pathMatch) {
|
|
181
|
+
const resolved = resolvePath(crateDir, pathMatch[1]);
|
|
182
|
+
result.set(depsTableDep, resolved);
|
|
183
|
+
}
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
// Inside [dependencies], look for inline table with path
|
|
187
|
+
if (inDepsSection && trimmed.length > 0 && !trimmed.startsWith('#')) {
|
|
188
|
+
// name = { path = "..." ... }
|
|
189
|
+
const inlineMatch = trimmed.match(/^(\S+)\s*=\s*\{[^}]*path\s*=\s*"([^"]+)"[^}]*\}/);
|
|
190
|
+
if (inlineMatch) {
|
|
191
|
+
const resolved = resolvePath(crateDir, inlineMatch[2]);
|
|
192
|
+
result.set(inlineMatch[1], resolved);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return result;
|
|
197
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
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
|
+
* Load aliases from webpack.config.ts/js and vite.config.ts/js.
|
|
12
|
+
* These are NOT in tsconfig — many large projects use bundler aliases instead.
|
|
13
|
+
*
|
|
14
|
+
* Parses simple alias patterns from resolve.alias blocks.
|
|
15
|
+
* Returns Map<prefix, absoluteDir> — same format as tsconfig aliases.
|
|
16
|
+
*/
|
|
17
|
+
export declare function loadBundlerAliases(repoRoot: string): Map<string, string[]>;
|
|
18
|
+
/**
|
|
19
|
+
* Resolve a TypeScript/JavaScript relative import to an absolute file path.
|
|
20
|
+
* Returns null for non-relative (external package) imports.
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolve(fromAbsFile: string, modulePath: string, repoRoot: string): string | null;
|
|
23
|
+
/**
|
|
24
|
+
* Load and parse tsconfig.json path aliases.
|
|
25
|
+
*
|
|
26
|
+
* Tries tsconfig.json first, then tsconfig.base.json.
|
|
27
|
+
* Converts alias patterns like "@libs/*" into prefix → resolved dirs.
|
|
28
|
+
*/
|
|
29
|
+
export declare function loadTsconfigAliases(repoRoot: string): Map<string, string[]>;
|
|
30
|
+
/**
|
|
31
|
+
* Resolve an import path using tsconfig aliases.
|
|
32
|
+
*
|
|
33
|
+
* Tries each alias prefix, and for matches, probes extensions and index files.
|
|
34
|
+
*/
|
|
35
|
+
export declare function resolveWithAliases(modulePath: string, aliases: Map<string, string[]>, _repoRoot: string): string | null;
|