From a197c185236aed678ac02c125359fa172dcbe1db Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 29 Aug 2017 21:35:02 -0400 Subject: [PATCH 01/10] failing tests for #637 --- .../runtime/samples/raw-anchor-first-child/_config.js | 10 ++++++++++ test/runtime/samples/raw-anchor-first-child/main.html | 1 + .../samples/raw-anchor-first-last-child/_config.js | 11 +++++++++++ .../samples/raw-anchor-first-last-child/main.html | 1 + test/runtime/samples/raw-anchor-last-child/_config.js | 10 ++++++++++ test/runtime/samples/raw-anchor-last-child/main.html | 1 + .../raw-anchor-next-previous-sibling/_config.js | 11 +++++++++++ .../raw-anchor-next-previous-sibling/main.html | 1 + .../samples/raw-anchor-next-sibling/_config.js | 10 ++++++++++ .../runtime/samples/raw-anchor-next-sibling/main.html | 1 + .../samples/raw-anchor-previous-sibling/_config.js | 10 ++++++++++ .../samples/raw-anchor-previous-sibling/main.html | 1 + 12 files changed, 68 insertions(+) create mode 100644 test/runtime/samples/raw-anchor-first-child/_config.js create mode 100644 test/runtime/samples/raw-anchor-first-child/main.html create mode 100644 test/runtime/samples/raw-anchor-first-last-child/_config.js create mode 100644 test/runtime/samples/raw-anchor-first-last-child/main.html create mode 100644 test/runtime/samples/raw-anchor-last-child/_config.js create mode 100644 test/runtime/samples/raw-anchor-last-child/main.html create mode 100644 test/runtime/samples/raw-anchor-next-previous-sibling/_config.js create mode 100644 test/runtime/samples/raw-anchor-next-previous-sibling/main.html create mode 100644 test/runtime/samples/raw-anchor-next-sibling/_config.js create mode 100644 test/runtime/samples/raw-anchor-next-sibling/main.html create mode 100644 test/runtime/samples/raw-anchor-previous-sibling/_config.js create mode 100644 test/runtime/samples/raw-anchor-previous-sibling/main.html diff --git a/test/runtime/samples/raw-anchor-first-child/_config.js b/test/runtime/samples/raw-anchor-first-child/_config.js new file mode 100644 index 0000000000..c6fe8430f3 --- /dev/null +++ b/test/runtime/samples/raw-anchor-first-child/_config.js @@ -0,0 +1,10 @@ +export default { + data: { + raw: `foo` + }, + + test ( assert, component, target ) { + const span = target.querySelector('span'); + assert.equal(!span.previousSibling); + } +}; diff --git a/test/runtime/samples/raw-anchor-first-child/main.html b/test/runtime/samples/raw-anchor-first-child/main.html new file mode 100644 index 0000000000..f9a62f27ea --- /dev/null +++ b/test/runtime/samples/raw-anchor-first-child/main.html @@ -0,0 +1 @@ +
{{{raw}}}{{#if maybe}}after{{/if}}
diff --git a/test/runtime/samples/raw-anchor-first-last-child/_config.js b/test/runtime/samples/raw-anchor-first-last-child/_config.js new file mode 100644 index 0000000000..60cbd02d39 --- /dev/null +++ b/test/runtime/samples/raw-anchor-first-last-child/_config.js @@ -0,0 +1,11 @@ +export default { + data: { + raw: `foo` + }, + + test ( assert, component, target ) { + const span = target.querySelector('span'); + assert.equal(!span.previousSibling); + assert.equal(!span.nextSibling); + } +}; diff --git a/test/runtime/samples/raw-anchor-first-last-child/main.html b/test/runtime/samples/raw-anchor-first-last-child/main.html new file mode 100644 index 0000000000..4f10ae03c0 --- /dev/null +++ b/test/runtime/samples/raw-anchor-first-last-child/main.html @@ -0,0 +1 @@ +
{{{raw}}}
diff --git a/test/runtime/samples/raw-anchor-last-child/_config.js b/test/runtime/samples/raw-anchor-last-child/_config.js new file mode 100644 index 0000000000..5ec1dd87de --- /dev/null +++ b/test/runtime/samples/raw-anchor-last-child/_config.js @@ -0,0 +1,10 @@ +export default { + data: { + raw: `foo` + }, + + test ( assert, component, target ) { + const span = target.querySelector('span'); + assert.equal(!span.nextSibling); + } +}; diff --git a/test/runtime/samples/raw-anchor-last-child/main.html b/test/runtime/samples/raw-anchor-last-child/main.html new file mode 100644 index 0000000000..67b6adf483 --- /dev/null +++ b/test/runtime/samples/raw-anchor-last-child/main.html @@ -0,0 +1 @@ +
{{#if maybe}}after{{/if}}{{{raw}}}
diff --git a/test/runtime/samples/raw-anchor-next-previous-sibling/_config.js b/test/runtime/samples/raw-anchor-next-previous-sibling/_config.js new file mode 100644 index 0000000000..5d2f0bed84 --- /dev/null +++ b/test/runtime/samples/raw-anchor-next-previous-sibling/_config.js @@ -0,0 +1,11 @@ +export default { + data: { + raw: `foo` + }, + + test ( assert, component, target ) { + const span = target.querySelector('span'); + assert.equal(span.previousSibling.nodeName, 'BR'); + assert.equal(span.nextSibling.nodeName, 'BR'); + } +}; diff --git a/test/runtime/samples/raw-anchor-next-previous-sibling/main.html b/test/runtime/samples/raw-anchor-next-previous-sibling/main.html new file mode 100644 index 0000000000..d0a2a83668 --- /dev/null +++ b/test/runtime/samples/raw-anchor-next-previous-sibling/main.html @@ -0,0 +1 @@ +before
{{{raw}}}
after diff --git a/test/runtime/samples/raw-anchor-next-sibling/_config.js b/test/runtime/samples/raw-anchor-next-sibling/_config.js new file mode 100644 index 0000000000..591bc03af8 --- /dev/null +++ b/test/runtime/samples/raw-anchor-next-sibling/_config.js @@ -0,0 +1,10 @@ +export default { + data: { + raw: `foo` + }, + + test ( assert, component, target ) { + const span = target.querySelector('span'); + assert.equal(span.previousSibling.nodeName, 'BR'); + } +}; diff --git a/test/runtime/samples/raw-anchor-next-sibling/main.html b/test/runtime/samples/raw-anchor-next-sibling/main.html new file mode 100644 index 0000000000..00c18218a0 --- /dev/null +++ b/test/runtime/samples/raw-anchor-next-sibling/main.html @@ -0,0 +1 @@ +before
{{{raw}}}{{#if maybe}}after{{/if}} diff --git a/test/runtime/samples/raw-anchor-previous-sibling/_config.js b/test/runtime/samples/raw-anchor-previous-sibling/_config.js new file mode 100644 index 0000000000..591bc03af8 --- /dev/null +++ b/test/runtime/samples/raw-anchor-previous-sibling/_config.js @@ -0,0 +1,10 @@ +export default { + data: { + raw: `foo` + }, + + test ( assert, component, target ) { + const span = target.querySelector('span'); + assert.equal(span.previousSibling.nodeName, 'BR'); + } +}; diff --git a/test/runtime/samples/raw-anchor-previous-sibling/main.html b/test/runtime/samples/raw-anchor-previous-sibling/main.html new file mode 100644 index 0000000000..00c18218a0 --- /dev/null +++ b/test/runtime/samples/raw-anchor-previous-sibling/main.html @@ -0,0 +1 @@ +before
{{{raw}}}{{#if maybe}}after{{/if}} From 5c88b6c8643a00a530a4983602a13ce324a4a990 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 29 Aug 2017 22:04:45 -0400 Subject: [PATCH 02/10] usedAsAnchor is not used --- src/generators/dom/visitors/EachBlock.ts | 2 -- src/generators/dom/visitors/IfBlock.ts | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/generators/dom/visitors/EachBlock.ts b/src/generators/dom/visitors/EachBlock.ts index ee4796946d..406ed6e2a3 100644 --- a/src/generators/dom/visitors/EachBlock.ts +++ b/src/generators/dom/visitors/EachBlock.ts @@ -60,8 +60,6 @@ export default function visitEachBlock( `@createComment()`, state.parentNode ); - } else if (node.next) { - node.next.usedAsAnchor = true; } if (node.else) { diff --git a/src/generators/dom/visitors/IfBlock.ts b/src/generators/dom/visitors/IfBlock.ts index e45ab1fb20..e858668932 100644 --- a/src/generators/dom/visitors/IfBlock.ts +++ b/src/generators/dom/visitors/IfBlock.ts @@ -124,8 +124,6 @@ export default function visitIfBlock( `@createComment()`, state.parentNode ); - } else if (node.next) { - node.next.usedAsAnchor = true; } } From 457f0189d405e7f1216c83a26f457e01ff6005cd Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 29 Aug 2017 22:10:29 -0400 Subject: [PATCH 03/10] determine needsAnchor lazily --- src/generators/dom/preprocess.ts | 10 ++-------- src/generators/dom/visitors/EachBlock.ts | 6 ++++-- src/generators/dom/visitors/IfBlock.ts | 6 ++++-- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/generators/dom/preprocess.ts b/src/generators/dom/preprocess.ts index 7855a52f89..bafbd5d397 100644 --- a/src/generators/dom/preprocess.ts +++ b/src/generators/dom/preprocess.ts @@ -421,10 +421,8 @@ function preprocessChildren( const preprocessor = preprocessors[child.type]; if (preprocessor) preprocessor(generator, block, state, child, inEachBlock, elementStack, componentStack, stripWhitespace, cleaned[i + 1] || nextSibling); - if (lastChild) { - lastChild.next = child; - lastChild.needsAnchor = !child._state || !child._state.name; - } + if (lastChild) lastChild.next = child; + child.prev = lastChild; lastChild = child; }); @@ -446,10 +444,6 @@ function preprocessChildren( } } - if (lastChild) { - lastChild.needsAnchor = !state.parentNode; - } - node.children = cleaned; } diff --git a/src/generators/dom/visitors/EachBlock.ts b/src/generators/dom/visitors/EachBlock.ts index 406ed6e2a3..bf838e5a25 100644 --- a/src/generators/dom/visitors/EachBlock.ts +++ b/src/generators/dom/visitors/EachBlock.ts @@ -18,7 +18,9 @@ export default function visitEachBlock( const each_block_value = node._block.listName; const iterations = block.getUniqueName(`${each_block}_iterations`); const params = block.params.join(', '); - const anchor = node.needsAnchor + + const needsAnchor = node.next ? (!node.next._state || !node.next._state.name) : !state.parentNode; + const anchor = needsAnchor ? block.getUniqueName(`${each_block}_anchor`) : (node.next && node.next._state.name) || 'null'; @@ -53,7 +55,7 @@ export default function visitEachBlock( const isToplevel = !state.parentNode; - if (node.needsAnchor) { + if (needsAnchor) { block.addElement( anchor, `@createComment()`, diff --git a/src/generators/dom/visitors/IfBlock.ts b/src/generators/dom/visitors/IfBlock.ts index e858668932..d91da53d02 100644 --- a/src/generators/dom/visitors/IfBlock.ts +++ b/src/generators/dom/visitors/IfBlock.ts @@ -78,7 +78,9 @@ export default function visitIfBlock( componentStack: Node[] ) { const name = generator.getUniqueName(`if_block`); - const anchor = node.needsAnchor + + const needsAnchor = node.next ? (!node.next._state || !node.next._state.name) : !state.parentNode; + const anchor = needsAnchor ? block.getUniqueName(`${name}_anchor`) : (node.next && node.next._state.name) || 'null'; const params = block.params.join(', '); @@ -117,7 +119,7 @@ export default function visitIfBlock( `${if_name}${name}.claim( ${state.parentNodes} );` ); - if (node.needsAnchor) { + if (needsAnchor) { block.addElement( anchor, `@createComment()`, From 6499d4714d2b3cae1362b6e1ce5ed01ca8c573d0 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 30 Aug 2017 08:46:11 -0400 Subject: [PATCH 04/10] 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(); From 9fae7671a4ae11251c80a1b4816818e799a3e53f Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 30 Aug 2017 10:40:21 -0400 Subject: [PATCH 05/10] refactor node._state stuff --- src/generators/dom/preprocess.ts | 21 +++++++------------ src/generators/dom/visitors/Component.ts | 2 +- src/generators/dom/visitors/EachBlock.ts | 4 ++-- .../dom/visitors/Element/Element.ts | 4 ++-- .../dom/visitors/Element/addTransitions.ts | 20 +++++++++--------- src/generators/dom/visitors/IfBlock.ts | 4 ++-- src/generators/dom/visitors/MustacheTag.ts | 8 +++---- src/generators/dom/visitors/RawMustacheTag.ts | 10 ++++----- src/generators/dom/visitors/Text.ts | 2 +- 9 files changed, 34 insertions(+), 41 deletions(-) diff --git a/src/generators/dom/preprocess.ts b/src/generators/dom/preprocess.ts index 737b1ddb79..dac7d5e5e2 100644 --- a/src/generators/dom/preprocess.ts +++ b/src/generators/dom/preprocess.ts @@ -48,9 +48,7 @@ const preprocessors = { const dependencies = block.findDependencies(node.expression); block.addDependencies(dependencies); - node._state = getChildState(state, { - name: block.getUniqueName('text'), - }); + node.var = block.getUniqueName('text'); }, RawMustacheTag: ( @@ -65,8 +63,7 @@ const preprocessors = { const dependencies = block.findDependencies(node.expression); block.addDependencies(dependencies); - const name = block.getUniqueName('raw'); - node._state = getChildState(state, { name }); + node.var = block.getUniqueName('raw'); }, Text: ( @@ -86,7 +83,7 @@ const preprocessors = { } node._state.shouldCreate = true; - node._state.name = block.getUniqueName(`text`); + node.var = block.getUniqueName(`text`); }, IfBlock: ( @@ -333,13 +330,12 @@ const preprocessors = { generator.components.has(node.name) || node.name === ':Self'; if (isComponent) { - const name = block.getUniqueName( + node.var = block.getUniqueName( (node.name === ':Self' ? generator.name : node.name).toLowerCase() ); node._state = getChildState(state, { - name, - parentNode: `${name}._slotted.default` + parentNode: `${node.var}._slotted.default` }); } else { const slot = getStaticAttributeValue(node, 'slot'); @@ -349,15 +345,14 @@ const preprocessors = { component._slots.add(slot); } - const name = block.getUniqueName( + node.var = block.getUniqueName( node.name.replace(/[^a-zA-Z0-9_$]/g, '_') ); node._state = getChildState(state, { isTopLevel: false, - name, - parentNode: name, - parentNodes: block.getUniqueName(`${name}_nodes`), + parentNode: node.var, + parentNodes: block.getUniqueName(`${node.var}_nodes`), parentNodeName: node.name, namespace: node.name === 'svg' ? 'http://www.w3.org/2000/svg' diff --git a/src/generators/dom/visitors/Component.ts b/src/generators/dom/visitors/Component.ts index 6757de1a8a..69587d5d07 100644 --- a/src/generators/dom/visitors/Component.ts +++ b/src/generators/dom/visitors/Component.ts @@ -48,7 +48,7 @@ export default function visitComponent( ) { generator.hasComponents = true; - const name = node._state.name; + const name = node.var; const componentInitProperties = [`_root: #component._root`]; diff --git a/src/generators/dom/visitors/EachBlock.ts b/src/generators/dom/visitors/EachBlock.ts index bf838e5a25..0e56727624 100644 --- a/src/generators/dom/visitors/EachBlock.ts +++ b/src/generators/dom/visitors/EachBlock.ts @@ -19,10 +19,10 @@ export default function visitEachBlock( const iterations = block.getUniqueName(`${each_block}_iterations`); const params = block.params.join(', '); - const needsAnchor = node.next ? (!node.next._state || !node.next._state.name) : !state.parentNode; + const needsAnchor = node.next ? (!node.next._state || !node.next.var) : !state.parentNode; const anchor = needsAnchor ? block.getUniqueName(`${each_block}_anchor`) - : (node.next && node.next._state.name) || 'null'; + : (node.next && node.next.var) || 'null'; // hack the sourcemap, so that if data is missing the bug // is easy to find diff --git a/src/generators/dom/visitors/Element/Element.ts b/src/generators/dom/visitors/Element/Element.ts index 1087273406..42b2ec71ff 100644 --- a/src/generators/dom/visitors/Element/Element.ts +++ b/src/generators/dom/visitors/Element/Element.ts @@ -45,7 +45,7 @@ export default function visitElement( } if (node.name === 'slot') { - return visitSlot(generator, block, state, node, elementStack); + return visitSlot(generator, block, state, node, elementStack, componentStack); } if (generator.components.has(node.name) || node.name === ':Self') { @@ -57,7 +57,7 @@ export default function visitElement( const slot = node.attributes.find((attribute: Node) => attribute.name === 'slot'); const parentNode = slot ? - `${componentStack[componentStack.length - 1]._state.name}._slotted.${slot.value[0].data}` : // TODO this looks bonkers + `${componentStack[componentStack.length - 1].var}._slotted.${slot.value[0].data}` : // TODO this looks bonkers state.parentNode; const isToplevel = !parentNode; diff --git a/src/generators/dom/visitors/Element/addTransitions.ts b/src/generators/dom/visitors/Element/addTransitions.ts index 889cf1eb0b..27a4ad289c 100644 --- a/src/generators/dom/visitors/Element/addTransitions.ts +++ b/src/generators/dom/visitors/Element/addTransitions.ts @@ -13,7 +13,7 @@ export default function addTransitions( outro ) { if (intro === outro) { - const name = block.getUniqueName(`${state.name}_transition`); + const name = block.getUniqueName(`${node.var}_transition`); const snippet = intro.expression ? block.contextualise(intro.expression).snippet : '{}'; @@ -24,23 +24,23 @@ export default function addTransitions( block.builders.intro.addBlock(deindent` #component._root._aftercreate.push( function () { - if ( !${name} ) ${name} = @wrapTransition( #component, ${state.name}, ${fn}, ${snippet}, true, null ); + if ( !${name} ) ${name} = @wrapTransition( #component, ${node.var}, ${fn}, ${snippet}, true, null ); ${name}.run( true, function () { - #component.fire( 'intro.end', { node: ${state.name} }); + #component.fire( 'intro.end', { node: ${node.var} }); }); }); `); block.builders.outro.addBlock(deindent` ${name}.run( false, function () { - #component.fire( 'outro.end', { node: ${state.name} }); + #component.fire( 'outro.end', { node: ${node.var} }); if ( --#outros === 0 ) #outrocallback(); ${name} = null; }); `); } else { - const introName = intro && block.getUniqueName(`${state.name}_intro`); - const outroName = outro && block.getUniqueName(`${state.name}_outro`); + const introName = intro && block.getUniqueName(`${node.var}_intro`); + const outroName = outro && block.getUniqueName(`${node.var}_outro`); if (intro) { block.addVariable(introName); @@ -59,9 +59,9 @@ export default function addTransitions( block.builders.intro.addBlock(deindent` #component._root._aftercreate.push( function () { - ${introName} = @wrapTransition( #component, ${state.name}, ${fn}, ${snippet}, true, null ); + ${introName} = @wrapTransition( #component, ${node.var}, ${fn}, ${snippet}, true, null ); ${introName}.run( true, function () { - #component.fire( 'intro.end', { node: ${state.name} }); + #component.fire( 'intro.end', { node: ${node.var} }); }); }); `); @@ -78,9 +78,9 @@ export default function addTransitions( // TODO hide elements that have outro'd (unless they belong to a still-outroing // group) prior to their removal from the DOM block.builders.outro.addBlock(deindent` - ${outroName} = @wrapTransition( #component, ${state.name}, ${fn}, ${snippet}, false, null ); + ${outroName} = @wrapTransition( #component, ${node.var}, ${fn}, ${snippet}, false, null ); ${outroName}.run( false, function () { - #component.fire( 'outro.end', { node: ${state.name} }); + #component.fire( 'outro.end', { node: ${node.var} }); if ( --#outros === 0 ) #outrocallback(); }); `); diff --git a/src/generators/dom/visitors/IfBlock.ts b/src/generators/dom/visitors/IfBlock.ts index d91da53d02..b9e0816cff 100644 --- a/src/generators/dom/visitors/IfBlock.ts +++ b/src/generators/dom/visitors/IfBlock.ts @@ -79,10 +79,10 @@ export default function visitIfBlock( ) { const name = generator.getUniqueName(`if_block`); - const needsAnchor = node.next ? (!node.next._state || !node.next._state.name) : !state.parentNode; + const needsAnchor = node.next ? (!node.next._state || !node.next.var) : !state.parentNode; const anchor = needsAnchor ? block.getUniqueName(`${name}_anchor`) - : (node.next && node.next._state.name) || 'null'; + : (node.next && node.next.var) || 'null'; const params = block.params.join(', '); const branches = getBranches(generator, block, state, node, elementStack, componentStack); diff --git a/src/generators/dom/visitors/MustacheTag.ts b/src/generators/dom/visitors/MustacheTag.ts index 3c03c51167..257a926e3b 100644 --- a/src/generators/dom/visitors/MustacheTag.ts +++ b/src/generators/dom/visitors/MustacheTag.ts @@ -11,19 +11,17 @@ export default function visitMustacheTag( state: State, node: Node ) { - const { name } = node._state; - const { init } = visitTag( generator, block, state, node, - name, - value => `${name}.data = ${value};` + node.var, + value => `${node.var}.data = ${value};` ); block.addElement( - name, + node.var, `@createText( ${init} )`, `@claimText( ${state.parentNodes}, ${init} )`, state.parentNode diff --git a/src/generators/dom/visitors/RawMustacheTag.ts b/src/generators/dom/visitors/RawMustacheTag.ts index 3c202ae252..675b17ff2b 100644 --- a/src/generators/dom/visitors/RawMustacheTag.ts +++ b/src/generators/dom/visitors/RawMustacheTag.ts @@ -11,18 +11,18 @@ export default function visitRawMustacheTag( state: State, node: Node ) { - const name = node._state.name; + const name = node.var; - 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 needsAnchorBefore = node.prev ? (node.prev.type !== 'Element' || !node.prev._state || !node.prev.var) : !state.parentNode; + const needsAnchorAfter = node.next ? (node.next.type !== 'Element' || !node.next._state || !node.next.var) : !state.parentNode; const anchorBefore = needsAnchorBefore ? block.getUniqueName(`${name}_before`) - : (node.prev && node.prev._state.name) || 'null'; + : (node.prev && node.prev.var) || 'null'; const anchorAfter = needsAnchorAfter ? block.getUniqueName(`${name}_after`) - : (node.next && node.next._state.name) || 'null'; + : (node.next && node.next.var) || 'null'; let detach: string; let insert: (content: string) => string; diff --git a/src/generators/dom/visitors/Text.ts b/src/generators/dom/visitors/Text.ts index 796bf9e309..fa1265f474 100644 --- a/src/generators/dom/visitors/Text.ts +++ b/src/generators/dom/visitors/Text.ts @@ -12,7 +12,7 @@ export default function visitText( ) { if (!node._state.shouldCreate) return; block.addElement( - node._state.name, + node.var, `@createText( ${stringify(node.data)} )`, `@claimText( ${state.parentNodes}, ${stringify(node.data)} )`, state.parentNode From 4603d7700fe8366fc5be5b61a415f7234cdeae46 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 30 Aug 2017 10:58:10 -0400 Subject: [PATCH 06/10] more refactoring --- src/generators/dom/preprocess.ts | 2 +- src/generators/dom/visitors/EachBlock.ts | 3 ++- src/generators/dom/visitors/IfBlock.ts | 3 ++- src/generators/dom/visitors/RawMustacheTag.ts | 4 ++-- src/generators/dom/visitors/Text.ts | 3 ++- src/generators/dom/visitors/shared/isDomNode.ts | 5 +++++ 6 files changed, 14 insertions(+), 6 deletions(-) create mode 100644 src/generators/dom/visitors/shared/isDomNode.ts diff --git a/src/generators/dom/preprocess.ts b/src/generators/dom/preprocess.ts index dac7d5e5e2..b91aacd266 100644 --- a/src/generators/dom/preprocess.ts +++ b/src/generators/dom/preprocess.ts @@ -82,7 +82,7 @@ const preprocessors = { if (elementsWithoutText.has(state.parentNodeName)) return; } - node._state.shouldCreate = true; + node.shouldCreate = true; node.var = block.getUniqueName(`text`); }, diff --git a/src/generators/dom/visitors/EachBlock.ts b/src/generators/dom/visitors/EachBlock.ts index 0e56727624..7796f2414c 100644 --- a/src/generators/dom/visitors/EachBlock.ts +++ b/src/generators/dom/visitors/EachBlock.ts @@ -2,6 +2,7 @@ import deindent from '../../../utils/deindent'; import visit from '../visit'; import { DomGenerator } from '../index'; import Block from '../Block'; +import isDomNode from './shared/isDomNode'; import { Node } from '../../../interfaces'; import { State } from '../interfaces'; @@ -19,7 +20,7 @@ export default function visitEachBlock( const iterations = block.getUniqueName(`${each_block}_iterations`); const params = block.params.join(', '); - const needsAnchor = node.next ? (!node.next._state || !node.next.var) : !state.parentNode; + const needsAnchor = node.next ? !isDomNode(node.next) : !state.parentNode; const anchor = needsAnchor ? block.getUniqueName(`${each_block}_anchor`) : (node.next && node.next.var) || 'null'; diff --git a/src/generators/dom/visitors/IfBlock.ts b/src/generators/dom/visitors/IfBlock.ts index b9e0816cff..3ce27599ff 100644 --- a/src/generators/dom/visitors/IfBlock.ts +++ b/src/generators/dom/visitors/IfBlock.ts @@ -2,6 +2,7 @@ import deindent from '../../../utils/deindent'; import visit from '../visit'; import { DomGenerator } from '../index'; import Block from '../Block'; +import isDomNode from './shared/isDomNode'; import { Node } from '../../../interfaces'; import { State } from '../interfaces'; @@ -79,7 +80,7 @@ export default function visitIfBlock( ) { const name = generator.getUniqueName(`if_block`); - const needsAnchor = node.next ? (!node.next._state || !node.next.var) : !state.parentNode; + const needsAnchor = node.next ? !isDomNode(node.next) : !state.parentNode; const anchor = needsAnchor ? block.getUniqueName(`${name}_anchor`) : (node.next && node.next.var) || 'null'; diff --git a/src/generators/dom/visitors/RawMustacheTag.ts b/src/generators/dom/visitors/RawMustacheTag.ts index 675b17ff2b..ab0ff7991d 100644 --- a/src/generators/dom/visitors/RawMustacheTag.ts +++ b/src/generators/dom/visitors/RawMustacheTag.ts @@ -13,8 +13,8 @@ export default function visitRawMustacheTag( ) { const name = node.var; - const needsAnchorBefore = node.prev ? (node.prev.type !== 'Element' || !node.prev._state || !node.prev.var) : !state.parentNode; - const needsAnchorAfter = node.next ? (node.next.type !== 'Element' || !node.next._state || !node.next.var) : !state.parentNode; + const needsAnchorBefore = node.prev ? node.prev.type !== 'Element' : !state.parentNode; + const needsAnchorAfter = node.next ? node.next.type !== 'Element' : !state.parentNode; const anchorBefore = needsAnchorBefore ? block.getUniqueName(`${name}_before`) diff --git a/src/generators/dom/visitors/Text.ts b/src/generators/dom/visitors/Text.ts index fa1265f474..d4d423d546 100644 --- a/src/generators/dom/visitors/Text.ts +++ b/src/generators/dom/visitors/Text.ts @@ -10,7 +10,8 @@ export default function visitText( state: State, node: Node ) { - if (!node._state.shouldCreate) return; + if (!node.shouldCreate) return; + block.addElement( node.var, `@createText( ${stringify(node.data)} )`, diff --git a/src/generators/dom/visitors/shared/isDomNode.ts b/src/generators/dom/visitors/shared/isDomNode.ts new file mode 100644 index 0000000000..256aa65f4e --- /dev/null +++ b/src/generators/dom/visitors/shared/isDomNode.ts @@ -0,0 +1,5 @@ +import { Node } from '../../../../interfaces'; + +export default function isDomNode(node: Node) { + return node.type === 'Element' || node.type === 'Text' || node.type === 'MustacheTag'; +} \ No newline at end of file From 370327cccde884daf68a9cb80da6b544076e9b3a Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 30 Aug 2017 11:02:14 -0400 Subject: [PATCH 07/10] more tidying up --- src/generators/dom/preprocess.ts | 9 +++------ src/generators/dom/visitors/Text.ts | 2 +- src/generators/dom/visitors/YieldTag.ts | 19 ------------------- src/generators/dom/visitors/index.ts | 4 +--- 4 files changed, 5 insertions(+), 29 deletions(-) delete mode 100644 src/generators/dom/visitors/YieldTag.ts diff --git a/src/generators/dom/preprocess.ts b/src/generators/dom/preprocess.ts index b91aacd266..0ac58adfa8 100644 --- a/src/generators/dom/preprocess.ts +++ b/src/generators/dom/preprocess.ts @@ -75,14 +75,11 @@ const preprocessors = { componentStack: Node[], stripWhitespace: boolean ) => { - node._state = getChildState(state); - - if (!/\S/.test(node.data)) { - if (state.namespace) return; - if (elementsWithoutText.has(state.parentNodeName)) return; + if (!/\S/.test(node.data) && (state.namespace || elementsWithoutText.has(state.parentNodeName))) { + node.shouldSkip = true; + return; } - node.shouldCreate = true; node.var = block.getUniqueName(`text`); }, diff --git a/src/generators/dom/visitors/Text.ts b/src/generators/dom/visitors/Text.ts index d4d423d546..cc510521c3 100644 --- a/src/generators/dom/visitors/Text.ts +++ b/src/generators/dom/visitors/Text.ts @@ -10,7 +10,7 @@ export default function visitText( state: State, node: Node ) { - if (!node.shouldCreate) return; + if (node.shouldSkip) return; block.addElement( node.var, diff --git a/src/generators/dom/visitors/YieldTag.ts b/src/generators/dom/visitors/YieldTag.ts deleted file mode 100644 index dbfee33f29..0000000000 --- a/src/generators/dom/visitors/YieldTag.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { DomGenerator } from '../index'; -import Block from '../Block'; -import { State } from '../interfaces'; - -export default function visitYieldTag( - generator: DomGenerator, - block: Block, - state: State -) { - const parentNode = state.parentNode || '#target'; - - block.builders.mount.addLine( - `if ( #component._yield ) #component._yield.mount( ${parentNode}, null );` - ); - - block.builders.unmount.addLine( - `if ( #component._yield ) #component._yield.unmount();` - ); -} diff --git a/src/generators/dom/visitors/index.ts b/src/generators/dom/visitors/index.ts index 3a6a1d02cf..74a272d1dd 100644 --- a/src/generators/dom/visitors/index.ts +++ b/src/generators/dom/visitors/index.ts @@ -4,7 +4,6 @@ import IfBlock from './IfBlock'; import MustacheTag from './MustacheTag'; import RawMustacheTag from './RawMustacheTag'; import Text from './Text'; -import YieldTag from './YieldTag'; import { Visitor } from '../interfaces'; const visitors: Record = { @@ -13,8 +12,7 @@ const visitors: Record = { IfBlock, MustacheTag, RawMustacheTag, - Text, - YieldTag, + Text }; export default visitors; \ No newline at end of file From 6e9f9633b0ecd0477fa8909a826a77e161586086 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 30 Aug 2017 11:31:58 -0400 Subject: [PATCH 08/10] set all node.var names in preprocess --- src/generators/dom/interfaces.ts | 3 --- src/generators/dom/preprocess.ts | 14 +++++++++----- src/generators/dom/visitors/EachBlock.ts | 3 ++- src/generators/dom/visitors/IfBlock.ts | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/generators/dom/interfaces.ts b/src/generators/dom/interfaces.ts index 37dc449d1d..a925fb1b91 100644 --- a/src/generators/dom/interfaces.ts +++ b/src/generators/dom/interfaces.ts @@ -3,13 +3,10 @@ import Block from './Block'; import { Node } from '../../interfaces'; export interface State { - name?: string; namespace: string; parentNode: string; parentNodes: string; - isTopLevel: boolean; parentNodeName?: string; - basename?: string; inEachBlock?: boolean; allUsedContexts?: string[]; usesComponent?: boolean; diff --git a/src/generators/dom/preprocess.ts b/src/generators/dom/preprocess.ts index 0ac58adfa8..3aa78249c3 100644 --- a/src/generators/dom/preprocess.ts +++ b/src/generators/dom/preprocess.ts @@ -16,7 +16,7 @@ function getChildState(parent: State, child = {}) { return assign( {}, parent, - { name: null, parentNode: null, parentNodes: 'nodes' }, + { parentNode: null, parentNodes: 'nodes' }, child || {} ); } @@ -45,10 +45,10 @@ const preprocessors = { componentStack: Node[], stripWhitespace: boolean ) => { + node.var = block.getUniqueName('text'); + const dependencies = block.findDependencies(node.expression); block.addDependencies(dependencies); - - node.var = block.getUniqueName('text'); }, RawMustacheTag: ( @@ -60,10 +60,10 @@ const preprocessors = { componentStack: Node[], stripWhitespace: boolean ) => { + node.var = block.getUniqueName('raw'); + const dependencies = block.findDependencies(node.expression); block.addDependencies(dependencies); - - node.var = block.getUniqueName('raw'); }, Text: ( @@ -100,6 +100,8 @@ const preprocessors = { let hasOutros = false; function attachBlocks(node: Node) { + node.var = block.getUniqueName(`if_block`); + const dependencies = block.findDependencies(node.expression); block.addDependencies(dependencies); @@ -171,6 +173,8 @@ const preprocessors = { stripWhitespace: boolean, nextSibling: Node ) => { + node.var = block.getUniqueName(`each_block`); + const dependencies = block.findDependencies(node.expression); block.addDependencies(dependencies); diff --git a/src/generators/dom/visitors/EachBlock.ts b/src/generators/dom/visitors/EachBlock.ts index 7796f2414c..0c769f2e78 100644 --- a/src/generators/dom/visitors/EachBlock.ts +++ b/src/generators/dom/visitors/EachBlock.ts @@ -14,7 +14,8 @@ export default function visitEachBlock( elementStack: Node[], componentStack: Node[] ) { - const each_block = generator.getUniqueName(`each_block`); + const each_block = node.var; + const create_each_block = node._block.name; const each_block_value = node._block.listName; const iterations = block.getUniqueName(`${each_block}_iterations`); diff --git a/src/generators/dom/visitors/IfBlock.ts b/src/generators/dom/visitors/IfBlock.ts index 3ce27599ff..97747b0083 100644 --- a/src/generators/dom/visitors/IfBlock.ts +++ b/src/generators/dom/visitors/IfBlock.ts @@ -78,7 +78,7 @@ export default function visitIfBlock( elementStack: Node[], componentStack: Node[] ) { - const name = generator.getUniqueName(`if_block`); + const name = node.var; const needsAnchor = node.next ? !isDomNode(node.next) : !state.parentNode; const anchor = needsAnchor From d1a6f9c11e8771fe1dbae1aa67db9281362a5255 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 30 Aug 2017 11:42:40 -0400 Subject: [PATCH 09/10] only use comments around as necessary --- src/generators/dom/visitors/Slot.ts | 60 +++++++++++++++++++++-------- src/shared/dom.js | 13 +++++++ 2 files changed, 57 insertions(+), 16 deletions(-) diff --git a/src/generators/dom/visitors/Slot.ts b/src/generators/dom/visitors/Slot.ts index 85f53f2ca3..5249879f40 100644 --- a/src/generators/dom/visitors/Slot.ts +++ b/src/generators/dom/visitors/Slot.ts @@ -20,11 +20,19 @@ export default function visitSlot( const content_name = block.getUniqueName(`slot_content_${slotName}`); block.addVariable(content_name, `#component._slotted.${slotName}`); - // TODO use surrounds as anchors where possible, a la if/each blocks - const before = block.getUniqueName(`${content_name}_before`); - const after = block.getUniqueName(`${content_name}_after`); - block.addVariable(before); - block.addVariable(after); + const needsAnchorBefore = node.prev ? node.prev.type !== 'Element' : !state.parentNode; + const needsAnchorAfter = node.next ? node.next.type !== 'Element' : !state.parentNode; + + const anchorBefore = needsAnchorBefore + ? block.getUniqueName(`${content_name}_before`) + : (node.prev && node.prev.var) || 'null'; + + const anchorAfter = needsAnchorAfter + ? block.getUniqueName(`${content_name}_after`) + : (node.next && node.next.var) || 'null'; + + if (needsAnchorBefore) block.addVariable(anchorBefore); + if (needsAnchorAfter) block.addVariable(anchorAfter); block.builders.create.pushCondition(`!${content_name}`); block.builders.hydrate.pushCondition(`!${content_name}`); @@ -46,17 +54,17 @@ export default function visitSlot( if (state.parentNode) { block.builders.mount.addBlock(deindent` if (${content_name}) { - @appendNode(${before} || (${before} = @createComment()), ${state.parentNode}); + ${needsAnchorBefore && `@appendNode(${anchorBefore} || (${anchorBefore} = @createComment()), ${state.parentNode});`} @appendNode(${content_name}, ${state.parentNode}); - @appendNode(${after} || (${after} = @createComment()), ${state.parentNode}); + ${needsAnchorAfter && `@appendNode(${anchorAfter} || (${anchorAfter} = @createComment()), ${state.parentNode});`} } `); } else { block.builders.mount.addBlock(deindent` if (${content_name}) { - @insertNode(${before} || (${before} = @createComment()), #target, anchor); + ${needsAnchorBefore && `@insertNode(${anchorBefore} || (${anchorBefore} = @createComment()), #target, anchor);`} @insertNode(${content_name}, #target, anchor); - @insertNode(${after} || (${after} = @createComment()), #target, anchor); + ${needsAnchorAfter && `@insertNode(${anchorAfter} || (${anchorAfter} = @createComment()), #target, anchor);`} } `); } @@ -66,11 +74,31 @@ export default function visitSlot( // TODO so that this can work with public API, component._slotted should // be all fragments, derived from options.slots. Not === options.slots // TODO can we use an else here? - block.builders.unmount.addBlock(deindent` - if (${content_name}) { - @reinsertBetween(${before}, ${after}, ${content_name}); - @detachNode(${before}); - @detachNode(${after}); - } - `); + if (anchorBefore === 'null' && anchorAfter === 'null') { + block.builders.unmount.addBlock(deindent` + if (${content_name}) { + @reinsertChildren(${state.parentNode}, ${content_name}); + } + `); + } else if (anchorBefore === 'null') { + block.builders.unmount.addBlock(deindent` + if (${content_name}) { + @reinsertBefore(${anchorAfter}, ${content_name}); + } + `); + } else if (anchorAfter === 'null') { + block.builders.unmount.addBlock(deindent` + if (${content_name}) { + @reinsertAfter(${anchorBefore}, ${content_name}); + } + `); + } else { + block.builders.unmount.addBlock(deindent` + if (${content_name}) { + @reinsertBetween(${anchorBefore}, ${anchorAfter}, ${content_name}); + @detachNode(${anchorBefore}); + @detachNode(${anchorAfter}); + } + `); + } } diff --git a/src/shared/dom.js b/src/shared/dom.js index 86da87c726..0372d96fdc 100644 --- a/src/shared/dom.js +++ b/src/shared/dom.js @@ -34,6 +34,19 @@ export function reinsertBetween(before, after, target) { } } +export function reinsertChildren(parent, target) { + while (parent.firstChild) target.appendChild(parent.firstChild); +} + +export function reinsertAfter(before, target) { + while (before.nextSibling) target.appendChild(before.nextSibling); +} + +export function reinsertBefore(after, target) { + var parent = after.parentNode; + while (parent.firstChild !== after) target.appendChild(parent.firstChild); +} + // TODO this is out of date export function destroyEach(iterations, detach, start) { for (var i = start; i < iterations.length; i += 1) { From c682e365f610b7a6faa83e853efa47477f7321b0 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 30 Aug 2017 11:54:29 -0400 Subject: [PATCH 10/10] optimise case where we can use innerHTML --- src/generators/dom/visitors/RawMustacheTag.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/generators/dom/visitors/RawMustacheTag.ts b/src/generators/dom/visitors/RawMustacheTag.ts index ab0ff7991d..bbdbc5b26f 100644 --- a/src/generators/dom/visitors/RawMustacheTag.ts +++ b/src/generators/dom/visitors/RawMustacheTag.ts @@ -26,8 +26,10 @@ export default function visitRawMustacheTag( let detach: string; let insert: (content: string) => string; + let useInnerHTML = false; if (anchorBefore === 'null' && anchorAfter === 'null') { + useInnerHTML = true; detach = `${state.parentNode}.innerHTML = '';`; insert = content => `${state.parentNode}.innerHTML = ${content};`; } else if (anchorBefore === 'null') { @@ -48,7 +50,7 @@ export default function visitRawMustacheTag( node, name, content => deindent` - ${detach} + ${!useInnerHTML && detach} ${insert(content)} ` );