diff --git a/src/generators/dom/Block.ts b/src/generators/dom/Block.ts index 9523d42e7c..cd0943cfcb 100644 --- a/src/generators/dom/Block.ts +++ b/src/generators/dom/Block.ts @@ -42,10 +42,10 @@ export default class Block { builders: { create: CodeBuilder; mount: CodeBuilder; - update: CodeBuilder; intro: CodeBuilder; + update: CodeBuilder; outro: CodeBuilder; - detach: CodeBuilder; + unmount: CodeBuilder; detachRaw: CodeBuilder; destroy: CodeBuilder; } @@ -88,10 +88,10 @@ export default class Block { this.builders = { create: new CodeBuilder(), mount: new CodeBuilder(), - update: new CodeBuilder(), intro: new CodeBuilder(), + update: new CodeBuilder(), outro: new CodeBuilder(), - detach: new CodeBuilder(), + unmount: new CodeBuilder(), detachRaw: new CodeBuilder(), destroy: new CodeBuilder() }; @@ -130,7 +130,7 @@ export default class Block { } if ( isToplevel ) { - this.builders.detach.addLine( `${this.generator.helper( 'detachNode' )}( ${name} );` ); + this.builders.unmount.addLine( `${this.generator.helper( 'detachNode' )}( ${name} );` ); } } @@ -202,15 +202,7 @@ export default class Block { // minor hack – we need to ensure that any {{{triples}}} are detached // first, so we append normal detach statements to detachRaw - this.builders.detachRaw.addBlock( this.builders.detach ); - - if ( !this.builders.detachRaw.isEmpty() ) { - this.builders.destroy.addBlock( deindent` - if ( detach ) { - ${this.builders.detachRaw} - } - ` ); - } + this.builders.detachRaw.addBlock( this.builders.unmount ); // TODO reverse this, using addBlockAtStart? const properties = new CodeBuilder(); @@ -290,11 +282,22 @@ export default class Block { } } + if ( this.builders.detachRaw.isEmpty() ) { + properties.addBlock( `unmount: ${this.generator.helper('noop')},`); + } else { + properties.addBlock( deindent` + unmount: function () { + ${this.builders.detachRaw} + }, + ` ); + } + if ( this.builders.destroy.isEmpty() ) { properties.addBlock( `destroy: ${this.generator.helper( 'noop' )}` ); } else { properties.addBlock( deindent` destroy: function ( detach ) { + ${!this.builders.detachRaw.isEmpty() && `if ( detach ) this.unmount();`} ${this.builders.destroy} } ` ); diff --git a/src/generators/dom/index.ts b/src/generators/dom/index.ts index dc112bb4ca..0b8167e752 100644 --- a/src/generators/dom/index.ts +++ b/src/generators/dom/index.ts @@ -191,7 +191,8 @@ export default function dom ( parsed: Parsed, source: string, options: CompileOp this.fire( 'destroy' ); ${templateProperties.ondestroy && `${generator.alias( 'template' )}.ondestroy.call( this );`} - this._fragment.destroy( detach !== false ); + if ( detach !== false ) this._fragment.unmount(); + this._fragment.destroy( false ); // TODO no arguments to destroy this._fragment = null; this._state = {}; diff --git a/src/generators/dom/visitors/Component/Component.ts b/src/generators/dom/visitors/Component/Component.ts index 3877e64a7d..2f05389cec 100644 --- a/src/generators/dom/visitors/Component/Component.ts +++ b/src/generators/dom/visitors/Component/Component.ts @@ -56,7 +56,7 @@ export default function visitComponent ( generator: DomGenerator, block: Block, update: new CodeBuilder() }; - const isToplevel = !state.parentNode; + const isTopLevel = !state.parentNode; generator.hasComponents = true; @@ -95,7 +95,7 @@ export default function visitComponent ( generator: DomGenerator, block: Block, } const componentInitProperties = [ - `target: ${!isToplevel ? state.parentNode: 'null'}`, + `target: ${!isTopLevel ? state.parentNode: 'null'}`, `_root: ${block.component}._root` ]; @@ -121,6 +121,10 @@ export default function visitComponent ( generator: DomGenerator, block: Block, ); } + block.builders.destroy.addLine( + `${yieldFragment}.destroy( false );` + ); + componentInitProperties.push( `_yield: ${yieldFragment}`); } @@ -157,7 +161,7 @@ export default function visitComponent ( generator: DomGenerator, block: Block, }); ` ); - if ( isToplevel ) { + if ( isTopLevel ) { block.builders.mount.addLine( `${name}._fragment.mount( ${block.target}, anchor );` ); } @@ -183,7 +187,8 @@ export default function visitComponent ( generator: DomGenerator, block: Block, ` ); } - block.builders.destroy.addLine( `${name}.destroy( ${isToplevel ? 'detach' : 'false'} );` ); + if ( isTopLevel ) block.builders.unmount.addLine( `${name}._fragment.unmount();` ); + block.builders.destroy.addLine( `${name}.destroy( false );` ); block.builders.create.addBlock( local.create ); if ( !local.update.isEmpty() ) block.builders.update.addBlock( local.update ); diff --git a/src/generators/dom/visitors/EachBlock.ts b/src/generators/dom/visitors/EachBlock.ts index 212f1af8b3..da2f322b3a 100644 --- a/src/generators/dom/visitors/EachBlock.ts +++ b/src/generators/dom/visitors/EachBlock.ts @@ -64,7 +64,8 @@ export default function visitEachBlock ( generator: DomGenerator, block: Block, ${each_block_else} = ${node.else._block.name}( ${params}, ${block.component} ); ${each_block_else}.${mountOrIntro}( ${parentNode}, ${anchor} ); } else if ( ${each_block_else} ) { - ${each_block_else}.destroy( true ); + ${each_block_else}.unmount(); + ${each_block_else}.destroy(); ${each_block_else} = null; } ` ); @@ -72,7 +73,8 @@ export default function visitEachBlock ( generator: DomGenerator, block: Block, block.builders.update.addBlock( deindent` if ( ${each_block_value}.length ) { if ( ${each_block_else} ) { - ${each_block_else}.destroy( true ); + ${each_block_else}.unmount(); + ${each_block_else}.destroy(); ${each_block_else} = null; } } else if ( !${each_block_else} ) { @@ -82,11 +84,12 @@ export default function visitEachBlock ( generator: DomGenerator, block: Block, ` ); } + block.builders.unmount.addLine( + `if ( ${each_block_else} ) ${each_block_else}.unmount()` + ); block.builders.destroy.addBlock( deindent` - if ( ${each_block_else} ) { - ${each_block_else}.destroy( ${isToplevel ? 'detach' : 'false'} ); - } + if ( ${each_block_else} ) ${each_block_else}.destroy( false ); ` ); } @@ -154,7 +157,8 @@ function keyed ( generator: DomGenerator, block: Block, state: State, node: Node block.builders.create.addBlock( deindent` function ${fn} ( iteration ) { iteration.outro( function () { - iteration.destroy( true ); + iteration.unmount(); + iteration.destroy(); ${lookup}[iteration.key] = null; }); } @@ -176,7 +180,8 @@ function keyed ( generator: DomGenerator, block: Block, state: State, node: Node const fn = block.getUniqueName( `${each_block}_destroy` ); block.builders.create.addBlock( deindent` function ${fn} ( iteration ) { - iteration.destroy( true ); + iteration.unmount(); + iteration.destroy(); ${lookup}[iteration.key] = null; } ` ); @@ -262,10 +267,20 @@ function keyed ( generator: DomGenerator, block: Block, state: State, node: Node ${head} = ${lookup}[${each_block_value}[0] && ${each_block_value}[0].${node.key}]; ` ); + if ( !state.parentNode ) { + block.builders.unmount.addBlock( deindent` + var ${iteration} = ${head}; + while ( ${iteration} ) { + ${iteration}.unmount(); + ${iteration} = ${iteration}.next; + } + ` ); + } + block.builders.destroy.addBlock( deindent` var ${iteration} = ${head}; while ( ${iteration} ) { - ${iteration}.destroy( ${state.parentNode ? 'false' : 'detach'} ); + ${iteration}.destroy( false ); ${iteration} = ${iteration}.next; } ` ); @@ -334,7 +349,8 @@ function unkeyed ( generator: DomGenerator, block: Block, state: State, node: No function ${outro} ( i ) { if ( ${iterations}[i] ) { ${iterations}[i].outro( function () { - ${iterations}[i].destroy( true ); + ${iterations}[i].unmount(); + ${iterations}[i].destroy(); ${iterations}[i] = null; }); } @@ -343,7 +359,10 @@ function unkeyed ( generator: DomGenerator, block: Block, state: State, node: No for ( ; ${i} < ${iterations}.length; ${i} += 1 ) ${outro}( ${i} ); ` : deindent` - ${generator.helper( 'destroyEach' )}( ${iterations}, true, ${each_block_value}.length ); + for ( ; ${i} < ${iterations}.length; ${i} += 1 ) { + ${iterations}[${i}].unmount(); + ${iterations}[${i}].destroy(); + } ${iterations}.length = ${each_block_value}.length; `; @@ -360,7 +379,13 @@ function unkeyed ( generator: DomGenerator, block: Block, state: State, node: No ` ); } + block.builders.unmount.addBlock( deindent` + for ( var ${i} = 0; ${i} < ${iterations}.length; ${i} += 1 ) { + ${iterations}[${i}].unmount(); + } + ` ); + block.builders.destroy.addBlock( - `${generator.helper( 'destroyEach' )}( ${iterations}, ${state.parentNode ? 'false' : 'detach'}, 0 );` + `${generator.helper( 'destroyEach' )}( ${iterations}, false, 0 );` ); } diff --git a/src/generators/dom/visitors/Element/Element.ts b/src/generators/dom/visitors/Element/Element.ts index a5974f9485..40cfb9d9f5 100644 --- a/src/generators/dom/visitors/Element/Element.ts +++ b/src/generators/dom/visitors/Element/Element.ts @@ -103,7 +103,7 @@ export default function visitElement ( generator: DomGenerator, block: Block, st if ( !state.parentNode ) { // TODO we eventually need to consider what happens to elements // that belong to the same outgroup as an outroing element... - block.builders.detach.addLine( `${generator.helper( 'detachNode' )}( ${name} );` ); + block.builders.unmount.addLine( `${generator.helper( 'detachNode' )}( ${name} );` ); } if ( node.name !== 'select' ) { diff --git a/src/generators/dom/visitors/IfBlock.ts b/src/generators/dom/visitors/IfBlock.ts index a503c75361..1105617a3c 100644 --- a/src/generators/dom/visitors/IfBlock.ts +++ b/src/generators/dom/visitors/IfBlock.ts @@ -135,12 +135,14 @@ function simple ( generator: DomGenerator, block: Block, state: State, node: Nod const exit = branch.hasOutroMethod ? deindent` ${name}.outro( function () { - ${name}.destroy( true ); + ${name}.unmount(); + ${name}.destroy(); ${name} = null; }); ` : deindent` - ${name}.destroy( true ); + ${name}.unmount(); + ${name}.destroy(); ${name} = null; `; @@ -152,8 +154,12 @@ function simple ( generator: DomGenerator, block: Block, state: State, node: Nod } ` ); + block.builders.unmount.addLine( + `${if_name}${name}.unmount();` + ); + block.builders.destroy.addLine( - `${if_name}${name}.destroy( ${state.parentNode ? 'false' : 'detach'} );` + `${if_name}${name}.destroy( false );` ); } @@ -185,7 +191,10 @@ function compound ( generator: DomGenerator, block: Block, state: State, node: N const parentNode = state.parentNode || `${anchor}.parentNode`; const changeBlock = deindent` - ${if_name}${name}.destroy( true ); + ${if_name}{ + ${name}.unmount(); + ${name}.destroy(); + } ${name} = ${current_block_and}${current_block}( ${params}, ${block.component} ); ${if_name}${name}.${mountOrIntro}( ${parentNode}, ${anchor} ); `; @@ -207,7 +216,10 @@ function compound ( generator: DomGenerator, block: Block, state: State, node: N } block.builders.destroy.addLine( - `${if_name}${name}.destroy( ${state.parentNode ? 'false' : 'detach'} );` + `${if_name}{ + ${name}.unmount(); + ${name}.destroy(); + }` ); } diff --git a/src/generators/dom/visitors/YieldTag.ts b/src/generators/dom/visitors/YieldTag.ts index 1482c5dae5..5a665b041f 100644 --- a/src/generators/dom/visitors/YieldTag.ts +++ b/src/generators/dom/visitors/YieldTag.ts @@ -10,6 +10,6 @@ export default function visitYieldTag ( generator: DomGenerator, block: Block, s ); block.builders.destroy.addLine( - `if ( ${block.component}._yield ) ${block.component}._yield.destroy( detach );` + `if ( ${block.component}._yield ) ${block.component}._yield.unmount();` ); } \ No newline at end of file diff --git a/test/runtime/samples/nested-component-if/Inner.html b/test/runtime/samples/component-yield-nested-if/Inner.html similarity index 100% rename from test/runtime/samples/nested-component-if/Inner.html rename to test/runtime/samples/component-yield-nested-if/Inner.html diff --git a/test/runtime/samples/nested-component-if/Outer.html b/test/runtime/samples/component-yield-nested-if/Outer.html similarity index 100% rename from test/runtime/samples/nested-component-if/Outer.html rename to test/runtime/samples/component-yield-nested-if/Outer.html diff --git a/test/runtime/samples/component-yield-nested-if/_config.js b/test/runtime/samples/component-yield-nested-if/_config.js new file mode 100644 index 0000000000..44548b2f16 --- /dev/null +++ b/test/runtime/samples/component-yield-nested-if/_config.js @@ -0,0 +1,14 @@ +export default { + html: ` + One + Inner + `, + + test ( assert, component, target ) { + component.set({ foo: false }); + assert.htmlEqual( target.innerHTML, `` ); + + component.set({ foo: true }); + assert.htmlEqual( target.innerHTML, `One\nInner` ); + } +}; diff --git a/test/runtime/samples/nested-component-if/main.html b/test/runtime/samples/component-yield-nested-if/main.html similarity index 100% rename from test/runtime/samples/nested-component-if/main.html rename to test/runtime/samples/component-yield-nested-if/main.html diff --git a/test/runtime/samples/nested-component-if/_config.js b/test/runtime/samples/nested-component-if/_config.js deleted file mode 100644 index afe9636c20..0000000000 --- a/test/runtime/samples/nested-component-if/_config.js +++ /dev/null @@ -1,8 +0,0 @@ -export default { - - test ( assert, component ) { - component.set({ foo: false }); - component.set({ foo: true }); - } - -}; diff --git a/test/server-side-rendering/index.js b/test/server-side-rendering/index.js index 66f2699fff..c5836041f4 100644 --- a/test/server-side-rendering/index.js +++ b/test/server-side-rendering/index.js @@ -107,10 +107,12 @@ describe( 'ssr', () => { } } - const resolved = require.resolve( `../runtime/samples/${dir}/main.html` ); - delete require.cache[ resolved ]; + fs.readdirSync( `test/runtime/samples/${dir}` ).forEach( file => { + const resolved = require.resolve( `../runtime/samples/${dir}/${file}` ); + delete require.cache[ resolved ]; + }); - const component = require( resolved ); + const component = require( `../runtime/samples/${dir}/main.html` ); let html; try {