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 tag itself", + '', + false, + ], + [ + "a root id on beside a template with no composition", + 'clone me
', + true, + ], + [ + "a full document whose carries the id", + '', + false, + ], + [ + "the attribute only in a comment or text", + 'data-composition-id="b"
', + false, + ], + [ + "a nested template", + '', + false, + ], + ])("%s", (_name, html, expected) => { + expect(hasCompositionOutsideTemplates(html)).toBe(expected); + }); +}); diff --git a/packages/core/src/compiler/htmlDocument.ts b/packages/core/src/compiler/htmlDocument.ts index cf98279623..579b1e0aa4 100644 --- a/packages/core/src/compiler/htmlDocument.ts +++ b/packages/core/src/compiler/htmlDocument.ts @@ -300,6 +300,35 @@ function findOuterTemplateClose(html: string): number { return -1; } +const COMPOSITION_ID_ATTR = /\sdata-composition-id\s*=/; + +const isDocumentWrapper = (lowered: string, open: number): boolean => + isTagAt(lowered, open, " 0) idInTemplate = true; + else if (isDocumentWrapper(lowered, open)) idOnWrapper = true; + else return true; + } + } + return idOnWrapper && !idInTemplate; +} + function insertBeforeDocumentTag(html: string, tag: DocumentTag, markup: string): string | null { const at = tag === " file lands before , outside its template", async () => { + // The registry-block shape: the loader mounts the template's content, so a script outside it never runs. + it("a first animation in a full-document sub-composition lands inside its template", async () => { const html = await addFirstAnimation( - '