mirror of
https://github.com/safishamsi/graphify.git
synced 2026-09-01 11:15:48 +00:00
fix: resolve tsconfig path aliases relative to baseUrl
_read_tsconfig_aliases joined alias targets onto the tsconfig's own directory and ignored compilerOptions.baseUrl. In the common monorepo / NestJS layout (baseUrl "./src" with "@services/*": ["services/*"]), the alias resolved to <dir>/services instead of <dir>/src/services, so every aliased import failed to resolve and the import edge was silently dropped — leaving cross-file caller graphs nearly empty on alias-heavy TS repos. Resolve `paths` relative to `baseUrl` (TypeScript's actual semantics), defaulting to "." so configs without baseUrl keep their current behavior. Add a regression test covering a subdirectory baseUrl; the existing alias test only exercised baseUrl ".", which is why this slipped through. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
cce2673021
commit
ec04152a90
@@ -2,6 +2,10 @@
|
||||
|
||||
Full release notes with details on each version: [GitHub Releases](https://github.com/safishamsi/graphify/releases)
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Fix: tsconfig `paths` aliases are now resolved relative to `baseUrl`. `_read_tsconfig_aliases` previously joined alias targets onto the tsconfig's directory and ignored `compilerOptions.baseUrl`, so the common monorepo / NestJS layout (`baseUrl: "./src"` with `"@services/*": ["services/*"]`) resolved to `<dir>/services` instead of `<dir>/src/services` and every aliased import edge was silently dropped — leaving cross-file caller graphs nearly empty on alias-heavy TypeScript codebases. Resolution now joins `paths` onto `baseUrl` (defaulting to `.`, preserving prior behavior for configs without `baseUrl`).
|
||||
|
||||
## 0.8.37 (2026-06-10)
|
||||
|
||||
- Security: SSRF guard rewritten to eliminate thread-safety race. The global `socket.getaddrinfo` monkey-patch is replaced with per-connection `_SSRFGuardedHTTPConnection`/`_SSRFGuardedHTTPSConnection` subclasses that resolve DNS once, validate the IP, and connect to that exact address — closing both the concurrent-thread race window and the underlying TOCTOU gap. No global state is mutated, so sibling threads (MCP server, PR triage pool) are unaffected.
|
||||
|
||||
+12
-2
@@ -255,13 +255,23 @@ def _read_tsconfig_aliases(tsconfig: Path, base_dir: Path, seen: set) -> dict[st
|
||||
if extended_path.exists():
|
||||
aliases.update(_read_tsconfig_aliases(extended_path, extended_path.parent, seen))
|
||||
|
||||
paths = data.get("compilerOptions", {}).get("paths", {})
|
||||
# tsconfig `paths` are resolved relative to `baseUrl` (itself relative to
|
||||
# the tsconfig's directory), not the tsconfig directory directly. Honoring
|
||||
# baseUrl is required for the common monorepo / NestJS layout where
|
||||
# baseUrl points at a subdirectory, e.g. baseUrl "./src" with
|
||||
# "@services/*": ["services/*"] must resolve to <dir>/src/services rather
|
||||
# than <dir>/services. Defaults to "." so configs without baseUrl (paths
|
||||
# relative to the tsconfig dir, the TS 4.1+ behavior) keep working.
|
||||
compiler_options = data.get("compilerOptions", {})
|
||||
base_url = compiler_options.get("baseUrl") or "."
|
||||
paths_base = base_dir / base_url
|
||||
paths = compiler_options.get("paths", {})
|
||||
for alias, targets in paths.items():
|
||||
if not targets:
|
||||
continue
|
||||
alias_prefix = alias.rstrip("/*")
|
||||
target_base = targets[0].rstrip("/*")
|
||||
aliases[alias_prefix] = str(base_dir / target_base)
|
||||
aliases[alias_prefix] = str(os.path.normpath(paths_base / target_base))
|
||||
|
||||
return aliases
|
||||
|
||||
|
||||
@@ -334,6 +334,27 @@ def test_tsconfig_alias_import_resolves_existing_ts_file(tmp_path: Path):
|
||||
assert _has_edge(result, "src/routes/page.ts", "src/lib/types/type-helpers.ts")
|
||||
|
||||
|
||||
def test_tsconfig_alias_with_subdirectory_baseurl_resolves_existing_ts_file(tmp_path: Path):
|
||||
# `paths` are resolved relative to `baseUrl`, which is commonly a
|
||||
# subdirectory in monorepo / NestJS layouts (baseUrl "./src").
|
||||
# Regression: baseUrl was ignored, so "@services/*": ["services/*"] with
|
||||
# baseUrl "./src" resolved to <root>/services instead of <root>/src/services,
|
||||
# and every aliased import edge was silently dropped.
|
||||
_write(
|
||||
tmp_path / "tsconfig.json",
|
||||
json.dumps({"compilerOptions": {"baseUrl": "./src", "paths": {"@services/*": ["services/*"]}}}),
|
||||
)
|
||||
target = _write(tmp_path / "src/services/foo/index.ts", "export class Foo { id = '' }\n")
|
||||
importer = _write(
|
||||
tmp_path / "src/routes/page.ts",
|
||||
"import { Foo } from '@services/foo'\nnew Foo()\n",
|
||||
)
|
||||
|
||||
result = _extract_for([target, importer], tmp_path)
|
||||
|
||||
assert _has_edge(result, "src/routes/page.ts", "src/services/foo/index.ts")
|
||||
|
||||
|
||||
def test_tsconfig_array_extends_alias_resolves_existing_ts_file(tmp_path: Path):
|
||||
# TypeScript 5.0 allows `extends` as an array; later entries override
|
||||
# earlier ones. The `paths` alias is inherited from the second parent.
|
||||
|
||||
Reference in New Issue
Block a user