From 025caeaa939524864116ec7fcd0138b28ead8c25 Mon Sep 17 00:00:00 2001 From: ayushcodes10 Date: Mon, 14 Sep 2026 14:46:23 +0530 Subject: [PATCH] Add a test for the per name destructured export split Four destructured names on one export statement must each get their own node, and the old combined pattern syntax label must not survive anywhere in the output. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh (cherry picked from commit 3f62c3d85c0d868101e972f353f9a2d62e9c93a3) --- tests/test_js_destructured_export.py | 41 ++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 tests/test_js_destructured_export.py diff --git a/tests/test_js_destructured_export.py b/tests/test_js_destructured_export.py new file mode 100644 index 000000000..8dc75e9f5 --- /dev/null +++ b/tests/test_js_destructured_export.py @@ -0,0 +1,41 @@ +"""#2604 Class 2: an exported object-destructure assignment must emit one node +per exported name, not one combined node for the whole pattern. + +`export const { auth, handlers } = NextAuth(config)` is NextAuth v5's own +documented boilerplate (next-intl's `createNavigation()` is the same shape). +Before this fix it collapsed to a single node whose id joined every +destructured name (`stem_auth_handlers`) and whose label was the literal +pattern syntax (`"{ auth, handlers }"`). A single-name import elsewhere +(`import { auth } from './auth'`) looks for a node named `auth` alone, which +never existed, so the import edge -- and any call resolved through it -- +dangled. +""" +from __future__ import annotations + +from graphify.extract import extract + +_NEXTAUTH_HELPER = "function NextAuth(config) { return {}; }\n" + + +def _extract(tmp_path, files: dict[str, str]): + for name, body in files.items(): + p = tmp_path / name + p.parent.mkdir(parents=True, exist_ok=True) + p.write_text(body) + r = extract([tmp_path / n for n in files], + cache_root=tmp_path / "graphify-out", parallel=False) + lbl = {n["id"]: n["label"] for n in r["nodes"]} + return r, lbl + + +def test_exported_destructure_emits_one_node_per_name(tmp_path): + r, lbl = _extract(tmp_path, { + "auth.ts": _NEXTAUTH_HELPER + + "export const { auth, handlers, signIn, signOut } = NextAuth({});\n", + }) + labels = set(lbl.values()) + for name in ("auth", "handlers", "signIn", "signOut"): + assert name in labels, f"no node for exported name {name!r}; got {sorted(labels)}" + assert not any(label.startswith("{") for label in labels), ( + f"a combined-pattern label survived: {sorted(labels)}" + )