From 8be72ef7bf9ca6648521b3160a1adbb843ee644d Mon Sep 17 00:00:00 2001 From: rohit-jsfreaky Date: Wed, 12 Aug 2026 14:20:21 +0100 Subject: [PATCH] fix(js): stabilize unresolved local import target ids (#2457) --- graphify/extract.py | 6 ++++++ tests/test_js_import_resolution.py | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/graphify/extract.py b/graphify/extract.py index bfdaba7c..c8a4f0bb 100644 --- a/graphify/extract.py +++ b/graphify/extract.py @@ -413,6 +413,12 @@ def _import_js(node, source: bytes, file_nid: str, stem: str, edges: list, str_p resolved = _resolve_js_import_target(raw, str_path) if resolved is not None: tgt_nid, resolved_path = resolved + # `_resolve_js_import_path` returns the attempted path when no + # local file exists. Static ES imports must treat that as unresolved + # rather than minting a checkout-specific target ID (#2457). + if resolved_path is not None and not resolved_path.is_file(): + tgt_nid = _make_id("ref", raw) + resolved_path = None edge = { "source": file_nid, "target": tgt_nid, diff --git a/tests/test_js_import_resolution.py b/tests/test_js_import_resolution.py index d62c4458..cc464e4b 100644 --- a/tests/test_js_import_resolution.py +++ b/tests/test_js_import_resolution.py @@ -1017,6 +1017,29 @@ def test_tsconfig_alias_none_exist_creates_no_false_edge(tmp_path: Path): assert not _has_edge(result, "src/routes/page.ts", "src/lib/utils.ts") +def test_unresolved_relative_import_uses_stable_ref_target(tmp_path: Path): + """A missing local module must not leak the checkout path into graph IDs (#2457).""" + importer = _write( + tmp_path / "src/consumer.ts", + "import { getFoo } from './generated/api'\n" + "export function run(): number { return getFoo() }\n", + ) + + result = _extract_for([importer], tmp_path) + source = _file_node_id(Path("src/consumer.ts")) + imports_from = [ + edge["target"] + for edge in result["edges"] + if edge["source"] == source and edge["relation"] == "imports_from" + ] + + assert imports_from == [_make_id("ref", "./generated/api")] + assert not any( + edge["source"] == source and edge["relation"] == "imports" + for edge in result["edges"] + ) + + # ── #927: wildcard tsconfig path patterns ────────────────────────────────────