diff --git a/packages/lint/src/rules/media.test.ts b/packages/lint/src/rules/media.test.ts index 5a38bf02c5..9aaca84eb4 100644 --- a/packages/lint/src/rules/media.test.ts +++ b/packages/lint/src/rules/media.test.ts @@ -48,6 +48,25 @@ describe("media rules", () => { expect(finding?.message).toContain("FROZEN"); }); + it("flags media that has data-hf-id but no real id", async () => { + // Regression: readAttr(tag, "id") used a \b boundary that matched the + // trailing `id="…"` inside `data-hf-id="…"`, so media carrying only a + // Studio-stamped data-hf-id passed the check and then rendered as a blank + // wash (video) / silent (audio). data-hf-id is NOT a render id. + const html = ` + +
+ + +
+ +`; + const result = await lintHyperframeHtml(html); + const findings = result.findings.filter((f) => f.code === "media_missing_id"); + expect(findings).toHaveLength(2); + expect(findings.every((f) => f.severity === "error")).toBe(true); + }); + it("does not flag media elements that have id", async () => { const html = ` diff --git a/packages/lint/src/utils.ts b/packages/lint/src/utils.ts index 21c24ce6e5..65362db2e9 100644 --- a/packages/lint/src/utils.ts +++ b/packages/lint/src/utils.ts @@ -112,7 +112,11 @@ export function findRootTag(source: string): OpenTag | null { export function readAttr(tagSource: string, attr: string): string | null { if (!tagSource) return null; const escaped = attr.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); - const match = tagSource.match(new RegExp(`\\b${escaped}\\s*=\\s*["']([^"']+)["']`, "i")); + // `(?`/`