diff --git a/src/__tests__/__snapshots__/inline-scripts.expected/step-0.html b/src/__tests__/__snapshots__/inline-scripts.expected/step-0.html new file mode 100644 index 0000000..2eed606 --- /dev/null +++ b/src/__tests__/__snapshots__/inline-scripts.expected/step-0.html @@ -0,0 +1 @@ +Embedded App. diff --git a/src/__tests__/__snapshots__/inline-scripts.expected/step-1.html b/src/__tests__/__snapshots__/inline-scripts.expected/step-1.html new file mode 100644 index 0000000..dd4d2da --- /dev/null +++ b/src/__tests__/__snapshots__/inline-scripts.expected/step-1.html @@ -0,0 +1,2 @@ +Embedded App. + diff --git a/src/__tests__/__snapshots__/inline-scripts.expected/step-3.html b/src/__tests__/__snapshots__/inline-scripts.expected/step-3.html new file mode 100644 index 0000000..c4e2acf --- /dev/null +++ b/src/__tests__/__snapshots__/inline-scripts.expected/step-3.html @@ -0,0 +1,5 @@ +Embedded App. + +After Script. diff --git a/src/__tests__/__snapshots__/inline-styles.expected/step-0.html b/src/__tests__/__snapshots__/inline-styles.expected/step-0.html new file mode 100644 index 0000000..2eed606 --- /dev/null +++ b/src/__tests__/__snapshots__/inline-styles.expected/step-0.html @@ -0,0 +1 @@ +Embedded App. diff --git a/src/__tests__/__snapshots__/inline-styles.expected/step-1.html b/src/__tests__/__snapshots__/inline-styles.expected/step-1.html new file mode 100644 index 0000000..a6dd68f --- /dev/null +++ b/src/__tests__/__snapshots__/inline-styles.expected/step-1.html @@ -0,0 +1 @@ +Embedded App. diff --git a/src/__tests__/__snapshots__/inline-styles.expected/step-3.html b/src/__tests__/__snapshots__/inline-styles.expected/step-3.html new file mode 100644 index 0000000..de9c359 --- /dev/null +++ b/src/__tests__/__snapshots__/inline-styles.expected/step-3.html @@ -0,0 +1,7 @@ +Embedded App. + +After Styles. diff --git a/src/__tests__/__snapshots__/stream-elements.expected/step-0.html b/src/__tests__/__snapshots__/stream-elements.expected/step-0.html new file mode 100644 index 0000000..1e1d1cc --- /dev/null +++ b/src/__tests__/__snapshots__/stream-elements.expected/step-0.html @@ -0,0 +1 @@ +

H1

diff --git a/src/__tests__/__snapshots__/stream-elements.expected/step-1.html b/src/__tests__/__snapshots__/stream-elements.expected/step-1.html new file mode 100644 index 0000000..d942798 --- /dev/null +++ b/src/__tests__/__snapshots__/stream-elements.expected/step-1.html @@ -0,0 +1,2 @@ +

H1

+

H2

diff --git a/src/__tests__/__snapshots__/stream-elements.expected/step-2.html b/src/__tests__/__snapshots__/stream-elements.expected/step-2.html new file mode 100644 index 0000000..b20d032 --- /dev/null +++ b/src/__tests__/__snapshots__/stream-elements.expected/step-2.html @@ -0,0 +1,3 @@ +

H1

+

H2

+

H3

diff --git a/src/__tests__/index.test.ts b/src/__tests__/index.test.ts index 18b9b31..8d99bb4 100644 --- a/src/__tests__/index.test.ts +++ b/src/__tests__/index.test.ts @@ -6,6 +6,8 @@ declare const inlineScriptValues: unknown[]; it("stream-text", fixture(["Hello", "World"])); +it("stream-elements", fixture(["

H1

", "

H2

", "

H3

"])); + it( "blocking-scripts", fixture([ @@ -118,3 +120,29 @@ After blocking.`, }, ]) ); + +it( + "inline-scripts", + fixture([ + "Embedded App.", + '', + "After Script.", + async (page) => { + assert.deepStrictEqual(await page.evaluate(() => scriptValues), [ + "a", + "b", + ]); + }, + ]) +); + +it( + "inline-styles", + fixture([ + "Embedded App. ", + "", + " After Styles.", + ]) +); diff --git a/src/index.ts b/src/index.ts index c42f3b2..2eaa2ae 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,12 +23,13 @@ export = function writableDOM( let scanNode: Node | null = null; let resolve: void | (() => void); let isBlocked = false; + let inlineHostNode: Node | null = null; return { write(chunk: string) { doc.write(chunk); - if (pendingText) { + if (pendingText && !inlineHostNode) { // When we left on text, it's possible more text was written to the same node. // here we copy in the final text content from the detached dom to the live dom. (targetNodes.get(pendingText) as Text).data = pendingText.data; @@ -42,6 +43,8 @@ export = function writableDOM( } }, close() { + appendInlineTextIfNeeded(pendingText, inlineHostNode); + return isBlocked ? new Promise((_) => (resolve = _)) : Promise.resolve(); @@ -68,6 +71,7 @@ export = function writableDOM( } else { while ((node = walker.nextNode())) { const clone = document.importNode(node, false); + const previousPendingText = pendingText; if (node.nodeType === Node.TEXT_NODE) { pendingText = node as Text; } else { @@ -86,10 +90,17 @@ export = function writableDOM( const parentNode = targetNodes.get(node.parentNode!)!; targetNodes.set(node, clone); - if (parentNode === target) { - target.insertBefore(clone, nextSibling); + if (isInlineHost(parentNode!)) { + inlineHostNode = parentNode; } else { - parentNode.appendChild(clone); + appendInlineTextIfNeeded(previousPendingText, inlineHostNode); + inlineHostNode = null; + + if (parentNode === target) { + target.insertBefore(clone, nextSibling); + } else { + parentNode.appendChild(clone); + } } // Start walking for preloads. @@ -178,3 +189,20 @@ function getPreloadLink(node: any) { return link; } + +function appendInlineTextIfNeeded( + pendingText: Text | null, + inlineTextHostNode: Node | null +) { + if (pendingText && inlineTextHostNode) { + inlineTextHostNode.appendChild(pendingText); + } +} + +function isInlineHost(node: Node) { + const { tagName } = node as Element; + return ( + (tagName === "SCRIPT" && !(node as HTMLScriptElement).src) || + tagName === "STYLE" + ); +}