diff --git a/packages/lint/src/rules/composition.test.ts b/packages/lint/src/rules/composition.test.ts index 8f7cc9511..701807f23 100644 --- a/packages/lint/src/rules/composition.test.ts +++ b/packages/lint/src/rules/composition.test.ts @@ -1049,6 +1049,24 @@ describe("composition rules", () => { expect(finding).toBeUndefined(); }); + it("does not warn for an HTML-entity-encoded declarations array", async () => { + const declarations = JSON.stringify([ + { + id: "title", + type: "string", + label: "Title", + description: 'A "quoted" title', + default: "Hello", + }, + ]).replaceAll('"', """); + const html = `
`; + const result = await lintHyperframeHtml(html); + const finding = result.findings.find( + (f) => f.code === "invalid_composition_variables_declaration", + ); + expect(finding).toBeUndefined(); + }); + it("does not warn when data-composition-variables is absent", async () => { const html = ``; const result = await lintHyperframeHtml(html); diff --git a/packages/lint/src/utils.ts b/packages/lint/src/utils.ts index 9823d9033..4487d7f81 100644 --- a/packages/lint/src/utils.ts +++ b/packages/lint/src/utils.ts @@ -194,27 +194,13 @@ export function readDecodedAttr(tagSource: string, attr: string): string | null } /** - * Read an attribute that may legitimately contain the opposite quote - * character. `readAttr` truncates `data-variable-values='{"title":"Hello"}'` - * at the first internal `"` because its `[^"']+` class excludes both quote - * types. This variant alternates: a double-quoted value never contains an - * unescaped `"`, and a single-quoted value never contains an unescaped `'`, - * so each branch can use a quote-specific class. - * - * Use for attributes whose values are JSON or otherwise carry the opposite - * quote character. Existing single-token attributes (`id`, `class`, etc.) - * stick with `readAttr` for consistency with the rest of the lint code. + * Read a JSON-bearing attribute with browser-equivalent character-reference + * decoding. Imported or formatter-serialized HTML commonly stores JSON quotes + * as `"`; lint must inspect the same decoded value that `getAttribute()` + * exposes at runtime. */ export function readJsonAttr(tagSource: string, attr: string): string | null { - if (!tagSource) return null; - const escaped = attr.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); - // See readAttr: `(? {