From 5d1a803b81cf5017f60f330c8273105869f18ae0 Mon Sep 17 00:00:00 2001 From: lumir Date: Mon, 7 Sep 2026 23:24:28 +0900 Subject: [PATCH] fix: display `Map` entries in the scope inspector --- e2e-tests/tools.test.ts | 20 ++++++++++++++++++++ src/components/scope/scope-item.tsx | 11 +++++++---- src/components/tree-entry.tsx | 7 +++++-- src/lib/render-value.ts | 11 +++++++++++ 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/e2e-tests/tools.test.ts b/e2e-tests/tools.test.ts index 298e8418..b671bb9f 100644 --- a/e2e-tests/tools.test.ts +++ b/e2e-tests/tools.test.ts @@ -31,3 +31,23 @@ test("should switch to each tool and show it", async ({ page }) => { await page.getByRole("button", { name: "Code Path" }).click(); await expect(page.getByTestId("rf__background")).toBeVisible(); }); + +test("should display variables in the scope set", async ({ page }) => { + await page.goto("/"); + await page + .getByRole("textbox", { name: "Code Editor", exact: true }) + .fill("const __x = 1;"); + await page.getByRole("button", { name: "Scope", exact: true }).click(); + await page.getByRole("button", { name: "2. module", exact: true }).click(); + + const moduleScope = page.getByRole("region", { name: "2. module" }); + const setEntry = moduleScope + .getByRole("listitem") + .filter({ hasText: /^setMap\(1\)$/ }); + await expect(setEntry).toBeVisible(); + await setEntry.getByRole("button", { name: "set", exact: true }).click(); + await moduleScope.getByRole("button", { name: "__x", exact: true }).click(); + await expect( + moduleScope.getByRole("listitem").filter({ hasText: /^name__x$/ }), + ).toBeVisible(); +}); diff --git a/src/components/scope/scope-item.tsx b/src/components/scope/scope-item.tsx index 75947484..ab138e1d 100644 --- a/src/components/scope/scope-item.tsx +++ b/src/components/scope/scope-item.tsx @@ -12,7 +12,7 @@ type ScopeItemProperties = { isArray: boolean; readonly index: number; readonly path: string; - readonly data: Scope | Variable | Reference | null; + readonly data: Scope | Variable | Reference | Map | null; readonly esqueryMatchedNodes: unknown[]; }; @@ -35,6 +35,8 @@ export const ScopeItem: FC = ({ key = data.name; } else if (data instanceof Reference) { key = data.identifier.name; + } else if (data instanceof Map) { + key = "Map"; } else { key = (data as Record)?.type ?? typeof data; } @@ -42,9 +44,10 @@ export const ScopeItem: FC = ({ const isEsqueryMatchedNode = esqueryMatchedNodes.includes(data); // filter out hidden properties - const properties = Object.entries(data).filter( - ([name]) => !name.startsWith("__"), - ); + const properties = + data instanceof Map + ? [...data.entries()] + : Object.entries(data).filter(([name]) => !name.startsWith("__")); if (isArray) { return ( diff --git a/src/components/tree-entry.tsx b/src/components/tree-entry.tsx index 029e74c9..1e433348 100644 --- a/src/components/tree-entry.tsx +++ b/src/components/tree-entry.tsx @@ -101,8 +101,11 @@ export const TreeEntry: FC = ({ const [open, setOpen] = useState(false); const Icon = open ? MinusSquareIcon : PlusSquareIcon; const isToggleable = - (typeof value === "object" && Object.values(value ?? {}).length) || - (Array.isArray(value) && value.length); + value instanceof Map + ? value.size + : (typeof value === "object" && + Object.values(value ?? {}).length) || + (Array.isArray(value) && value.length); const toggleOpen = () => setOpen(!open); diff --git a/src/lib/render-value.ts b/src/lib/render-value.ts index 9f68828a..16da1e43 100644 --- a/src/lib/render-value.ts +++ b/src/lib/render-value.ts @@ -10,6 +10,17 @@ export const renderValue = (value: unknown): string[] => { ]; } + if (value instanceof Map) { + return [ + "Map", + value.size + ? value.size === 1 + ? `{${value.size} entry}` + : `{${value.size} entries}` + : "{}", + ]; + } + if (value instanceof Object && Object.keys(value).length === 0) { return ["Object", "{}"]; }