From 0cfc67ca11f8eaa95df9bca0fb9af57314873005 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 30 Aug 2017 08:46:11 -0400 Subject: [PATCH] only use noscript if necessary --- src/generators/dom/preprocess.ts | 6 +- src/generators/dom/visitors/RawMustacheTag.ts | 82 ++++++++++++++----- src/generators/dom/visitors/shared/Tag.ts | 6 +- src/shared/dom.js | 12 +++ .../expected-bundle.js | 16 ++-- .../each-block-changed-check/expected.js | 14 ++-- .../samples/raw-anchor-first-child/_config.js | 6 +- .../raw-anchor-first-last-child/_config.js | 8 +- .../samples/raw-anchor-last-child/_config.js | 6 +- .../_config.js | 4 + .../raw-anchor-next-sibling/_config.js | 4 + .../raw-anchor-previous-sibling/_config.js | 4 + .../raw-mustaches-preserved/_config.js | 6 +- 13 files changed, 121 insertions(+), 53 deletions(-) diff --git a/src/generators/dom/preprocess.ts b/src/generators/dom/preprocess.ts index bafbd5d397..737b1ddb79 100644 --- a/src/generators/dom/preprocess.ts +++ b/src/generators/dom/preprocess.ts @@ -65,10 +65,8 @@ const preprocessors = { const dependencies = block.findDependencies(node.expression); block.addDependencies(dependencies); - const basename = block.getUniqueName('raw'); - const name = block.getUniqueName(`${basename}_before`); - - node._state = getChildState(state, { basename, name }); + const name = block.getUniqueName('raw'); + node._state = getChildState(state, { name }); }, Text: ( diff --git a/src/generators/dom/visitors/RawMustacheTag.ts b/src/generators/dom/visitors/RawMustacheTag.ts index 02facfcd24..3c202ae252 100644 --- a/src/generators/dom/visitors/RawMustacheTag.ts +++ b/src/generators/dom/visitors/RawMustacheTag.ts @@ -1,5 +1,4 @@ import deindent from '../../../utils/deindent'; -import addUpdateBlock from './shared/addUpdateBlock'; import visitTag from './shared/Tag'; import { DomGenerator } from '../index'; import Block from '../Block'; @@ -12,9 +11,35 @@ export default function visitRawMustacheTag( state: State, node: Node ) { - const name = node._state.basename; - const before = node._state.name; - const after = block.getUniqueName(`${name}_after`); + const name = node._state.name; + + const needsAnchorBefore = node.prev ? (node.prev.type !== 'Element' || !node.prev._state || !node.prev._state.name) : !state.parentNode; + const needsAnchorAfter = node.next ? (node.next.type !== 'Element' || !node.next._state || !node.next._state.name) : !state.parentNode; + + const anchorBefore = needsAnchorBefore + ? block.getUniqueName(`${name}_before`) + : (node.prev && node.prev._state.name) || 'null'; + + const anchorAfter = needsAnchorAfter + ? block.getUniqueName(`${name}_after`) + : (node.next && node.next._state.name) || 'null'; + + let detach: string; + let insert: (content: string) => string; + + if (anchorBefore === 'null' && anchorAfter === 'null') { + detach = `${state.parentNode}.innerHTML = '';`; + insert = content => `${state.parentNode}.innerHTML = ${content};`; + } else if (anchorBefore === 'null') { + detach = `@detachBefore(${anchorAfter});`; + insert = content => `${anchorAfter}.insertAdjacentHTML('beforebegin', ${content});`; + } else if (anchorAfter === 'null') { + detach = `@detachAfter(${anchorBefore});`; + insert = content => `${anchorBefore}.insertAdjacentHTML('afterend', ${content});`; + } else { + detach = `@detachBetween(${anchorBefore}, ${anchorAfter});`; + insert = content => `${anchorBefore}.insertAdjacentHTML('afterend', ${content});`; + } const { init } = visitTag( generator, @@ -22,28 +47,43 @@ export default function visitRawMustacheTag( state, node, name, - value => deindent` - @detachBetween( ${before}, ${after} ); - ${before}.insertAdjacentHTML( 'afterend', ${value} ); + content => deindent` + ${detach} + ${insert(content)} ` ); // we would have used comments here, but the `insertAdjacentHTML` api only // exists for `Element`s. - block.addElement( - before, - `@createElement( 'noscript' )`, - `@createElement( 'noscript' )`, - state.parentNode - ); + if (needsAnchorBefore) { + block.addElement( + anchorBefore, + `@createElement( 'noscript' )`, + `@createElement( 'noscript' )`, + state.parentNode + ); + } - block.addElement( - after, - `@createElement( 'noscript' )`, - `@createElement( 'noscript' )`, - state.parentNode - ); + function addAnchorAfter() { + block.addElement( + anchorAfter, + `@createElement( 'noscript' )`, + `@createElement( 'noscript' )`, + state.parentNode + ); + } + + if (needsAnchorAfter && anchorBefore === 'null') { + // anchorAfter needs to be in the DOM before we + // insert the HTML... + addAnchorAfter(); + } + + block.builders.mount.addLine(insert(init)); + block.builders.detachRaw.addBlock(detach); - block.builders.mount.addLine(`${before}.insertAdjacentHTML( 'afterend', ${init} );`); - block.builders.detachRaw.addBlock(`@detachBetween( ${before}, ${after} );`); + if (needsAnchorAfter && anchorBefore !== 'null') { + // ...otherwise it should go afterwards + addAnchorAfter(); + } } \ No newline at end of file diff --git a/src/generators/dom/visitors/shared/Tag.ts b/src/generators/dom/visitors/shared/Tag.ts index f15d72a5ba..6798916c5c 100644 --- a/src/generators/dom/visitors/shared/Tag.ts +++ b/src/generators/dom/visitors/shared/Tag.ts @@ -23,7 +23,7 @@ export default function visitTag( ); const value = shouldCache && block.getUniqueName(`${name}_value`); - const init = shouldCache ? value : snippet; + const content = shouldCache ? value : snippet; if (shouldCache) block.addVariable(value, snippet); @@ -41,9 +41,9 @@ export default function visitTag( block.builders.update.addConditional( condition, - update(shouldCache ? value : snippet) + update(content) ); } - return { init }; + return { init: content }; } diff --git a/src/shared/dom.js b/src/shared/dom.js index 8408ffd359..86da87c726 100644 --- a/src/shared/dom.js +++ b/src/shared/dom.js @@ -16,6 +16,18 @@ export function detachBetween(before, after) { } } +export function detachBefore(after) { + while (after.previousSibling) { + after.parentNode.removeChild(after.previousSibling); + } +} + +export function detachAfter(before) { + while (before.nextSibling) { + before.parentNode.removeChild(before.nextSibling); + } +} + export function reinsertBetween(before, after, target) { while (before.nextSibling && before.nextSibling !== after) { target.appendChild(before.parentNode.removeChild(before.nextSibling)); diff --git a/test/js/samples/each-block-changed-check/expected-bundle.js b/test/js/samples/each-block-changed-check/expected-bundle.js index a09dc68e48..3ddd408a05 100644 --- a/test/js/samples/each-block-changed-check/expected-bundle.js +++ b/test/js/samples/each-block-changed-check/expected-bundle.js @@ -25,8 +25,8 @@ function detachNode(node) { node.parentNode.removeChild(node); } -function detachBetween(before, after) { - while (before.nextSibling && before.nextSibling !== after) { +function detachAfter(before) { + while (before.nextSibling) { before.parentNode.removeChild(before.nextSibling); } } @@ -247,7 +247,7 @@ function create_main_fragment ( state, component ) { } function create_each_block ( state, each_block_value, comment, i, component ) { - var div, strong, text, text_1, span, text_2_value = comment.author, text_2, text_3, text_4_value = state.elapsed(comment.time, state.time), text_4, text_5, text_6, raw_value = comment.html, raw_before, raw_after; + var div, strong, text, text_1, span, text_2_value = comment.author, text_2, text_3, text_4_value = state.elapsed(comment.time, state.time), text_4, text_5, text_6, raw_value = comment.html, raw_before; return { create: function () { @@ -262,7 +262,6 @@ function create_each_block ( state, each_block_value, comment, i, component ) { text_5 = createText( " ago:" ); text_6 = createText( "\n\n\t\t" ); raw_before = createElement( 'noscript' ); - raw_after = createElement( 'noscript' ); this.hydrate(); }, @@ -283,8 +282,7 @@ function create_each_block ( state, each_block_value, comment, i, component ) { appendNode( text_5, span ); appendNode( text_6, div ); appendNode( raw_before, div ); - appendNode( raw_after, div ); - raw_before.insertAdjacentHTML( 'afterend', raw_value ); + raw_before.insertAdjacentHTML('afterend', raw_value); }, update: function ( changed, state, each_block_value, comment, i ) { @@ -297,13 +295,13 @@ function create_each_block ( state, each_block_value, comment, i, component ) { } if ( ( changed.comments ) && raw_value !== ( raw_value = comment.html ) ) { - detachBetween( raw_before, raw_after ); - raw_before.insertAdjacentHTML( 'afterend', raw_value ); + detachAfter(raw_before); + raw_before.insertAdjacentHTML('afterend', raw_value); } }, unmount: function () { - detachBetween( raw_before, raw_after ); + detachAfter(raw_before); detachNode( div ); }, diff --git a/test/js/samples/each-block-changed-check/expected.js b/test/js/samples/each-block-changed-check/expected.js index b81c64ec93..7dcdb66696 100644 --- a/test/js/samples/each-block-changed-check/expected.js +++ b/test/js/samples/each-block-changed-check/expected.js @@ -1,4 +1,4 @@ -import { appendNode, assign, createElement, createText, destroyEach, detachBetween, detachNode, insertNode, noop, proto } from "svelte/shared.js"; +import { appendNode, assign, createElement, createText, destroyEach, detachAfter, detachNode, insertNode, noop, proto } from "svelte/shared.js"; function create_main_fragment ( state, component ) { var text, p, text_1; @@ -74,7 +74,7 @@ function create_main_fragment ( state, component ) { } function create_each_block ( state, each_block_value, comment, i, component ) { - var div, strong, text, text_1, span, text_2_value = comment.author, text_2, text_3, text_4_value = state.elapsed(comment.time, state.time), text_4, text_5, text_6, raw_value = comment.html, raw_before, raw_after; + var div, strong, text, text_1, span, text_2_value = comment.author, text_2, text_3, text_4_value = state.elapsed(comment.time, state.time), text_4, text_5, text_6, raw_value = comment.html, raw_before; return { create: function () { @@ -89,7 +89,6 @@ function create_each_block ( state, each_block_value, comment, i, component ) { text_5 = createText( " ago:" ); text_6 = createText( "\n\n\t\t" ); raw_before = createElement( 'noscript' ); - raw_after = createElement( 'noscript' ); this.hydrate(); }, @@ -110,8 +109,7 @@ function create_each_block ( state, each_block_value, comment, i, component ) { appendNode( text_5, span ); appendNode( text_6, div ); appendNode( raw_before, div ); - appendNode( raw_after, div ); - raw_before.insertAdjacentHTML( 'afterend', raw_value ); + raw_before.insertAdjacentHTML('afterend', raw_value); }, update: function ( changed, state, each_block_value, comment, i ) { @@ -124,13 +122,13 @@ function create_each_block ( state, each_block_value, comment, i, component ) { } if ( ( changed.comments ) && raw_value !== ( raw_value = comment.html ) ) { - detachBetween( raw_before, raw_after ); - raw_before.insertAdjacentHTML( 'afterend', raw_value ); + detachAfter(raw_before); + raw_before.insertAdjacentHTML('afterend', raw_value); } }, unmount: function () { - detachBetween( raw_before, raw_after ); + detachAfter(raw_before); detachNode( div ); }, diff --git a/test/runtime/samples/raw-anchor-first-child/_config.js b/test/runtime/samples/raw-anchor-first-child/_config.js index c6fe8430f3..003dda1eaf 100644 --- a/test/runtime/samples/raw-anchor-first-child/_config.js +++ b/test/runtime/samples/raw-anchor-first-child/_config.js @@ -5,6 +5,10 @@ export default { test ( assert, component, target ) { const span = target.querySelector('span'); - assert.equal(!span.previousSibling); + assert.ok(!span.previousSibling); + + component.set({ + raw: `bar` + }); } }; diff --git a/test/runtime/samples/raw-anchor-first-last-child/_config.js b/test/runtime/samples/raw-anchor-first-last-child/_config.js index 60cbd02d39..4e8e796110 100644 --- a/test/runtime/samples/raw-anchor-first-last-child/_config.js +++ b/test/runtime/samples/raw-anchor-first-last-child/_config.js @@ -5,7 +5,11 @@ export default { test ( assert, component, target ) { const span = target.querySelector('span'); - assert.equal(!span.previousSibling); - assert.equal(!span.nextSibling); + assert.ok(!span.previousSibling); + assert.ok(!span.nextSibling); + + component.set({ + raw: `bar` + }); } }; diff --git a/test/runtime/samples/raw-anchor-last-child/_config.js b/test/runtime/samples/raw-anchor-last-child/_config.js index 5ec1dd87de..54a774f46f 100644 --- a/test/runtime/samples/raw-anchor-last-child/_config.js +++ b/test/runtime/samples/raw-anchor-last-child/_config.js @@ -5,6 +5,10 @@ export default { test ( assert, component, target ) { const span = target.querySelector('span'); - assert.equal(!span.nextSibling); + assert.ok(!span.nextSibling); + + component.set({ + raw: `bar` + }); } }; diff --git a/test/runtime/samples/raw-anchor-next-previous-sibling/_config.js b/test/runtime/samples/raw-anchor-next-previous-sibling/_config.js index 5d2f0bed84..7c9769ea7b 100644 --- a/test/runtime/samples/raw-anchor-next-previous-sibling/_config.js +++ b/test/runtime/samples/raw-anchor-next-previous-sibling/_config.js @@ -7,5 +7,9 @@ export default { const span = target.querySelector('span'); assert.equal(span.previousSibling.nodeName, 'BR'); assert.equal(span.nextSibling.nodeName, 'BR'); + + component.set({ + raw: `bar` + }); } }; diff --git a/test/runtime/samples/raw-anchor-next-sibling/_config.js b/test/runtime/samples/raw-anchor-next-sibling/_config.js index 591bc03af8..26046ea6fc 100644 --- a/test/runtime/samples/raw-anchor-next-sibling/_config.js +++ b/test/runtime/samples/raw-anchor-next-sibling/_config.js @@ -6,5 +6,9 @@ export default { test ( assert, component, target ) { const span = target.querySelector('span'); assert.equal(span.previousSibling.nodeName, 'BR'); + + component.set({ + raw: `bar` + }); } }; diff --git a/test/runtime/samples/raw-anchor-previous-sibling/_config.js b/test/runtime/samples/raw-anchor-previous-sibling/_config.js index 591bc03af8..26046ea6fc 100644 --- a/test/runtime/samples/raw-anchor-previous-sibling/_config.js +++ b/test/runtime/samples/raw-anchor-previous-sibling/_config.js @@ -6,5 +6,9 @@ export default { test ( assert, component, target ) { const span = target.querySelector('span'); assert.equal(span.previousSibling.nodeName, 'BR'); + + component.set({ + raw: `bar` + }); } }; diff --git a/test/runtime/samples/raw-mustaches-preserved/_config.js b/test/runtime/samples/raw-mustaches-preserved/_config.js index 4d3971d7e9..d613f852c5 100644 --- a/test/runtime/samples/raw-mustaches-preserved/_config.js +++ b/test/runtime/samples/raw-mustaches-preserved/_config.js @@ -1,5 +1,3 @@ -const ns = ''; - export default { 'skip-ssr': true, @@ -7,13 +5,13 @@ export default { raw: '

does not change

' }, - html: `
${ns}

does not change

${ns}
`, + html: `

does not change

`, test ( assert, component, target ) { const p = target.querySelector( 'p' ); component.set({ raw: '

does not change

' }); - assert.equal( target.innerHTML, `
${ns}

does not change

${ns}
` ); + assert.equal( target.innerHTML, `

does not change

` ); assert.strictEqual( target.querySelector( 'p' ), p ); component.destroy();