diff --git a/packages/lint/src/rules/core.test.ts b/packages/lint/src/rules/core.test.ts
index 604c0afc25..e4d7fc2814 100644
--- a/packages/lint/src/rules/core.test.ts
+++ b/packages/lint/src/rules/core.test.ts
@@ -1,35 +1,6 @@
import { describe, it, expect } from "vitest";
import { lintHyperframeHtml } from "../hyperframeLinter.js";
-function compositionWithHead(headContent: string): string {
- return `
-
-
-${headContent}
-
-
-
-
-
-`;
-}
-
-function compositionWithHeadBoundary(boundaryContent: string): string {
- return `
-
-
-
-
-${boundaryContent}
-
-
-
-
-`;
-}
-
function compositionWithBodyPrefix(prefixContent: string, rootContent = ""): string {
return `
@@ -48,34 +19,6 @@ ${rootContent}
`;
}
-function compositionWithImplicitBodyPrefix(prefixContent: string): string {
- return `
-
-
-
-
-${prefixContent}
-
-
-`;
-}
-
-function templateCompositionWithHead(headContent: string): string {
- return `
-
-
-
-${headContent}
-
-
-
-
-
-`;
-}
-
describe("core rules", () => {
it("does not lint scripts embedded inside an iframe srcdoc attribute", async () => {
const html = `
@@ -218,7 +161,7 @@ describe("core rules", () => {
// Regression: a CSS comment referencing an SVG tag name (e.g. `/* wrapper */`)
// inside a
-
- /* Decorative Elements */
- .particle {
- position: absolute;
- width: 4px;
- height: 4px;
- background: #fff;
- }
-`);
- const result = await lintHyperframeHtml(html);
- const finding = result.findings.find((f) => f.code === "head_leaked_text");
-
- expect(finding).toBeDefined();
- expect(finding?.severity).toBe("error");
- expect(finding?.message).toContain("");
- expect(finding?.snippet).toContain(".particle");
- });
-
- it("reports error when CSS variables leak between head and body", async () => {
- const html = compositionWithHeadBoundary(`
- --bg-color: #F5F1E8;
- --text-color: #212121;
-}
-
-body {
- background-color: var(--bg-color);
- color: var(--text-color);
-}
-`);
- const result = await lintHyperframeHtml(html);
- const finding = result.findings.find((f) => f.code === "head_leaked_text");
-
- expect(finding).toBeDefined();
- expect(finding?.message).toContain("");
- expect(finding?.snippet).toContain("body");
- });
-
- it("reports error when stray close tags leak between head and body", async () => {
- const html = compositionWithHeadBoundary(`
-
-
-`);
- const result = await lintHyperframeHtml(html);
- const finding = result.findings.find((f) => f.code === "head_leaked_text");
-
- expect(finding).toBeDefined();
- expect(finding?.snippet).toContain("");
- });
-
- it("reports error when markdown code fences leak between head and body", async () => {
- const html = compositionWithHeadBoundary(`
- \`\`\`css
- .particle {
- color: white;
- }
- \`\`\`
-`);
- const result = await lintHyperframeHtml(html);
- const finding = result.findings.find((f) => f.code === "head_leaked_text");
-
- expect(finding).toBeDefined();
- expect(finding?.snippet).toContain("```css");
- });
-
- it("reports error when CSS at-rules leak between head and body", async () => {
- const html = compositionWithHeadBoundary(`
- @media (min-width: 800px) {
- .particle {
- transform: scale(1.2);
- }
- }
-`);
- const result = await lintHyperframeHtml(html);
- const finding = result.findings.find((f) => f.code === "head_leaked_text");
-
- expect(finding).toBeDefined();
- expect(finding?.snippet).toContain("@media");
- });
-
- it("does not report leaked text for valid script and style blocks around the head boundary", async () => {
- const html = compositionWithHeadBoundary(`
-
-
-
-
-`);
- const result = await lintHyperframeHtml(html);
- const finding = result.findings.find((f) => f.code === "head_leaked_text");
-
- expect(finding).toBeUndefined();
- });
-
- it("reports error when CSS text leaks before the composition root", async () => {
- const html = compositionWithBodyPrefix(`
- .orphan {
- position: absolute;
- inset: 0;
- }
-`);
- const result = await lintHyperframeHtml(html);
- const finding = result.findings.find((f) => f.code === "head_leaked_text");
-
- expect(finding).toBeDefined();
- expect(finding?.snippet).toContain(".orphan");
- });
-
- it("reports error when CSS text leaks before the composition root without an explicit body", async () => {
- const html = compositionWithImplicitBodyPrefix(`
- .implicit-body-orphan {
- position: absolute;
- inset: 0;
- }
-`);
- const result = await lintHyperframeHtml(html);
- const finding = result.findings.find((f) => f.code === "head_leaked_text");
-
- expect(finding).toBeDefined();
- expect(finding?.snippet).toContain(".implicit-body-orphan");
- });
-
- it("does not report leaked text for valid script and style blocks before the composition root", async () => {
- const html = compositionWithBodyPrefix(`
-
-
-`);
- const result = await lintHyperframeHtml(html);
- const finding = result.findings.find((f) => f.code === "head_leaked_text");
-
- expect(finding).toBeUndefined();
- });
-
- it("does not report CSS-looking educational text inside the composition root", async () => {
- const html = compositionWithBodyPrefix(
- "",
- `
-
- body {
- margin: 0;
- }
-
-`,
- );
- const result = await lintHyperframeHtml(html);
- const finding = result.findings.find((f) => f.code === "head_leaked_text");
-
- expect(finding).toBeUndefined();
- });
-
it("reports error when CSS block comment syntax leaks into visible markup", async () => {
const html = compositionWithBodyPrefix(
"",
@@ -585,175 +366,6 @@ body {
expect(finding).toBeUndefined();
});
- it("reports error when a stray style close tag is left in the document head", async () => {
- const html = compositionWithHead(`
-
-
-`);
- const result = await lintHyperframeHtml(html);
- const finding = result.findings.find((f) => f.code === "head_leaked_text");
-
- expect(finding).toBeDefined();
- expect(finding?.snippet).toContain("");
- });
-
- it("reports error when a stray script close tag is left in the document head", async () => {
- const html = compositionWithHead(`
-
-
-`);
- const result = await lintHyperframeHtml(html);
- const finding = result.findings.find((f) => f.code === "head_leaked_text");
-
- expect(finding).toBeDefined();
- expect(finding?.snippet).toContain("");
- });
-
- it("does not report leaked head text for valid closing tags with trailing whitespace", async () => {
- const html = compositionWithHead(`
-
-
- Particle Field
-`);
- const result = await lintHyperframeHtml(html);
- const finding = result.findings.find((f) => f.code === "head_leaked_text");
-
- expect(finding).toBeUndefined();
- });
-
- it("reports error when markdown code fences leak into the document head", async () => {
- const withLanguage = compositionWithHead(`
- \`\`\`css
- .particle {
- position: absolute;
- }
- \`\`\`
-`);
- const withoutLanguage = compositionWithHead(`
- \`\`\`
- .particle {
- position: absolute;
- }
- \`\`\`
-`);
- const withTsxLanguage = compositionWithHead(`
- \`\`\`tsx
- export function Particle() {
- return ;
- }
- \`\`\`
-`);
- const withLanguageResult = await lintHyperframeHtml(withLanguage);
- const withoutLanguageResult = await lintHyperframeHtml(withoutLanguage);
- const withTsxLanguageResult = await lintHyperframeHtml(withTsxLanguage);
- const languageFinding = withLanguageResult.findings.find((f) => f.code === "head_leaked_text");
- const unlabeledFinding = withoutLanguageResult.findings.find(
- (f) => f.code === "head_leaked_text",
- );
- const tsxLanguageFinding = withTsxLanguageResult.findings.find(
- (f) => f.code === "head_leaked_text",
- );
-
- expect(languageFinding).toBeDefined();
- expect(languageFinding?.snippet).toContain("```css");
- expect(unlabeledFinding).toBeDefined();
- expect(unlabeledFinding?.snippet).toContain("```");
- expect(tsxLanguageFinding).toBeDefined();
- expect(tsxLanguageFinding?.snippet).toContain("```tsx");
- });
-
- it("reports error when CSS at-rules leak into the document head", async () => {
- const html = compositionWithHead(`
- @media (min-width: 800px) {
- .particle {
- transform: scale(1.2);
- }
- }
-`);
- const result = await lintHyperframeHtml(html);
- const finding = result.findings.find((f) => f.code === "head_leaked_text");
-
- expect(finding).toBeDefined();
- expect(finding?.snippet).toContain("@media");
- });
-
- it("reports leaked CSS when a style block is unclosed in the document head", async () => {
- const html = compositionWithHead(`
-
-`);
- const result = await lintHyperframeHtml(html);
- const finding = result.findings.find((f) => f.code === "head_leaked_text");
-
- expect(finding).toBeUndefined();
- });
-
- it("reports leaked head text inside template-wrapped sub-compositions", async () => {
- const html = templateCompositionWithHead(`
-
- .particle { color: white; }
-`);
- const result = await lintHyperframeHtml(html, { isSubComposition: true });
- const finding = result.findings.find((f) => f.code === "head_leaked_text");
-
- expect(finding).toBeDefined();
- expect(finding?.snippet).toContain(".particle");
- });
-
describe("timeline_id_mismatch", () => {
it("accepts dot timeline registration", async () => {
const html = `
diff --git a/packages/lint/src/rules/core.ts b/packages/lint/src/rules/core.ts
index bb3eecaeff..de31a2cfc8 100644
--- a/packages/lint/src/rules/core.ts
+++ b/packages/lint/src/rules/core.ts
@@ -130,17 +130,6 @@ function describeStudioElement(tag: { raw: string; name: string }): string {
return parts.join("");
}
-const HEAD_BLOCKS_TO_IGNORE_PATTERN =
- /<(?:style|script|template|title|noscript)\b[^>]*>[\s\S]*?<\/(?:style|script|template|title|noscript)(?:\s[^>]*)?>/gi;
-const HTML_TAG_PATTERN = /<[^>]+>/g;
-const HEAD_CONTENT_PATTERN = /]*>([\s\S]*?)(?:<\/head>|]*)?>([\s\S]*?)(?=]*)?>/i;
-const MARKDOWN_CODE_FENCE_PATTERN = /```[^\r\n`]*(?:\r?\n|$)[\s\S]*?```/i;
-const ORPHAN_CSS_AT_RULE_PATTERN =
- /(?:^|\s)@(?:container|font-face|keyframes|layer|media|page|property|scope|supports)[^{<]*\{[\s\S]*?:[\s\S]*?\}/i;
-const ORPHAN_CSS_RULE_PATTERN =
- /(?:^|\s)(?:\/\*[\s\S]*?\*\/\s*)?(?:@[a-z-]+[^{}<]*|[.#][\w-]+[^{}<]*|[a-z][\w-]*(?:\s+[.#:[\w-][^{}<]*)?)\s*\{[^{}]*:[^{}]*\}/i;
const VISIBLE_MARKUP_COMMENT_PATTERN = /\/\*[\s\S]*?\*\//g;
const VISIBLE_MARKUP_COMMENT_PROTECTED_BLOCK_PATTERN =
/<(style|script|template|title|noscript|pre|code|textarea|text)\b[^>]*>[\s\S]*?<\/\1(?:\s[^>]*)?>/gi;
@@ -150,64 +139,6 @@ interface SourceRange {
end: number;
}
-function findCodeFenceLeak(headWithoutValidBlocks: string): string | null {
- return MARKDOWN_CODE_FENCE_PATTERN.exec(headWithoutValidBlocks)?.[0] ?? null;
-}
-
-function findOrphanCssLeak(headContent: string): string | null {
- const residualText = headContent
- .replace(HEAD_BLOCKS_TO_IGNORE_PATTERN, " ")
- .replace(HTML_TAG_PATTERN, " ");
- return (
- ORPHAN_CSS_AT_RULE_PATTERN.exec(residualText)?.[0] ??
- ORPHAN_CSS_RULE_PATTERN.exec(residualText)?.[0] ??
- null
- );
-}
-
-function findStrayCloseLeak(headWithoutValidBlocks: string): string | null {
- return STRAY_HEAD_CLOSE_PATTERN.exec(headWithoutValidBlocks)?.[0] ?? null;
-}
-
-function findLeakedTextInHeadContent(headContent: string): string | null {
- const withoutValidBlocks = headContent.replace(HEAD_BLOCKS_TO_IGNORE_PATTERN, " ");
- return (
- findCodeFenceLeak(withoutValidBlocks) ??
- findOrphanCssLeak(headContent) ??
- findStrayCloseLeak(withoutValidBlocks)
- );
-}
-
-function findLeakedTextInHead(rawSource: string): string | null {
- const headMatches = [...rawSource.matchAll(HEAD_CONTENT_PATTERN)];
- for (const match of headMatches) {
- const leakedText = findLeakedTextInHeadContent(match[1] ?? "");
- if (leakedText) return leakedText;
- }
- return null;
-}
-
-function findLeakedTextBetweenHeadAndBody(rawSource: string): string | null {
- const boundaryMatches = [...rawSource.matchAll(AFTER_HEAD_BEFORE_BODY_PATTERN)];
- for (const match of boundaryMatches) {
- const leakedText = findLeakedTextInHeadContent(match[1] ?? "");
- if (leakedText) return leakedText;
- }
- return null;
-}
-
-function findLeakedTextBeforeCompositionRoot(
- source: string,
- rootTag: LintContext["rootTag"],
-): string | null {
- if (!rootTag || rootTag.name === "body") return null;
- const bodyOpenMatch = /]*>/i.exec(source);
- const prefixStart = bodyOpenMatch ? bodyOpenMatch.index + bodyOpenMatch[0].length : 0;
- const prefixEnd = rootTag.index;
- if (prefixEnd <= prefixStart) return null;
- return findLeakedTextInHeadContent(source.slice(prefixStart, prefixEnd));
-}
-
function findProtectedVisibleMarkupRanges(source: string): SourceRange[] {
const ranges: SourceRange[] = [];
for (const match of source.matchAll(VISIBLE_MARKUP_COMMENT_PROTECTED_BLOCK_PATTERN)) {
@@ -298,26 +229,6 @@ export const coreRules: Array<(ctx: LintContext) => HyperframeLintFinding[]> = [
return findings;
},
- // head_leaked_text
- ({ source, rootTag }) => {
- const snippet =
- findLeakedTextInHead(source) ??
- findLeakedTextBetweenHeadAndBody(source) ??
- findLeakedTextBeforeCompositionRoot(source, rootTag);
- if (!snippet) return [];
- return [
- {
- code: "head_leaked_text",
- severity: "error",
- message:
- "Detected leaked code or CSS text around the document `` or before the composition root. Browsers render this as visible text in the video.",
- fixHint:
- "Move CSS into a single `` block and remove stray close tags, markdown fences, or code text from ``, the ``/`` boundary, or the pre-root body prefix.",
- snippet: truncateSnippet(snippet),
- },
- ];
- },
-
// visible_markup_comment
({ source }) => {
const snippet = findVisibleMarkupCommentLeak(source);