mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-01 19:42:03 +00:00
fix(lint): stop duplicate_composition_id firing on repeated sub-composition mounts (#3404)
sub-compositions.md documents mounting one sub-composition several times with different data-variable-values to get per-instance variations. That necessarily repeats data-composition-id, so the rule reported our own documented pattern as an error and blocked check with no correct way to satisfy it. The rule bucketed every element by id with no awareness of data-composition-src, so it could not tell a composition root from a mount. The runtime already distinguishes them: repeated mounts are rewritten to id__hf1, id__hf2 so they coexist, and render, validate, inspect and snapshot all handle the pattern. Skip mounts, the same way the rule already skips tags inside an inert template. The collision it exists for is unaffected: its own fixHint names a <meta> tag carrying the root's id, and that tag has no data-composition-src. Closes #3403
This commit is contained in:
@@ -315,6 +315,50 @@ describe("composition rules", () => {
|
||||
expect(finding).toBeUndefined();
|
||||
});
|
||||
|
||||
it("does not flag one sub-composition mounted repeatedly with per-instance values", async () => {
|
||||
// Regression: sub-compositions.md "Per-Instance Variables" documents
|
||||
// mounting one source several times with different data-variable-values.
|
||||
// That necessarily repeats the id, and the runtime rewrites repeated
|
||||
// mounts to `id__hf1`/`id__hf2` so they coexist. Flagging it made the
|
||||
// documented pattern an error with no correct way to satisfy it.
|
||||
const html = `<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<div data-composition-id="main" data-width="1920" data-height="1080" data-start="0" data-duration="6" data-no-timeline>
|
||||
<div data-composition-id="word" data-composition-src="compositions/word-caption.html" data-variable-values='{"text":"one"}' data-start="0" data-duration="2"></div>
|
||||
<div data-composition-id="word" data-composition-src="compositions/word-caption.html" data-variable-values='{"text":"two"}' data-start="2" data-duration="2"></div>
|
||||
<div data-composition-id="word" data-composition-src="compositions/word-caption.html" data-variable-values='{"text":"three"}' data-start="4" data-duration="2"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
const result = await lintHyperframeHtml(html);
|
||||
expect(result.findings.find((f) => f.code === "duplicate_composition_id")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("still flags a real collision between a root and a non-mount element", async () => {
|
||||
// The guard that keeps the exemption honest: skipping mounts must not
|
||||
// blind the rule to the meta-versus-root collision it exists for, even
|
||||
// when a legitimately repeated mount is present in the same file.
|
||||
const html = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="composition-id" data-composition-id="main">
|
||||
</head>
|
||||
<body>
|
||||
<div data-composition-id="main" data-width="1920" data-height="1080" data-start="0" data-duration="4" data-no-timeline>
|
||||
<div data-composition-id="word" data-composition-src="compositions/word-caption.html" data-start="0" data-duration="2"></div>
|
||||
<div data-composition-id="word" data-composition-src="compositions/word-caption.html" data-start="2" data-duration="2"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
const result = await lintHyperframeHtml(html);
|
||||
const finding = result.findings.find((f) => f.code === "duplicate_composition_id");
|
||||
expect(finding).toBeDefined();
|
||||
expect(finding?.message).toContain("main");
|
||||
});
|
||||
|
||||
it("ignores composition ids inside inert template content", async () => {
|
||||
const html = `<!DOCTYPE html>
|
||||
<html><body>
|
||||
|
||||
@@ -262,6 +262,16 @@ export const compositionRules: Array<(ctx: LintContext) => HyperframeLintFinding
|
||||
const tagsByCompositionId = new Map<string, string[]>();
|
||||
for (const tag of tags) {
|
||||
if (isInsideInertTemplate(tag, tags)) continue;
|
||||
// A `data-composition-src` element is a MOUNT of a sub-composition, not a
|
||||
// composition root, and sub-compositions.md documents mounting one source
|
||||
// repeatedly with different `data-variable-values` to get per-instance
|
||||
// variations. Those mounts legitimately share an id: the runtime rewrites
|
||||
// repeated ones to `id__hf1`, `id__hf2` so they coexist. Counting them
|
||||
// here made the documented pattern an error with no correct way to
|
||||
// satisfy it. The collision this rule exists for -- a <meta> tag carrying
|
||||
// the root's id, per its own fixHint -- is unaffected, since that tag has
|
||||
// no `data-composition-src`.
|
||||
if (readAttr(tag.raw, "data-composition-src")) continue;
|
||||
const compositionId = readDecodedAttr(tag.raw, "data-composition-id");
|
||||
if (!compositionId || compositionId.trim().length === 0) continue;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user