From 9251b2b369e541004a31505d85d2b5cf545d9226 Mon Sep 17 00:00:00 2001 From: Dario Piotrowicz Date: Thu, 15 Sep 2022 13:53:21 +0100 Subject: [PATCH] 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 | 40 +++++++++++++++++-- 8 files changed, 79 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..6e4fa49 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,24 @@ function getPreloadLink(node: any) { return link; } + +function appendInlineTextIfNeeded( + pendingText: Text | null, + inlineTextHostNode: Node | null +) { + if (pendingText && inlineTextHostNode) { + inlineTextHostNode.appendChild(pendingText); + } +} + +function isInlineHost(node: Node) { + if (node.nodeType !== Node.ELEMENT_NODE) { + return false; + } + + const { tagName } = node as Element; + return ( + (tagName === "SCRIPT" && !(node as HTMLScriptElement).src) || + tagName === "STYLE" + ); +}