import { expect, test } from "bun:test"; import { parse } from "../src/index.ts"; // One literal backslash character. Composing the .wrn sources below by // concatenation keeps the escaping unambiguous at a glance. const BS = String.fromCharCode(92); const Q = '"'; const view = (attr: string) => `page Esc {\n view {\n
\n
x
\n
\n }\n}\n`; const attrValue = (source: string): string => { const ast = parse(source) as unknown; const found: string[] = []; const walk = (node: unknown): void => { if (!node || typeof node !== "object") return; if (Array.isArray(node)) { node.forEach(walk); return; } const record = node as Record; if (typeof record.value === "string" && typeof record.name === "string") { found.push(record.value); } for (const key of Object.keys(record)) walk(record[key]); }; walk(ast); return found[found.length - 1] ?? ""; }; test("a backslash-escaped delimiter becomes a bare quote", () => { // api="fn({ a: \"b\" })" -> fn({ a: "b" }) const attr = `api=${Q}fn({ a: ${BS}${Q}b${BS}${Q} })${Q}`; expect(attrValue(view(attr))).toBe(`fn({ a: ${Q}b${Q} })`); }); test("a doubled backslash collapses to exactly one", () => { // data-x="a\\b" -> a\b const attr = `data-x=${Q}a${BS}${BS}b${Q}`; expect(attrValue(view(attr))).toBe(`a${BS}b`); }); test("a lone backslash before an ordinary character is preserved", () => { // data-path="C:\Users\name" must survive the lexer intact. const attr = `data-path=${Q}C:${BS}Users${BS}name${Q}`; expect(attrValue(view(attr))).toBe(`C:${BS}Users${BS}name`); }); test("a lone backslash before an escape-looking character is preserved verbatim", () => { // data-x="a\nb" is two characters a, backslash, n, b -- not a newline. const attr = `data-x=${Q}a${BS}nb${Q}`; expect(attrValue(view(attr))).toBe(`a${BS}nb`); });