Merge pull request from sveltejs/gh-472

Only update dynamic subtrees
pull/497/head
Rich Harris 8 years ago committed by GitHub
commit fc31d39a64

@ -63,17 +63,14 @@ export default class Generator {
} }
contextualise ( block, expression, context, isEventHandler ) { contextualise ( block, expression, context, isEventHandler ) {
if ( expression._contextualised ) return expression._contextualised;
this.addSourcemapLocations( expression ); this.addSourcemapLocations( expression );
const usedContexts = []; const usedContexts = [];
const dependencies = [];
const { code, helpers } = this; const { code, helpers } = this;
const { contextDependencies, contexts, indexes } = block; const { contexts, indexes } = block;
let scope = annotateWithScopes( expression ); let scope = annotateWithScopes( expression ); // TODO this already happens in findDependencies
let lexicalDepth = 0; let lexicalDepth = 0;
const self = this; const self = this;
@ -110,7 +107,6 @@ export default class Generator {
code.overwrite( node.start, node.start + name.length, contextName, true ); code.overwrite( node.start, node.start + name.length, contextName, true );
} }
dependencies.push( ...contextDependencies.get( name ) );
if ( !~usedContexts.indexOf( name ) ) usedContexts.push( name ); if ( !~usedContexts.indexOf( name ) ) usedContexts.push( name );
} }
@ -135,7 +131,6 @@ export default class Generator {
code.prependRight( node.start, `root.` ); code.prependRight( node.start, `root.` );
} }
dependencies.push( name );
if ( !~usedContexts.indexOf( 'root' ) ) usedContexts.push( 'root' ); if ( !~usedContexts.indexOf( 'root' ) ) usedContexts.push( 'root' );
} }
@ -149,19 +144,52 @@ export default class Generator {
} }
}); });
return {
dependencies: expression._dependencies, // TODO probably a better way to do this
contexts: usedContexts,
snippet: `[✂${expression.start}-${expression.end}✂]`
};
}
findDependencies ( contextDependencies, expression ) {
if ( expression._dependencies ) return expression._dependencies;
let scope = annotateWithScopes( expression );
const dependencies = [];
walk( expression, {
enter ( node, parent ) {
if ( node._scope ) {
scope = node._scope;
return;
}
if ( isReference( node, parent ) ) {
const { name } = flattenReference( node );
if ( scope.has( name ) ) return;
if ( contextDependencies.has( name ) ) {
dependencies.push( ...contextDependencies.get( name ) );
} else {
dependencies.push( name );
}
this.skip();
}
},
leave ( node ) {
if ( node._scope ) scope = scope.parent;
}
});
dependencies.forEach( name => { dependencies.forEach( name => {
if ( !globalWhitelist.has( name ) ) { if ( !globalWhitelist.has( name ) ) {
this.expectedProperties.add( name ); this.expectedProperties.add( name );
} }
}); });
expression._contextualised = { return ( expression._dependencies = dependencies );
dependencies,
contexts: usedContexts,
snippet: `[✂${expression.start}-${expression.end}✂]`
};
return expression._contextualised;
} }
generate ( result, options, { name, format } ) { generate ( result, options, { name, format } ) {

@ -34,6 +34,8 @@ export default class Block {
// unique names // unique names
this.component = this.getUniqueName( 'component' ); this.component = this.getUniqueName( 'component' );
this.target = this.getUniqueName( 'target' ); this.target = this.getUniqueName( 'target' );
this.hasUpdateMethod = false; // determined later
} }
addDependencies ( dependencies ) { addDependencies ( dependencies ) {
@ -72,6 +74,10 @@ export default class Block {
this.addElement( name, renderStatement, parentNode, true ); this.addElement( name, renderStatement, parentNode, true );
} }
findDependencies ( expression ) {
return this.generator.findDependencies( this.contextDependencies, expression );
}
mount ( name, parentNode ) { mount ( name, parentNode ) {
if ( parentNode ) { if ( parentNode ) {
this.builders.create.addLine( `${this.generator.helper( 'appendNode' )}( ${name}, ${parentNode} );` ); this.builders.create.addLine( `${this.generator.helper( 'appendNode' )}( ${name}, ${parentNode} );` );
@ -115,6 +121,7 @@ export default class Block {
` ); ` );
} }
if ( this.hasUpdateMethod ) {
if ( this.builders.update.isEmpty() ) { if ( this.builders.update.isEmpty() ) {
properties.addBlock( `update: ${this.generator.helper( 'noop' )},` ); properties.addBlock( `update: ${this.generator.helper( 'noop' )},` );
} else { } else {
@ -125,6 +132,7 @@ export default class Block {
}, },
` ); ` );
} }
}
if ( this.builders.destroy.isEmpty() ) { if ( this.builders.destroy.isEmpty() ) {
properties.addBlock( `destroy: ${this.generator.helper( 'noop' )}` ); properties.addBlock( `destroy: ${this.generator.helper( 'noop' )}` );
@ -146,9 +154,4 @@ export default class Block {
} }
`; `;
} }
tmp () {
if ( !this._tmp ) this._tmp = this.getUniqueName( 'tmp' );
return this._tmp;
}
} }

@ -24,10 +24,6 @@ class DomGenerator extends Generator {
}; };
} }
addBlock ( block ) {
this.blocks.push( block );
}
helper ( name ) { helper ( name ) {
if ( this.options.dev && `${name}Dev` in shared ) { if ( this.options.dev && `${name}Dev` in shared ) {
name = `${name}Dev`; name = `${name}Dev`;
@ -59,8 +55,6 @@ export default function dom ( parsed, source, options ) {
visit( generator, block, state, node ); visit( generator, block, state, node );
}); });
generator.addBlock( block );
const builders = { const builders = {
main: new CodeBuilder(), main: new CodeBuilder(),
init: new CodeBuilder(), init: new CodeBuilder(),
@ -113,12 +107,9 @@ export default function dom ( parsed, source, options ) {
builders._set.addLine( `${generator.alias( 'recompute' )}( this._state, newState, oldState, false )` ); builders._set.addLine( `${generator.alias( 'recompute' )}( this._state, newState, oldState, false )` );
} }
// TODO is the `if` necessary? builders._set.addLine( `${generator.helper( 'dispatchObservers' )}( this, this._observers.pre, newState, oldState );` );
builders._set.addBlock( deindent` if ( block.hasUpdateMethod ) builders._set.addLine( `if ( this._fragment ) this._fragment.update( newState, this._state );` ); // TODO is the condition necessary?
${generator.helper( 'dispatchObservers' )}( this, this._observers.pre, newState, oldState ); builders._set.addLine( `${generator.helper( 'dispatchObservers' )}( this, this._observers.post, newState, oldState );` );
if ( this._fragment ) this._fragment.update( newState, this._state );
${generator.helper( 'dispatchObservers' )}( this, this._observers.post, newState, oldState );
` );
if ( hasJs ) { if ( hasJs ) {
builders.main.addBlock( `[✂${parsed.js.content.start}-${parsed.js.content.end}✂]` ); builders.main.addBlock( `[✂${parsed.js.content.start}-${parsed.js.content.end}✂]` );
@ -137,8 +128,9 @@ export default function dom ( parsed, source, options ) {
` ); ` );
} }
let i = generator.blocks.length; generator.blocks.forEach( block => {
while ( i-- ) builders.main.addBlock( generator.blocks[i].render() ); builders.main.addBlock( block.render() );
});
builders.init.addLine( `this._torndown = false;` ); builders.init.addLine( `this._torndown = false;` );

@ -6,21 +6,29 @@ function isElseIf ( node ) {
const preprocessors = { const preprocessors = {
MustacheTag: ( generator, block, node ) => { MustacheTag: ( generator, block, node ) => {
const { dependencies } = block.contextualise( node.expression ); const dependencies = block.findDependencies( node.expression );
block.addDependencies( dependencies ); block.addDependencies( dependencies );
}, },
IfBlock: ( generator, block, node ) => { IfBlock: ( generator, block, node ) => {
const blocks = [];
let dynamic = false;
function attachBlocks ( node ) { function attachBlocks ( node ) {
const { dependencies } = block.contextualise( node.expression ); const dependencies = block.findDependencies( node.expression );
block.addDependencies( dependencies ); block.addDependencies( dependencies );
node._block = block.child({ node._block = block.child({
name: generator.getUniqueName( `create_if_block` ) name: generator.getUniqueName( `create_if_block` )
}); });
blocks.push( node._block );
preprocessChildren( generator, node._block, node.children ); preprocessChildren( generator, node._block, node.children );
if ( node._block.dependencies.size > 0 ) {
dynamic = true;
block.addDependencies( node._block.dependencies ); block.addDependencies( node._block.dependencies );
}
if ( isElseIf( node.else ) ) { if ( isElseIf( node.else ) ) {
attachBlocks( node.else.children[0] ); attachBlocks( node.else.children[0] );
@ -29,16 +37,27 @@ const preprocessors = {
name: generator.getUniqueName( `create_if_block` ) name: generator.getUniqueName( `create_if_block` )
}); });
blocks.push( node.else._block );
preprocessChildren( generator, node.else._block, node.else.children ); preprocessChildren( generator, node.else._block, node.else.children );
if ( node.else._block.dependencies.size > 0 ) {
dynamic = true;
block.addDependencies( node.else._block.dependencies ); block.addDependencies( node.else._block.dependencies );
} }
} }
}
attachBlocks( node ); attachBlocks( node );
blocks.forEach( block => {
block.hasUpdateMethod = dynamic;
});
generator.blocks.push( ...blocks );
}, },
EachBlock: ( generator, block, node ) => { EachBlock: ( generator, block, node ) => {
const { dependencies } = block.contextualise( node.expression ); const dependencies = block.findDependencies( node.expression );
block.addDependencies( dependencies ); block.addDependencies( dependencies );
const indexNames = new Map( block.indexNames ); const indexNames = new Map( block.indexNames );
@ -77,15 +96,19 @@ const preprocessors = {
params: block.params.concat( listName, context, indexName ) params: block.params.concat( listName, context, indexName )
}); });
generator.blocks.push( node._block );
preprocessChildren( generator, node._block, node.children ); preprocessChildren( generator, node._block, node.children );
block.addDependencies( node._block.dependencies ); block.addDependencies( node._block.dependencies );
node._block.hasUpdateMethod = node._block.dependencies.size > 0;
if ( node.else ) { if ( node.else ) {
node.else._block = block.child({ node.else._block = block.child({
name: generator.getUniqueName( `${node._block.name}_else` ) name: generator.getUniqueName( `${node._block.name}_else` )
}); });
generator.blocks.push( node.else._block );
preprocessChildren( generator, node.else._block, node.else.children ); preprocessChildren( generator, node.else._block, node.else.children );
node.else._block.hasUpdateMethod = node.else._block.dependencies.size > 0;
} }
}, },
@ -94,14 +117,14 @@ const preprocessors = {
if ( attribute.type === 'Attribute' && attribute.value !== true ) { if ( attribute.type === 'Attribute' && attribute.value !== true ) {
attribute.value.forEach( chunk => { attribute.value.forEach( chunk => {
if ( chunk.type !== 'Text' ) { if ( chunk.type !== 'Text' ) {
const { dependencies } = block.contextualise( chunk.expression ); const dependencies = block.findDependencies( chunk.expression );
block.addDependencies( dependencies ); block.addDependencies( dependencies );
} }
}); });
} }
else if ( attribute.type === 'Binding' ) { else if ( attribute.type === 'Binding' ) {
const { dependencies } = block.contextualise( attribute.value ); const dependencies = block.findDependencies( attribute.value );
block.addDependencies( dependencies ); block.addDependencies( dependencies );
} }
}); });
@ -115,7 +138,10 @@ const preprocessors = {
name: generator.getUniqueName( `create_${name}_yield_fragment` ) name: generator.getUniqueName( `create_${name}_yield_fragment` )
}); });
generator.blocks.push( node._block );
preprocessChildren( generator, node._block, node.children ); preprocessChildren( generator, node._block, node.children );
block.addDependencies( node._block.dependencies );
node._block.hasUpdateMethod = node._block.dependencies.size > 0;
} }
else { else {
@ -141,6 +167,7 @@ export default function preprocess ( generator, children ) {
contexts: new Map(), contexts: new Map(),
indexes: new Map(), indexes: new Map(),
contextDependencies: new Map(),
params: [ 'root' ], params: [ 'root' ],
indexNames: new Map(), indexNames: new Map(),
@ -149,7 +176,9 @@ export default function preprocess ( generator, children ) {
dependencies: new Set() dependencies: new Set()
}); });
generator.blocks.push( block );
preprocessChildren( generator, block, children ); preprocessChildren( generator, block, children );
block.hasUpdateMethod = block.dependencies.size > 0;
return block; return block;
} }

@ -118,8 +118,6 @@ export default function visitComponent ( generator, block, state, node ) {
); );
componentInitProperties.push( `_yield: ${yieldFragment}`); componentInitProperties.push( `_yield: ${yieldFragment}`);
generator.addBlock( childBlock );
} }
const statements = []; const statements = [];

@ -75,25 +75,23 @@ export default function visitEachBlock ( generator, block, state, node ) {
` ); ` );
} }
const childBlock = node._block;
const childState = Object.assign( {}, state, { const childState = Object.assign( {}, state, {
parentNode: null, parentNode: null,
inEachBlock: true inEachBlock: true
}); });
node.children.forEach( child => { node.children.forEach( child => {
visit( generator, childBlock, childState, child ); visit( generator, node._block, childState, child );
}); });
generator.addBlock( childBlock );
if ( node.else ) { if ( node.else ) {
const childState = Object.assign( {}, state, {
parentNode: null
});
node.else.children.forEach( child => { node.else.children.forEach( child => {
visit( generator, node.else._block, childState, child ); visit( generator, node.else._block, childState, child );
}); });
generator.addBlock( node.else._block );
} }
} }
@ -127,6 +125,13 @@ function keyed ( generator, block, state, node, snippet, { each_block, create_ea
} }
` ); ` );
const consequent = node._block.hasUpdateMethod ?
deindent`
${_iterations}[${i}] = ${_lookup}[ ${key} ] = ${lookup}[ ${key} ];
${_lookup}[ ${key} ].update( changed, ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i} );
` :
`${_iterations}[${i}] = ${_lookup}[ ${key} ] = ${lookup}[ ${key} ];`;
block.builders.update.addBlock( deindent` block.builders.update.addBlock( deindent`
var ${each_block_value} = ${snippet}; var ${each_block_value} = ${snippet};
var ${_iterations} = []; var ${_iterations} = [];
@ -140,8 +145,7 @@ function keyed ( generator, block, state, node, snippet, { each_block, create_ea
var ${key} = ${value}.${node.key}; var ${key} = ${value}.${node.key};
if ( ${lookup}[ ${key} ] ) { if ( ${lookup}[ ${key} ] ) {
${_iterations}[${i}] = ${_lookup}[ ${key} ] = ${lookup}[ ${key} ]; ${consequent}
${_lookup}[ ${key} ].update( changed, ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i} );
} else { } else {
${_iterations}[${i}] = ${_lookup}[ ${key} ] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i}, ${block.component}${node.key ? `, ${key}` : `` } ); ${_iterations}[${i}] = ${_lookup}[ ${key} ] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i}, ${block.component}${node.key ? `, ${key}` : `` } );
} }
@ -183,7 +187,7 @@ function unkeyed ( generator, block, state, node, snippet, { create_each_block,
} }
` ); ` );
const { dependencies } = block.contextualise( node.expression ); const dependencies = block.findDependencies( node.expression );
const allDependencies = new Set( block.dependencies ); const allDependencies = new Set( block.dependencies );
dependencies.forEach( dependency => { dependencies.forEach( dependency => {
allDependencies.add( dependency ); allDependencies.add( dependency );
@ -194,17 +198,28 @@ function unkeyed ( generator, block, state, node, snippet, { create_each_block,
.join( ' || ' ); .join( ' || ' );
if ( condition !== '' ) { if ( condition !== '' ) {
const forLoopBody = node._block.hasUpdateMethod ?
deindent`
if ( ${iterations}[${i}] ) {
${iterations}[${i}].update( changed, ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i} );
} else {
${iterations}[${i}] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i}, ${block.component} );
${iterations}[${i}].mount( ${anchor}.parentNode, ${anchor} );
}
` :
deindent`
${iterations}[${i}] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i}, ${block.component} );
${iterations}[${i}].mount( ${anchor}.parentNode, ${anchor} );
`;
const start = node._block.hasUpdateMethod ? '0' : `${iterations}.length`;
block.builders.update.addBlock( deindent` block.builders.update.addBlock( deindent`
var ${each_block_value} = ${snippet}; var ${each_block_value} = ${snippet};
if ( ${condition} ) { if ( ${condition} ) {
for ( var ${i} = 0; ${i} < ${each_block_value}.length; ${i} += 1 ) { for ( var ${i} = ${start}; ${i} < ${each_block_value}.length; ${i} += 1 ) {
if ( !${iterations}[${i}] ) { ${forLoopBody}
${iterations}[${i}] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i}, ${block.component} );
${iterations}[${i}].mount( ${anchor}.parentNode, ${anchor} );
} else {
${iterations}[${i}].update( changed, ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i} );
}
} }
${generator.helper( 'destroyEach' )}( ${iterations}, true, ${each_block_value}.length ); ${generator.helper( 'destroyEach' )}( ${iterations}, true, ${each_block_value}.length );

@ -43,7 +43,7 @@ export default function visitAttribute ( generator, block, state, node, attribut
); );
} }
const last = `last_${state.parentNode}_${name.replace( /[^a-zA-Z_$]/g, '_')}`; const last = block.getUniqueName( `${state.parentNode}_${name.replace( /[^a-zA-Z_$]/g, '_')}_value` );
block.builders.create.addLine( `var ${last} = ${value};` ); block.builders.create.addLine( `var ${last} = ${value};` );
const isSelectValueAttribute = name === 'value' && state.parentNodeName === 'select'; const isSelectValueAttribute = name === 'value' && state.parentNodeName === 'select';
@ -81,8 +81,7 @@ export default function visitAttribute ( generator, block, state, node, attribut
block.builders.create.addLine( updater ); block.builders.create.addLine( updater );
block.builders.update.addBlock( deindent` block.builders.update.addBlock( deindent`
if ( ( ${block.tmp()} = ${value} ) !== ${last} ) { if ( ${last} !== ( ${last} = ${value} ) ) {
${last} = ${block.tmp()};
${updater} ${updater}
} }
` ); ` );

@ -75,7 +75,7 @@ export default function visitEventHandler ( generator, block, state, node, attri
`; `;
if ( shouldHoist ) { if ( shouldHoist ) {
generator.addBlock({ generator.blocks.push({
render: () => handler render: () => handler
}); });
} else { } else {

@ -5,67 +5,65 @@ function isElseIf ( node ) {
return node && node.children.length === 1 && node.children[0].type === 'IfBlock'; return node && node.children.length === 1 && node.children[0].type === 'IfBlock';
} }
function getConditionsAndBlocks ( generator, block, state, node ) { function getBranches ( generator, block, state, node ) {
const conditionsAndBlocks = [{ const branches = [{
condition: block.contextualise( node.expression ).snippet, condition: block.contextualise( node.expression ).snippet,
block: node._block.name block: node._block.name,
dynamic: node._block.dependencies.size > 0
}]; }];
generateBlock( generator, block, state, node ); visitChildren( generator, block, state, node );
if ( isElseIf( node.else ) ) { if ( isElseIf( node.else ) ) {
conditionsAndBlocks.push( branches.push(
...getConditionsAndBlocks( generator, block, state, node.else.children[0] ) ...getBranches( generator, block, state, node.else.children[0] )
); );
} else { } else {
conditionsAndBlocks.push({ branches.push({
condition: null, condition: null,
block: node.else ? node.else._block.name : null, block: node.else ? node.else._block.name : null,
dynamic: node.else ? node.else._block.dependencies.size > 0 : false
}); });
if ( node.else ) { if ( node.else ) {
generateBlock( generator, block, state, node.else ); visitChildren( generator, block, state, node.else );
} }
} }
return conditionsAndBlocks; return branches;
} }
function generateBlock ( generator, block, state, node ) { function visitChildren ( generator, block, state, node ) {
const childBlock = node._block;
const childState = Object.assign( {}, state, { const childState = Object.assign( {}, state, {
parentNode: null parentNode: null
}); });
node.children.forEach( node => { node.children.forEach( child => {
visit( generator, childBlock, childState, node ); visit( generator, node._block, childState, child );
}); });
generator.addBlock( childBlock );
} }
export default function visitIfBlock ( generator, block, state, node ) { export default function visitIfBlock ( generator, block, state, node ) {
const params = block.params.join( ', ' ); const params = block.params.join( ', ' );
const name = generator.getUniqueName( `if_block` ); const name = generator.getUniqueName( `if_block` );
const getBlock = block.getUniqueName( `get_block` ); const getBlock = block.getUniqueName( `get_block` );
const currentBlock = block.getUniqueName( `current_block` ); const current_block = block.getUniqueName( `current_block` );
const _currentBlock = block.getUniqueName( `_current_block` );
const conditionsAndBlocks = getConditionsAndBlocks( generator, block, state, node, generator.getUniqueName( `create_if_block` ) ); const branches = getBranches( generator, block, state, node, generator.getUniqueName( `create_if_block` ) );
const dynamic = branches.some( branch => branch.dynamic );
const anchor = `${name}_anchor`; const anchor = `${name}_anchor`;
block.createAnchor( anchor, state.parentNode ); block.createAnchor( anchor, state.parentNode );
block.builders.create.addBlock( deindent` block.builders.create.addBlock( deindent`
function ${getBlock} ( ${params} ) { function ${getBlock} ( ${params} ) {
${conditionsAndBlocks.map( ({ condition, block }) => { ${branches.map( ({ condition, block }) => {
return `${condition ? `if ( ${condition} ) ` : ''}return ${block};`; return `${condition ? `if ( ${condition} ) ` : ''}return ${block};`;
} ).join( '\n' )} } ).join( '\n' )}
} }
var ${currentBlock} = ${getBlock}( ${params} ); var ${current_block} = ${getBlock}( ${params} );
var ${name} = ${currentBlock} && ${currentBlock}( ${params}, ${block.component} ); var ${name} = ${current_block} && ${current_block}( ${params}, ${block.component} );
` ); ` );
const isToplevel = !state.parentNode; const isToplevel = !state.parentNode;
@ -76,17 +74,25 @@ export default function visitIfBlock ( generator, block, state, node ) {
block.builders.create.addLine( `if ( ${name} ) ${name}.mount( ${state.parentNode}, ${anchor} );` ); block.builders.create.addLine( `if ( ${name} ) ${name}.mount( ${state.parentNode}, ${anchor} );` );
} }
if ( dynamic ) {
block.builders.update.addBlock( deindent` block.builders.update.addBlock( deindent`
var ${_currentBlock} = ${currentBlock}; if ( ${current_block} === ( ${current_block} = ${getBlock}( ${params} ) ) && ${name} ) {
${currentBlock} = ${getBlock}( ${params} );
if ( ${_currentBlock} === ${currentBlock} && ${name}) {
${name}.update( changed, ${params} ); ${name}.update( changed, ${params} );
} else { } else {
if ( ${name} ) ${name}.destroy( true ); if ( ${name} ) ${name}.destroy( true );
${name} = ${currentBlock} && ${currentBlock}( ${params}, ${block.component} ); ${name} = ${current_block} && ${current_block}( ${params}, ${block.component} );
if ( ${name} ) ${name}.mount( ${anchor}.parentNode, ${anchor} ); if ( ${name} ) ${name}.mount( ${anchor}.parentNode, ${anchor} );
} }
` ); ` );
} else {
block.builders.update.addBlock( deindent`
if ( ${current_block} !== ( ${current_block} = ${getBlock}( ${params} ) ) ) {
if ( ${name} ) ${name}.destroy( true );
${name} = ${current_block} && ${current_block}( ${params}, ${block.component} );
if ( ${name} ) ${name}.mount( ${anchor}.parentNode, ${anchor} );
}
` );
}
block.builders.destroy.addLine( block.builders.destroy.addLine(
`if ( ${name} ) ${name}.destroy( ${isToplevel ? 'detach' : 'false'} );` `if ( ${name} ) ${name}.destroy( ${isToplevel ? 'detach' : 'false'} );`

@ -2,15 +2,16 @@ import deindent from '../../../utils/deindent.js';
export default function visitMustacheTag ( generator, block, state, node ) { export default function visitMustacheTag ( generator, block, state, node ) {
const name = block.getUniqueName( 'text' ); const name = block.getUniqueName( 'text' );
const value = block.getUniqueName( `${name}_value` );
const { snippet } = block.contextualise( node.expression ); const { snippet } = block.contextualise( node.expression );
block.builders.create.addLine( `var last_${name} = ${snippet};` ); block.builders.create.addLine( `var ${value} = ${snippet};` );
block.addElement( name, `${generator.helper( 'createText' )}( last_${name} )`, state.parentNode, true ); block.addElement( name, `${generator.helper( 'createText' )}( ${value} )`, state.parentNode, true );
block.builders.update.addBlock( deindent` block.builders.update.addBlock( deindent`
if ( ( ${block.tmp()} = ${snippet} ) !== last_${name} ) { if ( ${value} !== ( ${value} = ${snippet} ) ) {
${name}.data = last_${name} = ${block.tmp()}; ${name}.data = ${value};
} }
` ); ` );
} }

@ -2,21 +2,21 @@ import deindent from '../../../utils/deindent.js';
export default function visitRawMustacheTag ( generator, block, state, node ) { export default function visitRawMustacheTag ( generator, block, state, node ) {
const name = block.getUniqueName( 'raw' ); const name = block.getUniqueName( 'raw' );
const value = block.getUniqueName( `${name}_value` );
const before = block.getUniqueName( `${name}_before` );
const after = block.getUniqueName( `${name}_after` );
const { snippet } = block.contextualise( node.expression ); const { snippet } = block.contextualise( node.expression );
// we would have used comments here, but the `insertAdjacentHTML` api only // we would have used comments here, but the `insertAdjacentHTML` api only
// exists for `Element`s. // exists for `Element`s.
const before = `${name}_before`;
block.addElement( before, `${generator.helper( 'createElement' )}( 'noscript' )`, state.parentNode, true ); block.addElement( before, `${generator.helper( 'createElement' )}( 'noscript' )`, state.parentNode, true );
const after = `${name}_after`;
block.addElement( after, `${generator.helper( 'createElement' )}( 'noscript' )`, state.parentNode, true ); block.addElement( after, `${generator.helper( 'createElement' )}( 'noscript' )`, state.parentNode, true );
const isToplevel = !state.parentNode; const isToplevel = !state.parentNode;
block.builders.create.addLine( `var last_${name} = ${snippet};` ); block.builders.create.addLine( `var ${value} = ${snippet};` );
const mountStatement = `${before}.insertAdjacentHTML( 'afterend', last_${name} );`; const mountStatement = `${before}.insertAdjacentHTML( 'afterend', ${value} );`;
const detachStatement = `${generator.helper( 'detachBetween' )}( ${before}, ${after} );`; const detachStatement = `${generator.helper( 'detachBetween' )}( ${before}, ${after} );`;
if ( isToplevel ) { if ( isToplevel ) {
@ -26,8 +26,7 @@ export default function visitRawMustacheTag ( generator, block, state, node ) {
} }
block.builders.update.addBlock( deindent` block.builders.update.addBlock( deindent`
if ( ( ${block.tmp()} = ${snippet} ) !== last_${name} ) { if ( ${value} !== ( ${value} = ${snippet} ) ) {
last_${name} = ${block.tmp()};
${detachStatement} ${detachStatement}
${mountStatement} ${mountStatement}
} }

@ -22,8 +22,6 @@ function create_main_fragment ( root, component ) {
return { return {
mount: noop, mount: noop,
update: noop,
destroy: noop destroy: noop
}; };
} }
@ -55,9 +53,7 @@ SvelteComponent.prototype._set = function _set ( newState ) {
var oldState = this._state; var oldState = this._state;
this._state = assign( {}, oldState, newState ); this._state = assign( {}, oldState, newState );
recompute( this._state, newState, oldState, false ) recompute( this._state, newState, oldState, false )
dispatchObservers( this, this._observers.pre, newState, oldState ); dispatchObservers( this, this._observers.pre, newState, oldState );
if ( this._fragment ) this._fragment.update( newState, this._state );
dispatchObservers( this, this._observers.post, newState, oldState ); dispatchObservers( this, this._observers.post, newState, oldState );
}; };

@ -23,11 +23,11 @@ function create_main_fragment ( root, component ) {
if ( 'comments' in changed || 'elapsed' in changed || 'time' in changed ) { if ( 'comments' in changed || 'elapsed' in changed || 'time' in changed ) {
for ( var i = 0; i < each_block_value.length; i += 1 ) { for ( var i = 0; i < each_block_value.length; i += 1 ) {
if ( !each_block_iterations[i] ) { if ( each_block_iterations[i] ) {
each_block_iterations[i].update( changed, root, each_block_value, each_block_value[i], i );
} else {
each_block_iterations[i] = create_each_block( root, each_block_value, each_block_value[i], i, component ); each_block_iterations[i] = create_each_block( root, each_block_value, each_block_value[i], i, component );
each_block_iterations[i].mount( each_block_anchor.parentNode, each_block_anchor ); each_block_iterations[i].mount( each_block_anchor.parentNode, each_block_anchor );
} else {
each_block_iterations[i].update( changed, root, each_block_value, each_block_value[i], i );
} }
} }
@ -53,12 +53,12 @@ function create_each_block ( root, each_block_value, comment, comment_index, com
var span = createElement( 'span' ); var span = createElement( 'span' );
appendNode( span, div ); appendNode( span, div );
span.className = "meta"; span.className = "meta";
var last_text = comment.author; var text_value = comment.author;
var text = createText( last_text ); var text = createText( text_value );
appendNode( text, span ); appendNode( text, span );
appendNode( createText( " wrote " ), span ); appendNode( createText( " wrote " ), span );
var last_text_2 = root.elapsed(comment.time, root.time); var text_2_value = root.elapsed(comment.time, root.time);
var text_2 = createText( last_text_2 ); var text_2 = createText( text_2_value );
appendNode( text_2, span ); appendNode( text_2, span );
appendNode( createText( " ago:" ), span ); appendNode( createText( " ago:" ), span );
appendNode( createText( "\n\n\t\t" ), div ); appendNode( createText( "\n\n\t\t" ), div );
@ -66,8 +66,8 @@ function create_each_block ( root, each_block_value, comment, comment_index, com
appendNode( raw_before, div ); appendNode( raw_before, div );
var raw_after = createElement( 'noscript' ); var raw_after = createElement( 'noscript' );
appendNode( raw_after, div ); appendNode( raw_after, div );
var last_raw = comment.html; var raw_value = comment.html;
raw_before.insertAdjacentHTML( 'afterend', last_raw ); raw_before.insertAdjacentHTML( 'afterend', raw_value );
return { return {
mount: function ( target, anchor ) { mount: function ( target, anchor ) {
@ -75,20 +75,17 @@ function create_each_block ( root, each_block_value, comment, comment_index, com
}, },
update: function ( changed, root, each_block_value, comment, comment_index ) { update: function ( changed, root, each_block_value, comment, comment_index ) {
var tmp; if ( text_value !== ( text_value = comment.author ) ) {
text.data = text_value;
if ( ( tmp = comment.author ) !== last_text ) {
text.data = last_text = tmp;
} }
if ( ( tmp = root.elapsed(comment.time, root.time) ) !== last_text_2 ) { if ( text_2_value !== ( text_2_value = root.elapsed(comment.time, root.time) ) ) {
text_2.data = last_text_2 = tmp; text_2.data = text_2_value;
} }
if ( ( tmp = comment.html ) !== last_raw ) { if ( raw_value !== ( raw_value = comment.html ) ) {
last_raw = tmp;
detachBetween( raw_before, raw_after ); detachBetween( raw_before, raw_after );
raw_before.insertAdjacentHTML( 'afterend', last_raw ); raw_before.insertAdjacentHTML( 'afterend', raw_value );
} }
}, },
@ -127,7 +124,6 @@ assign( SvelteComponent.prototype, proto );
SvelteComponent.prototype._set = function _set ( newState ) { SvelteComponent.prototype._set = function _set ( newState ) {
var oldState = this._state; var oldState = this._state;
this._state = assign( {}, oldState, newState ); this._state = assign( {}, oldState, newState );
dispatchObservers( this, this._observers.pre, newState, oldState ); dispatchObservers( this, this._observers.pre, newState, oldState );
if ( this._fragment ) this._fragment.update( newState, this._state ); if ( this._fragment ) this._fragment.update( newState, this._state );
dispatchObservers( this, this._observers.post, newState, oldState ); dispatchObservers( this, this._observers.post, newState, oldState );

@ -1,4 +1,4 @@
import { appendNode, assign, createElement, createText, detachNode, dispatchObservers, insertNode, noop, proto } from "svelte/shared.js"; import { appendNode, assign, createElement, createText, detachNode, dispatchObservers, insertNode, proto } from "svelte/shared.js";
var template = (function () { var template = (function () {
return { return {
@ -30,8 +30,6 @@ function create_main_fragment ( root, component ) {
insertNode( button, target, anchor ); insertNode( button, target, anchor );
}, },
update: noop,
destroy: function ( detach ) { destroy: function ( detach ) {
foo_handler.teardown(); foo_handler.teardown();
@ -67,9 +65,7 @@ assign( SvelteComponent.prototype, template.methods, proto );
SvelteComponent.prototype._set = function _set ( newState ) { SvelteComponent.prototype._set = function _set ( newState ) {
var oldState = this._state; var oldState = this._state;
this._state = assign( {}, oldState, newState ); this._state = assign( {}, oldState, newState );
dispatchObservers( this, this._observers.pre, newState, oldState ); dispatchObservers( this, this._observers.pre, newState, oldState );
if ( this._fragment ) this._fragment.update( newState, this._state );
dispatchObservers( this, this._observers.post, newState, oldState ); dispatchObservers( this, this._observers.post, newState, oldState );
}; };

@ -0,0 +1,112 @@
import { appendNode, assign, createComment, createElement, createText, detachNode, dispatchObservers, insertNode, proto } from "svelte/shared.js";
function create_main_fragment ( root, component ) {
var if_block_anchor = createComment();
function get_block ( root ) {
if ( root.foo ) return create_if_block;
return create_if_block_1;
}
var current_block = get_block( root );
var if_block = current_block && current_block( root, component );
return {
mount: function ( target, anchor ) {
insertNode( if_block_anchor, target, anchor );
if ( if_block ) if_block.mount( target, if_block_anchor );
},
update: function ( changed, root ) {
if ( current_block !== ( current_block = get_block( root ) ) ) {
if ( if_block ) if_block.destroy( true );
if_block = current_block && current_block( root, component );
if ( if_block ) if_block.mount( if_block_anchor.parentNode, if_block_anchor );
}
},
destroy: function ( detach ) {
if ( if_block ) if_block.destroy( detach );
if ( detach ) {
detachNode( if_block_anchor );
}
}
};
}
function create_if_block ( root, component ) {
var p = createElement( 'p' );
appendNode( createText( "foo!" ), p );
return {
mount: function ( target, anchor ) {
insertNode( p, target, anchor );
},
destroy: function ( detach ) {
if ( detach ) {
detachNode( p );
}
}
};
}
function create_if_block_1 ( root, component ) {
var p = createElement( 'p' );
appendNode( createText( "not foo!" ), p );
return {
mount: function ( target, anchor ) {
insertNode( p, target, anchor );
},
destroy: function ( detach ) {
if ( detach ) {
detachNode( p );
}
}
};
}
function SvelteComponent ( options ) {
options = options || {};
this._state = options.data || {};
this._observers = {
pre: Object.create( null ),
post: Object.create( null )
};
this._handlers = Object.create( null );
this._root = options._root;
this._yield = options._yield;
this._torndown = false;
this._fragment = create_main_fragment( this._state, this );
if ( options.target ) this._fragment.mount( options.target, null );
}
assign( SvelteComponent.prototype, proto );
SvelteComponent.prototype._set = function _set ( newState ) {
var oldState = this._state;
this._state = assign( {}, oldState, newState );
dispatchObservers( this, this._observers.pre, newState, oldState );
if ( this._fragment ) this._fragment.update( newState, this._state );
dispatchObservers( this, this._observers.post, newState, oldState );
};
SvelteComponent.prototype.teardown = SvelteComponent.prototype.destroy = function destroy ( detach ) {
this.fire( 'destroy' );
this._fragment.destroy( detach !== false );
this._fragment = null;
this._state = {};
this._torndown = true;
};
export default SvelteComponent;

@ -0,0 +1,5 @@
{{#if foo}}
<p>foo!</p>
{{else}}
<p>not foo!</p>
{{/if}}

@ -1,9 +1,15 @@
export default { export default {
html: '<div><p>Hello</p></div>', html: `
<div><p>Hello</p></div>
`,
test ( assert, component, target ) { test ( assert, component, target ) {
assert.equal( component.get( 'data' ), 'Hello' ); assert.equal( component.get( 'data' ), 'Hello' );
component.set({ data: 'World' }); component.set({ data: 'World' });
assert.equal( component.get( 'data' ), 'World' ); assert.equal( component.get( 'data' ), 'World' );
assert.equal( target.innerHTML, '<div><p>World<!----></p></div>' ); assert.htmlEqual( target.innerHTML, `
<div><p>World</p></div>
` );
} }
}; };

Loading…
Cancel
Save