", "data-start")).toBeNull();
+ expect(readAttr("", "data-start")).toBeNull();
+ });
+
+ it("treats an empty quoted value as absent, as before", () => {
+ expect(readAttr('
', "data-start")).toBeNull();
+ });
+});
diff --git a/packages/lint/src/utils.ts b/packages/lint/src/utils.ts
index 39b2315381..7a0f31ff94 100644
--- a/packages/lint/src/utils.ts
+++ b/packages/lint/src/utils.ts
@@ -162,15 +162,15 @@ export function findRootTag(source: string, parsedTags?: readonly OpenTag[]): Op
return null;
}
+/**
+ * Read an HTML attribute the way the runtime does. Parser-backed rather than a
+ * regex over the tag text: a regex cannot see unquoted values (valid HTML5, and
+ * the runtime honours them), so it both invents "missing attribute" errors and
+ * hides real violations — and widening it to accept unquoted values makes it
+ * capture `name=value` pairs out of other attributes' quoted values instead.
+ */
export function readAttr(tagSource: string, attr: string): string | null {
- if (!tagSource) return null;
- const escaped = attr.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
- // `(?