diff --git a/packages/core/src/compiler/htmlDocument.test.ts b/packages/core/src/compiler/htmlDocument.test.ts index 615fd6c66c..3f1138e453 100644 --- a/packages/core/src/compiler/htmlDocument.test.ts +++ b/packages/core/src/compiler/htmlDocument.test.ts @@ -5,6 +5,7 @@ import { injectScriptsIntoHtml, injectTagsAtHeadStart, insertBeforeCloseTag, + hasCompositionOutsideTemplates, isFullHtmlDocument, parseHTMLContent, stripEmbeddedRuntimeScripts, @@ -272,3 +273,47 @@ describe("injectTagsAtHeadStart on long adversarial input", () => { expect(performance.now() - started).toBeLessThan(1500); }); }); + +describe("hasCompositionOutsideTemplates", () => { + it.each([ + ["a root in ", '
', true], + [ + "a root in beside an inline template", + '
', + true, + ], + [ + "a full document whose composition is in its template", + '', + false, + ], + ["a bare template", '', false], + [ + "an id on and on the "); + expect(html.indexOf('window.__timelines["sub"]')).toBeGreaterThan(-1); + expect(html.lastIndexOf("")).toBeLessThan(close); + expect(html.slice(close)).toBe("\n"); + }); + + it("a first animation lands inside the template when carries the composition id", async () => { + const html = await addFirstAnimation( + '\n', + ); + const close = html.indexOf(""); + expect(html.lastIndexOf("")).toBeLessThan(close); + expect(html.slice(close)).toBe("\n"); + }); + + it("a first animation lands inside the template when the "); + expect(html.lastIndexOf("")).toBeLessThan(close); + expect(html.slice(close)).toBe("\n"); + }); + + it("a first animation in a file whose composition is in lands before ", async () => { + const html = await addFirstAnimation( + '
\n', + ); + expect(html.indexOf('window.__timelines["main"]')).toBeGreaterThan(html.indexOf("")); expect(html.lastIndexOf("")).toBeLessThan(html.indexOf("")); }); diff --git a/packages/studio-server/src/routes/files.ts b/packages/studio-server/src/routes/files.ts index afc7f2e03a..03476dc847 100644 --- a/packages/studio-server/src/routes/files.ts +++ b/packages/studio-server/src/routes/files.ts @@ -105,7 +105,10 @@ import { } from "../helpers/compositionInsertion.js"; import { resolveGsapWriter } from "./gsapMutationCapabilities.js"; import { requestSubPath } from "../helpers/requestSubPath.js"; -import { insertBeforeCloseTag } from "@hyperframes/core/compiler/html-document"; +import { + hasCompositionOutsideTemplates, + insertBeforeCloseTag, +} from "@hyperframes/core/compiler/html-document"; // ── Server cutover flag ───────────────────────────────────────────────────── @@ -1457,9 +1460,12 @@ async function prepareGsapMutationScript( `window.__timelines["${compId}"] = tl;`, "", ].join("\n"); + const [first, second] = hasCompositionOutsideTemplates(html) + ? (["body", "template"] as const) + : (["template", "body"] as const); html = - insertBeforeCloseTag(html, "body", `${bootstrap}\n`) ?? - insertBeforeCloseTag(html, "template", `${bootstrap}\n`) ?? + insertBeforeCloseTag(html, first, `${bootstrap}\n`) ?? + insertBeforeCloseTag(html, second, `${bootstrap}\n`) ?? `${html}\n${bootstrap}`; block = extractGsapScriptBlock(html); }