fix(extract_dart): use _file_stem instead of str(path) for child node IDs to prevent machine-specific absolute paths in graph.json (#999)

This commit is contained in:
Alexey Z
2026-05-26 20:21:27 +01:00
committed by GitHub
parent 2c01a89b28
commit baaab5f2a9
2 changed files with 42 additions and 2 deletions
+4 -2
View File
@@ -3110,6 +3110,8 @@ def extract_dart(path: Path) -> dict:
except OSError:
return {"error": f"cannot read {path}"}
# Use stem (not str(path)) for child IDs to keep them machine-independent.
stem = _file_stem(path)
file_nid = _make_id(str(path))
nodes = [{"id": file_nid, "label": path.name, "file_type": "code",
"source_file": str(path), "source_location": None}]
@@ -3118,7 +3120,7 @@ def extract_dart(path: Path) -> dict:
# Classes and mixins
for m in re.finditer(r"^\s*(?:abstract\s+)?(?:class|mixin)\s+(\w+)", src, re.MULTILINE):
nid = _make_id(str(path), m.group(1))
nid = _make_id(stem, m.group(1))
if nid not in defined:
nodes.append({"id": nid, "label": m.group(1), "file_type": "code",
"source_file": str(path), "source_location": None})
@@ -3132,7 +3134,7 @@ def extract_dart(path: Path) -> dict:
name = m.group(1)
if name in {"if", "for", "while", "switch", "catch", "return"}:
continue
nid = _make_id(str(path), name)
nid = _make_id(stem, name)
if nid not in defined:
nodes.append({"id": nid, "label": name, "file_type": "code",
"source_file": str(path), "source_location": None})
+38
View File
@@ -1022,3 +1022,41 @@ def test_pure_export_no_from_not_treated_as_reexport():
result = extract_js(Path(f.name))
reexports = [e for e in result["edges"] if e["relation"] == "re_exports"]
assert reexports == [], f"Pure export should not create re_exports: {reexports}"
def test_dart_child_node_ids_are_stem_based(tmp_path):
"""Dart child node IDs must be built from _file_stem rather than absolute path."""
from graphify.extract import extract_dart, _file_stem, _make_id
src_file = tmp_path / "mydir" / "sample.dart"
src_file.parent.mkdir(parents=True, exist_ok=True)
src_file.write_bytes(b"class MyClass {}\nvoid myFunc() {}\n")
result = extract_dart(src_file)
stem = _file_stem(src_file) # -> "mydir.sample"
expected_class_nid = _make_id(stem, "MyClass") # -> "mydir_sample_myclass"
expected_func_nid = _make_id(stem, "myFunc") # -> "mydir_sample_myfunc"
node_ids = {n["id"] for n in result["nodes"]}
assert expected_class_nid in node_ids, (
f"Class node ID '{expected_class_nid}' not found in {node_ids}. "
"extract_dart may still be using str(path) instead of _file_stem(path)."
)
assert expected_func_nid in node_ids, (
f"Function node ID '{expected_func_nid}' not found in {node_ids}. "
"extract_dart may still be using str(path) instead of _file_stem(path)."
)
# Sanity-check: no child node ID should contain any path separator fragment.
file_nid = next(n["id"] for n in result["nodes"] if n.get("label") == src_file.name)
for node in result["nodes"]:
if node["id"] == file_nid:
continue
assert "_" + stem.replace(".", "_") in node["id"] or node["id"].startswith(stem.replace(".", "_")), (
f"Child node ID '{node['id']}' does not start with the expected stem prefix '{stem}'. "
"This suggests an absolute path is still leaking into the ID."
)