From 442cfa31b8894664a79afb03861d206e1982571b Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Thu, 15 Sep 2022 13:53:21 +0100 Subject: [PATCH 1/2] fix: fix inline scripts and styles potentially breaking fix the issue which breaks scripts split in different chunks in the input stream by applying them only after the scripts has been fully visited also fix the same issue for embeded styles, even though there everything works fine it is better to apply all the styles together and avoid potential flashes of unexpected style combinations resolves #1 --- .../inline-scripts.expected/step-0.html | 1 + .../inline-scripts.expected/step-1.html | 2 ++ .../inline-scripts.expected/step-3.html | 5 +++ .../inline-styles.expected/step-0.html | 1 + .../inline-styles.expected/step-1.html | 1 + .../inline-styles.expected/step-3.html | 7 ++++ src/__tests__/index.test.ts | 26 ++++++++++++++ src/index.ts | 36 ++++++++++++++++--- 8 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 src/__tests__/__snapshots__/inline-scripts.expected/step-0.html create mode 100644 src/__tests__/__snapshots__/inline-scripts.expected/step-1.html create mode 100644 src/__tests__/__snapshots__/inline-scripts.expected/step-3.html create mode 100644 src/__tests__/__snapshots__/inline-styles.expected/step-0.html create mode 100644 src/__tests__/__snapshots__/inline-styles.expected/step-1.html create mode 100644 src/__tests__/__snapshots__/inline-styles.expected/step-3.html 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__/index.test.ts b/src/__tests__/index.test.ts index 18b9b31..fe04578 100644 --- a/src/__tests__/index.test.ts +++ b/src/__tests__/index.test.ts @@ -118,3 +118,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" + ); +} From b5c015ebb8174634253632636983cfe6b29b51db Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Thu, 15 Sep 2022 21:17:43 +0100 Subject: [PATCH 2/2] test: add test for simple elements streaming --- .../__snapshots__/stream-elements.expected/step-0.html | 1 + .../__snapshots__/stream-elements.expected/step-1.html | 2 ++ .../__snapshots__/stream-elements.expected/step-2.html | 3 +++ src/__tests__/index.test.ts | 2 ++ 4 files changed, 8 insertions(+) create mode 100644 src/__tests__/__snapshots__/stream-elements.expected/step-0.html create mode 100644 src/__tests__/__snapshots__/stream-elements.expected/step-1.html create mode 100644 src/__tests__/__snapshots__/stream-elements.expected/step-2.html 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 fe04578..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([