fix(lint): exempt caption cues from track density (#2461)

* fix(lint): exempt caption cues from track density

* style(media-use): satisfy resolver formatting gate
This commit is contained in:
Miguel Ángel
2026-07-15 02:05:39 -04:00
committed by GitHub
parent 7cc10a9922
commit f3800f3579
2 changed files with 28 additions and 0 deletions
@@ -173,6 +173,22 @@ describe("composition rules", () => {
expect(finding).toBeUndefined();
});
it("does not count transcript caption cues as dense track elements", async () => {
const html = `<!DOCTYPE html>
<html><body>
<div data-composition-id="main" data-width="1080" data-height="1920" data-start="0">
<div class="caption-group clip" data-start="0" data-duration="1" data-track-index="2">一</div>
<div class="caption-line clip" data-start="1" data-duration="1" data-track-index="2">二</div>
<div class="caption_block clip" data-start="2" data-duration="1" data-track-index="2">三</div>
<div class="cg-4 clip" data-start="3" data-duration="1" data-track-index="2">四</div>
</div>
</body></html>`;
const result = await lintHyperframeHtml(html, { filePath: "/project/index.html" });
const finding = result.findings.find((f) => f.code === "timeline_track_too_dense");
expect(finding).toBeUndefined();
});
it("does not count root composition or mounted sub-compositions as dense elements", async () => {
const html = `<!DOCTYPE html>
<html><body>
+12
View File
@@ -15,6 +15,8 @@ import { COMPOSITION_VARIABLE_TYPES } from "@hyperframes/parsers/composition";
const MAX_COMPOSITION_LINES = 300;
const MAX_TIMED_ELEMENTS_PER_TRACK = 3;
const TRACK_DENSITY_EXEMPT_TAGS = new Set(["audio", "script", "style", "video"]);
const CAPTION_CUE_TOKEN =
/^(?:caption(?:[-_](?:group|word|line|block|cue|text))?|subtitle(?:[-_](?:group|line|cue|text))?|cg-.+)$/i;
// `parseFloat("0.1") + parseFloat("0.2") = 0.30000000000000004`. Sub-second
// authored adjacencies survive parse + add as a value a few ulps above the
@@ -36,6 +38,15 @@ function countStructuralLines(source: string): number {
return countPhysicalLines(source.replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, "<style></style>"));
}
function isCaptionCue(tag: OpenTag): boolean {
const classTokens = (readAttr(tag.raw, "class") || "").split(/\s+/).filter(Boolean);
const id = readAttr(tag.raw, "id");
return (
classTokens.some((token) => CAPTION_CUE_TOKEN.test(token)) ||
Boolean(id && CAPTION_CUE_TOKEN.test(id))
);
}
export function isRegistrySourceFile(filePath?: string): boolean {
if (!filePath) return false;
@@ -294,6 +305,7 @@ export const compositionRules: Array<(ctx: LintContext) => HyperframeLintFinding
const trackCounts = new Map<string, number>();
for (const tag of tags) {
if (TRACK_DENSITY_EXEMPT_TAGS.has(tag.name)) continue;
if (isCaptionCue(tag)) continue;
if (isCompositionRootOrMount(tag.raw)) continue;
if (!readAttr(tag.raw, "data-start")) continue;