universal-ast-mapper 1.23.0 → 1.24.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/CHANGELOG.md +17 -0
- package/README.md +2 -1
- package/dist/callgraph.js +21 -7
- package/dist/graph.js +2 -2
- package/dist/resolver.js +29 -0
- package/dist/tsconfig.js +212 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,23 @@ since 1.0.0, guarantees a stable MCP tool / CLI surface across the 1.x line.
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## [1.24.0] — 2026-06-10 · TS path-alias resolution
|
|
10
|
+
- Bare imports like `@/components/Button` now resolve through **`tsconfig.json` /
|
|
11
|
+
`jsconfig.json` `compilerOptions.paths`** (+ `baseUrl`): nearest-config lookup above
|
|
12
|
+
the importing file (monorepo-safe, per-process cached), relative `extends` chains
|
|
13
|
+
(child `paths` replace the parent's, per TS semantics), longest-prefix pattern
|
|
14
|
+
matching, candidate probing with the usual extension/index logic.
|
|
15
|
+
- **String-aware JSONC parser** — comments/trailing commas are stripped with a
|
|
16
|
+
character walk, not regex (naive stripping corrupts Next.js configs where `"@/*"`
|
|
17
|
+
pairs with the `*/` inside `"**/*.ts"` include globs).
|
|
18
|
+
- Wired into `resolve_imports` (aliased imports report `importKind: "relative"` +
|
|
19
|
+
resolved file), `build_symbol_graph` (alias edges before workspace-package fallback),
|
|
20
|
+
and the call graph (callee origin + reverse `calledBy`).
|
|
21
|
+
- Real-world effect (Next.js app, 186 files): import graph 31 → **324 edges**;
|
|
22
|
+
dead exports 210 → 153; god nodes now reflect true usage.
|
|
23
|
+
- New module `tsconfig` (`aliasCandidates`, `clearAliasCaches`) + `resolveAliasedImport`
|
|
24
|
+
in the resolver. Tests: new `test/tsalias-smoke.mjs` (15 checks), wired into `npm test`.
|
|
25
|
+
|
|
9
26
|
## [1.23.0] — 2026-06-10 · Configurable root boundary (multi-root + unlocked)
|
|
10
27
|
- **`AST_MAP_ROOT` accepts multiple roots**, separated by the OS path delimiter
|
|
11
28
|
(`;` Windows / `:` POSIX). The first root is primary; absolute paths inside any
|
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ Built on [tree-sitter](https://tree-sitter.github.io/) WASM grammars. Zero regex
|
|
|
20
20
|
> As of v0.8.2, all four v0.8.0 languages have **cross-file graph + resolver** wiring: Kotlin (FQCN/package index), C/C++ (`#include` with header↔impl pairing), and Swift (module = directory under `Sources/`). Call-graph callee origin is resolved for Kotlin; for C/C++/Swift it stays limited because their imports don't name individual symbols. (PHP & Ruby landed in v1.22.0 — symbol extraction + imports; cross-file graph wiring for them is the next step. Ruby was unblocked by upgrading `web-tree-sitter` to 0.21.0.)
|
|
21
21
|
|
|
22
22
|
Each language uses the resolution strategy that fits it:
|
|
23
|
-
- **TS/JS/Python** — relative paths (`./foo`, `..mod`) resolved against the importing file's directory, with TS-ESM `.js` → `.ts` rewriting.
|
|
23
|
+
- **TS/JS/Python** — relative paths (`./foo`, `..mod`) resolved against the importing file's directory, with TS-ESM `.js` → `.ts` rewriting. **Path aliases** (`@/*` etc.) resolve via the nearest `tsconfig.json`/`jsconfig.json` (`paths` + `baseUrl`, relative `extends`). *(v1.24.0)*
|
|
24
24
|
- **Go** — `go.mod` ancestor lookup → module path prefix → package directory → all `.go` files (skips `_test.go`).
|
|
25
25
|
- **Rust** — `Cargo.toml` ancestor → `crate::` / `self::` / `super::` walks; supports `mod.rs` + Rust-2018 sibling-dir style.
|
|
26
26
|
- **Java** — project-wide FQCN index (`package + "." + className → file`) built lazily on first cross-lang call; supports wildcard imports.
|
|
@@ -801,6 +801,7 @@ Not part of the public API: the internal `src/` module layout and the generated
|
|
|
801
801
|
|
|
802
802
|
| Version | What changed |
|
|
803
803
|
|---------|--------------|
|
|
804
|
+
| **1.24.0** | **TS path-alias resolution** — bare imports like `@/components/Button` now resolve via the **nearest** `tsconfig.json`/`jsconfig.json` (`compilerOptions.paths` + `baseUrl`, relative `extends` chains, longest-prefix matching, string-aware JSONC parser). Wired into `resolve_imports`, the symbol graph, and the call graph — on a real Next.js app this took the import graph from 31 to **324 edges** and cut false dead-exports by ~30%. |
|
|
804
805
|
| **1.23.0** | **Configurable root boundary** — `AST_MAP_ROOT` accepts **multiple roots** (path-delimiter separated) and `AST_MAP_UNLOCKED=1` allows analyzing **any absolute path** on request (default stays locked). Analysis/graph/report rel-paths now computed against the matched root, so cross-root results are correct. New `roots` module + 13-check test suite. |
|
|
805
806
|
| **1.22.0** | **PHP & Ruby support** — `.php` (classes, interfaces, traits, enums, methods with visibility, `use` imports incl. grouped, require/include) and `.rb`/`.rake` (classes, modules, methods, `self.` singleton methods, `private` section tracking, require/require_relative). Unblocked by upgrading `web-tree-sitter` 0.20.8 → 0.21.0 (all existing grammars re-verified). **16 languages**. |
|
|
806
807
|
| **1.21.0** | **Quality gate** — `ast-map check` fails CI when quality regresses: **baseline ratchet** vs `.ast-map.baseline.json` (cycles · dead exports · SDP · very-high complexity · score; `--update-baseline` re-anchors) + absolute thresholds (flags or config `"check"`). New MCP tool `check_quality_gate` (**28 tools**); GitHub Action gains `mode: check`. |
|
package/dist/callgraph.js
CHANGED
|
@@ -4,7 +4,7 @@ import { parseSource } from "./parser.js";
|
|
|
4
4
|
import { buildSkeleton } from "./skeleton.js";
|
|
5
5
|
import { resolveOptions, loadProjectConfig } from "./config.js";
|
|
6
6
|
import { detectLanguage } from "./registry.js";
|
|
7
|
-
import { resolveImportPath, getOrBuildCrossLangIndex } from "./resolver.js";
|
|
7
|
+
import { resolveImportPath, resolveAliasedImport, getOrBuildCrossLangIndex } from "./resolver.js";
|
|
8
8
|
import { resolveCrossLangTarget } from "./crosslang.js";
|
|
9
9
|
const CROSS_LANG = new Set(["java", "csharp", "rust", "go", "kotlin", "c", "cpp", "swift"]);
|
|
10
10
|
function pushCall(out, callee, anchor) {
|
|
@@ -313,8 +313,14 @@ export async function buildCallGraph(filePath, funcName, root, allSkeletons) {
|
|
|
313
313
|
}
|
|
314
314
|
}
|
|
315
315
|
else {
|
|
316
|
-
|
|
317
|
-
|
|
316
|
+
const aliasAbs = resolveAliasedImport(importRef.from, filePath);
|
|
317
|
+
if (aliasAbs) {
|
|
318
|
+
call.calleeFileRel = path.relative(root, aliasAbs).split(path.sep).join("/");
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
call.isExternal = true;
|
|
322
|
+
call.calleeFileRel = importRef.from;
|
|
323
|
+
}
|
|
318
324
|
}
|
|
319
325
|
}
|
|
320
326
|
else if (aliasOrigin) {
|
|
@@ -326,8 +332,14 @@ export async function buildCallGraph(filePath, funcName, root, allSkeletons) {
|
|
|
326
332
|
}
|
|
327
333
|
}
|
|
328
334
|
else {
|
|
329
|
-
|
|
330
|
-
|
|
335
|
+
const aliasAbs = resolveAliasedImport(aliasOrigin, filePath);
|
|
336
|
+
if (aliasAbs) {
|
|
337
|
+
call.calleeFileRel = path.relative(root, aliasAbs).split(path.sep).join("/");
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
call.isExternal = true;
|
|
341
|
+
call.calleeFileRel = aliasOrigin;
|
|
342
|
+
}
|
|
331
343
|
}
|
|
332
344
|
}
|
|
333
345
|
else if (crossIndex && skel.language === "csharp") {
|
|
@@ -387,8 +399,10 @@ export async function buildCallGraph(filePath, funcName, root, allSkeletons) {
|
|
|
387
399
|
break;
|
|
388
400
|
}
|
|
389
401
|
}
|
|
390
|
-
else
|
|
391
|
-
const resolvedAbs =
|
|
402
|
+
else {
|
|
403
|
+
const resolvedAbs = imp.from.startsWith(".")
|
|
404
|
+
? resolveImportPath(imp.from, otherAbs)
|
|
405
|
+
: resolveAliasedImport(imp.from, otherAbs);
|
|
392
406
|
if (!resolvedAbs)
|
|
393
407
|
continue;
|
|
394
408
|
const resolvedRel = path.relative(root, resolvedAbs).split(path.sep).join("/");
|
package/dist/graph.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { resolveImportPath } from "./resolver.js";
|
|
2
|
+
import { resolveImportPath, resolveAliasedImport } from "./resolver.js";
|
|
3
3
|
import { resolveWorkspaceImportCached } from "./workspace.js";
|
|
4
4
|
import { buildCrossLangIndex, resolveCrossLangTarget, } from "./crosslang.js";
|
|
5
5
|
// ─── Internal helpers ─────────────────────────────────────────────────────────
|
|
@@ -38,7 +38,7 @@ function wirePathImport(skel, imp, fromFileAbs, root, exportedSymbolMap, edges)
|
|
|
38
38
|
// Relative import → path resolve; bare specifier → monorepo workspace package.
|
|
39
39
|
const resolvedAbs = imp.from.startsWith(".")
|
|
40
40
|
? resolveImportPath(imp.from, fromFileAbs)
|
|
41
|
-
: resolveWorkspaceImportCached(imp.from, root);
|
|
41
|
+
: resolveAliasedImport(imp.from, fromFileAbs) ?? resolveWorkspaceImportCached(imp.from, root);
|
|
42
42
|
if (!resolvedAbs)
|
|
43
43
|
return;
|
|
44
44
|
const resolvedRel = path.relative(root, resolvedAbs).split(path.sep).join("/");
|
package/dist/resolver.js
CHANGED
|
@@ -5,6 +5,7 @@ import { resolveOptions } from "./config.js";
|
|
|
5
5
|
import { findSymbol } from "./analysis.js";
|
|
6
6
|
import { buildCrossLangIndex, resolveCrossLangTarget, } from "./crosslang.js";
|
|
7
7
|
import { resolveWorkspaceImportCached } from "./workspace.js";
|
|
8
|
+
import { aliasCandidates } from "./tsconfig.js";
|
|
8
9
|
const SRC_EXTS = [".ts", ".tsx", ".js", ".jsx", ".mts", ".cts", ".mjs", ".cjs", ".vue", ".svelte"];
|
|
9
10
|
function extractParams(sig) {
|
|
10
11
|
const start = sig.indexOf("(");
|
|
@@ -46,6 +47,10 @@ export function resolveImportPath(importFrom, fromAbs) {
|
|
|
46
47
|
return p;
|
|
47
48
|
}
|
|
48
49
|
}
|
|
50
|
+
return probeCandidate(candidate);
|
|
51
|
+
}
|
|
52
|
+
/** Probe a path base: exact file → +extensions → /index.<ext>. */
|
|
53
|
+
function probeCandidate(candidate) {
|
|
49
54
|
try {
|
|
50
55
|
const stat = fs.statSync(candidate);
|
|
51
56
|
if (stat.isFile())
|
|
@@ -64,6 +69,28 @@ export function resolveImportPath(importFrom, fromAbs) {
|
|
|
64
69
|
}
|
|
65
70
|
return null;
|
|
66
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Resolve a tsconfig/jsconfig path-aliased bare import (e.g. `@/components/X`
|
|
74
|
+
* with `"@/*": ["./src/*"]`) to an absolute file path, using the nearest
|
|
75
|
+
* config above the importing file. Returns null when not an alias.
|
|
76
|
+
*/
|
|
77
|
+
export function resolveAliasedImport(importFrom, fromAbs) {
|
|
78
|
+
for (const base of aliasCandidates(importFrom, fromAbs)) {
|
|
79
|
+
const declaredExt = path.extname(base).toLowerCase();
|
|
80
|
+
if (declaredExt && JS_TO_TS[declaredExt]) {
|
|
81
|
+
const stem = base.slice(0, base.length - declaredExt.length);
|
|
82
|
+
for (const ext of JS_TO_TS[declaredExt]) {
|
|
83
|
+
const p = stem + ext;
|
|
84
|
+
if (fs.existsSync(p))
|
|
85
|
+
return p;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const hit = probeCandidate(base);
|
|
89
|
+
if (hit)
|
|
90
|
+
return hit;
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
67
94
|
/* ─── Cross-language index cache ──────────────────────────────────────────── */
|
|
68
95
|
// Java/C# need a project-wide index to resolve fully-qualified imports.
|
|
69
96
|
// Built lazily on first cross-language resolve, then reused for the process
|
|
@@ -124,6 +151,8 @@ async function enrichRelativeImport(imp, fromAbs, root) {
|
|
|
124
151
|
const isBare = !imp.from.startsWith(".");
|
|
125
152
|
// Relative import → path resolve; bare specifier → try monorepo workspace.
|
|
126
153
|
let resolvedAbs = isBare ? null : resolveImportPath(imp.from, fromAbs);
|
|
154
|
+
if (!resolvedAbs && isBare)
|
|
155
|
+
resolvedAbs = resolveAliasedImport(imp.from, fromAbs);
|
|
127
156
|
if (!resolvedAbs && isBare)
|
|
128
157
|
resolvedAbs = resolveWorkspaceImportCached(imp.from, root);
|
|
129
158
|
const treatedExternal = isBare && !resolvedAbs;
|
package/dist/tsconfig.js
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const CONFIG_NAMES = ["tsconfig.json", "jsconfig.json"];
|
|
4
|
+
/**
|
|
5
|
+
* Tolerant JSONC parse. String-aware: comments and trailing commas are removed
|
|
6
|
+
* with a character walk, never with regex — naive stripping corrupts configs
|
|
7
|
+
* whose strings contain comment-like text (e.g. Next.js `"include": ["**\/*.ts"]`
|
|
8
|
+
* pairs the `/*` inside `"@/*"` with the `*\/` inside the glob).
|
|
9
|
+
*/
|
|
10
|
+
function parseJsonc(raw) {
|
|
11
|
+
let out = "";
|
|
12
|
+
let i = 0;
|
|
13
|
+
let inStr = false;
|
|
14
|
+
while (i < raw.length) {
|
|
15
|
+
const c = raw[i];
|
|
16
|
+
if (inStr) {
|
|
17
|
+
out += c;
|
|
18
|
+
if (c === "\\") {
|
|
19
|
+
out += raw[i + 1] ?? "";
|
|
20
|
+
i += 2;
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
if (c === '"')
|
|
24
|
+
inStr = false;
|
|
25
|
+
i++;
|
|
26
|
+
}
|
|
27
|
+
else if (c === '"') {
|
|
28
|
+
inStr = true;
|
|
29
|
+
out += c;
|
|
30
|
+
i++;
|
|
31
|
+
}
|
|
32
|
+
else if (c === "/" && raw[i + 1] === "/") {
|
|
33
|
+
while (i < raw.length && raw[i] !== "\n")
|
|
34
|
+
i++;
|
|
35
|
+
}
|
|
36
|
+
else if (c === "/" && raw[i + 1] === "*") {
|
|
37
|
+
i += 2;
|
|
38
|
+
while (i < raw.length && !(raw[i] === "*" && raw[i + 1] === "/"))
|
|
39
|
+
i++;
|
|
40
|
+
i += 2;
|
|
41
|
+
}
|
|
42
|
+
else if (c === ",") {
|
|
43
|
+
// trailing comma: skip when the next non-whitespace char closes a scope
|
|
44
|
+
let j = i + 1;
|
|
45
|
+
while (j < raw.length && /\s/.test(raw[j]))
|
|
46
|
+
j++;
|
|
47
|
+
if (raw[j] === "}" || raw[j] === "]")
|
|
48
|
+
i++; // drop the comma
|
|
49
|
+
else {
|
|
50
|
+
out += c;
|
|
51
|
+
i++;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
out += c;
|
|
56
|
+
i++;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
return JSON.parse(out);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** Read a config file, following relative `extends` (child overrides parent). */
|
|
67
|
+
function readConfigChain(configPath, depth = 0) {
|
|
68
|
+
if (depth > 5)
|
|
69
|
+
return null;
|
|
70
|
+
let raw;
|
|
71
|
+
try {
|
|
72
|
+
raw = fs.readFileSync(configPath, "utf8");
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
const json = parseJsonc(raw);
|
|
78
|
+
if (!json || typeof json !== "object")
|
|
79
|
+
return null;
|
|
80
|
+
const dir = path.dirname(configPath);
|
|
81
|
+
let baseUrl;
|
|
82
|
+
let paths;
|
|
83
|
+
let baseDir = dir;
|
|
84
|
+
const ext = json.extends;
|
|
85
|
+
if (typeof ext === "string" && ext.startsWith(".")) {
|
|
86
|
+
let parentPath = path.resolve(dir, ext);
|
|
87
|
+
if (!parentPath.endsWith(".json"))
|
|
88
|
+
parentPath += ".json";
|
|
89
|
+
const parent = readConfigChain(parentPath, depth + 1);
|
|
90
|
+
if (parent) {
|
|
91
|
+
baseUrl = parent.baseUrl;
|
|
92
|
+
paths = parent.paths;
|
|
93
|
+
baseDir = parent.dir; // paths in a parent resolve against the parent's dir
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const co = json.compilerOptions;
|
|
97
|
+
if (co && typeof co === "object") {
|
|
98
|
+
if (typeof co.baseUrl === "string") {
|
|
99
|
+
baseUrl = co.baseUrl;
|
|
100
|
+
baseDir = dir;
|
|
101
|
+
}
|
|
102
|
+
if (co.paths && typeof co.paths === "object") {
|
|
103
|
+
paths = co.paths;
|
|
104
|
+
baseDir = dir;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return { baseUrl, paths, dir: baseDir };
|
|
108
|
+
}
|
|
109
|
+
function buildAliasConfig(configPath) {
|
|
110
|
+
const merged = readConfigChain(configPath);
|
|
111
|
+
if (!merged || !merged.paths)
|
|
112
|
+
return null;
|
|
113
|
+
const base = path.resolve(merged.dir, merged.baseUrl ?? ".");
|
|
114
|
+
const patterns = [];
|
|
115
|
+
for (const [key, targets] of Object.entries(merged.paths)) {
|
|
116
|
+
if (!Array.isArray(targets) || targets.length === 0)
|
|
117
|
+
continue;
|
|
118
|
+
const star = key.indexOf("*");
|
|
119
|
+
const abs = targets
|
|
120
|
+
.filter((t) => typeof t === "string")
|
|
121
|
+
.map((t) => path.resolve(base, t));
|
|
122
|
+
if (abs.length === 0)
|
|
123
|
+
continue;
|
|
124
|
+
if (star === -1) {
|
|
125
|
+
patterns.push({ prefix: key, suffix: "", exact: true, targets: abs });
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
patterns.push({ prefix: key.slice(0, star), suffix: key.slice(star + 1), exact: false, targets: abs });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Longest prefix wins (TypeScript's matching rule).
|
|
132
|
+
patterns.sort((a, b) => b.prefix.length - a.prefix.length);
|
|
133
|
+
return patterns.length > 0 ? { patterns } : null;
|
|
134
|
+
}
|
|
135
|
+
// dir → config path (or null when none found up the tree)
|
|
136
|
+
const configPathCache = new Map();
|
|
137
|
+
// config path → parsed alias config (or null when it has no paths)
|
|
138
|
+
const aliasCache = new Map();
|
|
139
|
+
function findNearestConfig(fromDir) {
|
|
140
|
+
const cached = configPathCache.get(fromDir);
|
|
141
|
+
if (cached !== undefined)
|
|
142
|
+
return cached;
|
|
143
|
+
let dir = fromDir;
|
|
144
|
+
let result = null;
|
|
145
|
+
const visited = [];
|
|
146
|
+
for (;;) {
|
|
147
|
+
const hit = configPathCache.get(dir);
|
|
148
|
+
if (hit !== undefined) {
|
|
149
|
+
result = hit;
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
visited.push(dir);
|
|
153
|
+
let found = null;
|
|
154
|
+
for (const name of CONFIG_NAMES) {
|
|
155
|
+
const p = path.join(dir, name);
|
|
156
|
+
if (fs.existsSync(p)) {
|
|
157
|
+
found = p;
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (found) {
|
|
162
|
+
result = found;
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
const parent = path.dirname(dir);
|
|
166
|
+
if (parent === dir || dir.includes("node_modules")) {
|
|
167
|
+
result = null;
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
dir = parent;
|
|
171
|
+
}
|
|
172
|
+
for (const d of visited)
|
|
173
|
+
configPathCache.set(d, result);
|
|
174
|
+
return result;
|
|
175
|
+
}
|
|
176
|
+
/** Test-only: clear the per-process caches. */
|
|
177
|
+
export function clearAliasCaches() {
|
|
178
|
+
configPathCache.clear();
|
|
179
|
+
aliasCache.clear();
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Map an aliased bare import to absolute candidate base paths (no extension
|
|
183
|
+
* probing). Empty array = not an alias / no config / no pattern match.
|
|
184
|
+
*/
|
|
185
|
+
export function aliasCandidates(importFrom, fromAbs) {
|
|
186
|
+
if (importFrom.startsWith(".") || path.isAbsolute(importFrom))
|
|
187
|
+
return [];
|
|
188
|
+
const configPath = findNearestConfig(path.dirname(fromAbs));
|
|
189
|
+
if (!configPath)
|
|
190
|
+
return [];
|
|
191
|
+
let cfg = aliasCache.get(configPath);
|
|
192
|
+
if (cfg === undefined) {
|
|
193
|
+
cfg = buildAliasConfig(configPath);
|
|
194
|
+
aliasCache.set(configPath, cfg);
|
|
195
|
+
}
|
|
196
|
+
if (!cfg)
|
|
197
|
+
return [];
|
|
198
|
+
for (const p of cfg.patterns) {
|
|
199
|
+
if (p.exact) {
|
|
200
|
+
if (importFrom === p.prefix)
|
|
201
|
+
return p.targets;
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
if (importFrom.length >= p.prefix.length + p.suffix.length &&
|
|
205
|
+
importFrom.startsWith(p.prefix) &&
|
|
206
|
+
importFrom.endsWith(p.suffix)) {
|
|
207
|
+
const star = importFrom.slice(p.prefix.length, importFrom.length - p.suffix.length);
|
|
208
|
+
return p.targets.map((t) => t.replace("*", star));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return [];
|
|
212
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "universal-ast-mapper",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.24.0",
|
|
4
4
|
"description": "MCP server that maps source files into a normalized code skeleton (JSON + HTML) using tree-sitter.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"build": "tsc",
|
|
20
20
|
"start": "node dist/index.js",
|
|
21
21
|
"smoke": "node test/smoke.mjs",
|
|
22
|
-
"test": "node test/smoke.mjs && node test/analysis.mjs && node test/cache-smoke.mjs && node test/check-smoke.mjs && node test/roots-smoke.mjs",
|
|
22
|
+
"test": "node test/smoke.mjs && node test/analysis.mjs && node test/cache-smoke.mjs && node test/check-smoke.mjs && node test/roots-smoke.mjs && node test/tsalias-smoke.mjs",
|
|
23
23
|
"postinstall": "node scripts/install-skill.mjs"
|
|
24
24
|
},
|
|
25
25
|
"engines": {
|