another half-done commit

pull/453/head
Rich Harris 9 years ago
parent 063e76f322
commit d0752cd6b1

@ -3,7 +3,37 @@ export default class Fragment {
Object.assign( this, options );
}
addElement ( name, renderStatement, needsIdentifier = false ) {
const isToplevel = this.localElementDepth === 0;
if ( needsIdentifier || isToplevel ) {
this.builders.create.addLine(
`var ${name} = ${renderStatement};`
);
this.createMountStatement( name );
} else {
this.builders.create.addLine( `${this.generator.helper( 'appendNode' )}( ${renderStatement}, ${this.target} );` );
}
if ( isToplevel ) {
this.builders.detach.addLine( `${this.generator.helper( 'detachNode' )}( ${name} );` );
}
}
child ( options ) {
return new Fragment( Object.assign( {}, this, options, { parent: this } ) );
}
createAnchor ( name ) {
const renderStatement = `${this.generator.helper( 'createComment' )}()`;
this.addElement( name, renderStatement, true );
}
createMountStatement ( name ) {
if ( this.target === 'target' ) {
this.builders.mount.addLine( `${this.generator.helper( 'insertNode' )}( ${name}, target, anchor );` );
} else {
this.builders.create.addLine( `${this.generator.helper( 'appendNode' )}( ${name}, ${this.target} );` );
}
}
}

@ -18,23 +18,6 @@ class DomGenerator extends Generator {
};
}
addElement ( name, renderStatement, needsIdentifier = false ) {
const isToplevel = this.current.localElementDepth === 0;
if ( needsIdentifier || isToplevel ) {
this.current.builders.create.addLine(
`var ${name} = ${renderStatement};`
);
this.createMountStatement( name );
} else {
this.current.builders.create.addLine( `${this.helper( 'appendNode' )}( ${renderStatement}, ${this.current.target} );` );
}
if ( isToplevel ) {
this.current.builders.detach.addLine( `${this.helper( 'detachNode' )}( ${name} );` );
}
}
addRenderer ( fragment ) {
if ( fragment.autofocus ) {
fragment.builders.create.addLine( `${fragment.autofocus}.focus();` );
@ -103,19 +86,6 @@ class DomGenerator extends Generator {
` );
}
createAnchor ( name ) {
const renderStatement = `${this.helper( 'createComment' )}()`;
this.addElement( name, renderStatement, true );
}
createMountStatement ( name ) {
if ( this.current.target === 'target' ) {
this.current.builders.mount.addLine( `${this.helper( 'insertNode' )}( ${name}, target, anchor );` );
} else {
this.current.builders.create.addLine( `${this.helper( 'appendNode' )}( ${name}, ${this.current.target} );` );
}
}
generateBlock ( node, name, type ) {
const childFragment = this.current.child({
type,
@ -183,7 +153,7 @@ export default function dom ( parsed, source, options ) {
generator.push( mainFragment );
parsed.html.children.forEach( node => {
visit( node, generator );
visit( generator, mainFragment, node );
});
generator.addRenderer( mainFragment );

@ -1,6 +1,6 @@
import visitors from './visitors/index.js';
export default function visit ( node, generator ) {
export default function visit ( generator, fragment, node ) {
const visitor = visitors[ node.type ];
visitor( generator, node );
visitor( generator, fragment, node );
}

@ -19,14 +19,13 @@ function stringifyProps ( props ) {
return `{ ${joined} }`;
}
export default function visitComponent ( generator, node ) {
export default function visitComponent ( generator, fragment, node ) {
const hasChildren = node.children.length > 0;
const { current } = generator;
const name = current.getUniqueName( capDown( node.name === ':Self' ? generator.name : node.name ) );
const name = fragment.getUniqueName( capDown( node.name === ':Self' ? generator.name : node.name ) );
const local = {
name,
namespace: current.namespace,
namespace: fragment.namespace,
isComponent: true,
allUsedContexts: [],
@ -35,18 +34,18 @@ export default function visitComponent ( generator, node ) {
update: new CodeBuilder()
};
const isToplevel = current.localElementDepth === 0;
const isToplevel = fragment.localElementDepth === 0;
generator.hasComponents = true;
addComponentAttributes( generator, node, local );
addComponentAttributes( generator, fragment, node, local );
if ( local.allUsedContexts.length ) {
const initialProps = local.allUsedContexts.map( contextName => {
if ( contextName === 'root' ) return `root: root`;
const listName = current.listNames.get( contextName );
const indexName = current.indexNames.get( contextName );
const listName = fragment.listNames.get( contextName );
const indexName = fragment.indexNames.get( contextName );
return `${listName}: ${listName},\n${indexName}: ${indexName}`;
}).join( ',\n' );
@ -54,8 +53,8 @@ export default function visitComponent ( generator, node ) {
const updates = local.allUsedContexts.map( contextName => {
if ( contextName === 'root' ) return `${name}._context.root = root;`;
const listName = current.listNames.get( contextName );
const indexName = current.indexNames.get( contextName );
const listName = fragment.listNames.get( contextName );
const indexName = fragment.indexNames.get( contextName );
return `${name}._context.${listName} = ${listName};\n${name}._context.${indexName} = ${indexName};`;
}).join( '\n' );
@ -70,24 +69,24 @@ export default function visitComponent ( generator, node ) {
}
const componentInitProperties = [
`target: ${!isToplevel ? current.target: 'null'}`,
`_root: ${current.component}._root || ${current.component}`
`target: ${!isToplevel ? fragment.target: 'null'}`,
`_root: ${fragment.component}._root || ${fragment.component}`
];
// Component has children, put them in a separate {{yield}} block
if ( hasChildren ) {
const yieldName = generator.getUniqueName( `render_${name}_yield_fragment` );
const params = current.params.join( ', ' );
const params = fragment.params.join( ', ' );
generator.generateBlock( node, yieldName, 'block' );
const yieldFragment = current.getUniqueName( `${name}_yield_fragment` );
const yieldFragment = fragment.getUniqueName( `${name}_yield_fragment` );
current.builders.create.addLine(
`var ${yieldFragment} = ${yieldName}( ${params}, ${current.component} );`
fragment.builders.create.addLine(
`var ${yieldFragment} = ${yieldName}( ${params}, ${fragment.component} );`
);
current.builders.update.addLine(
fragment.builders.update.addLine(
`${yieldFragment}.update( changed, ${params} );`
);
@ -104,7 +103,7 @@ export default function visitComponent ( generator, node ) {
const initialPropString = stringifyProps( initialProps );
if ( local.bindings.length ) {
const initialData = current.getUniqueName( `${name}_initial_data` );
const initialData = fragment.getUniqueName( `${name}_initial_data` );
statements.push( `var ${name}_initial_data = ${initialPropString};` );
@ -128,7 +127,7 @@ export default function visitComponent ( generator, node ) {
` );
if ( isToplevel ) {
current.builders.mount.addLine( `${name}._fragment.mount( target, anchor );` );
fragment.builders.mount.addLine( `${name}._fragment.mount( target, anchor );` );
}
if ( local.dynamicAttributes.length ) {
@ -153,17 +152,17 @@ export default function visitComponent ( generator, node ) {
` );
}
current.builders.destroy.addLine( `${name}.destroy( ${isToplevel ? 'detach' : 'false'} );` );
fragment.builders.destroy.addLine( `${name}.destroy( ${isToplevel ? 'detach' : 'false'} );` );
current.builders.create.addBlock( local.create );
if ( !local.update.isEmpty() ) current.builders.update.addBlock( local.update );
fragment.builders.create.addBlock( local.create );
if ( !local.update.isEmpty() ) fragment.builders.update.addBlock( local.update );
const childFragment = generator.current.child({
const childFragment = fragment.child({
type: 'component',
namespace: local.namespace,
target: name,
parent: current,
localElementDepth: current.localElementDepth + 1,
parent: fragment,
localElementDepth: fragment.localElementDepth + 1,
key: null
});
generator.push( childFragment );
@ -171,7 +170,7 @@ export default function visitComponent ( generator, node ) {
generator.elementDepth += 1;
node.children.forEach( child => {
visit( child, generator );
visit( generator, childFragment, child );
});
generator.elementDepth -= 1;

@ -3,52 +3,52 @@ import deindent from '../../../utils/deindent.js';
import getBuilders from '../utils/getBuilders.js';
import visit from '../visit.js';
export default function visitEachBlock ( generator, node ) {
export default function visitEachBlock ( generator, fragment, node ) {
const name = generator.getUniqueName( `each_block` );
const renderer = generator.getUniqueName( `render_each_block` );
const elseName = generator.getUniqueName( `${name}_else` );
const renderElse = generator.getUniqueName( `${renderer}_else` );
const i = generator.current.getUniqueName( `i` );
const params = generator.current.params.join( ', ' );
const i = fragment.getUniqueName( `i` );
const params = fragment.params.join( ', ' );
const listName = generator.current.getUniqueName( `${name}_value` );
const listName = fragment.getUniqueName( `${name}_value` );
const isToplevel = generator.current.localElementDepth === 0;
const isToplevel = fragment.localElementDepth === 0;
generator.addSourcemapLocations( node.expression );
const { dependencies, snippet } = generator.contextualise( node.expression );
const anchor = generator.current.getUniqueName( `${name}_anchor` );
generator.createAnchor( anchor );
const anchor = fragment.getUniqueName( `${name}_anchor` );
fragment.createAnchor( anchor );
const localVars = {};
localVars.iteration = generator.current.getUniqueName( `${name}_iteration` );
localVars.iterations = generator.current.getUniqueName( `${name}_iterations` );
localVars._iterations = generator.current.getUniqueName( `_${name}_iterations` );
localVars.lookup = generator.current.getUniqueName( `${name}_lookup` );
localVars._lookup = generator.current.getUniqueName( `_${name}_lookup` );
localVars.iteration = fragment.getUniqueName( `${name}_iteration` );
localVars.iterations = fragment.getUniqueName( `${name}_iterations` );
localVars._iterations = fragment.getUniqueName( `_${name}_iterations` );
localVars.lookup = fragment.getUniqueName( `${name}_lookup` );
localVars._lookup = fragment.getUniqueName( `_${name}_lookup` );
generator.current.builders.create.addLine( `var ${listName} = ${snippet};` );
generator.current.builders.create.addLine( `var ${localVars.iterations} = [];` );
if ( node.key ) generator.current.builders.create.addLine( `var ${localVars.lookup} = Object.create( null );` );
if ( node.else ) generator.current.builders.create.addLine( `var ${elseName} = null;` );
fragment.builders.create.addLine( `var ${listName} = ${snippet};` );
fragment.builders.create.addLine( `var ${localVars.iterations} = [];` );
if ( node.key ) fragment.builders.create.addLine( `var ${localVars.lookup} = Object.create( null );` );
if ( node.else ) fragment.builders.create.addLine( `var ${elseName} = null;` );
const initialRender = new CodeBuilder();
if ( node.key ) {
localVars.fragment = generator.current.getUniqueName( 'fragment' );
localVars.value = generator.current.getUniqueName( 'value' );
localVars.key = generator.current.getUniqueName( 'key' );
localVars.fragment = fragment.getUniqueName( 'fragment' );
localVars.value = fragment.getUniqueName( 'value' );
localVars.key = fragment.getUniqueName( 'key' );
initialRender.addBlock( deindent`
var ${localVars.key} = ${listName}[${i}].${node.key};
${localVars.iterations}[${i}] = ${localVars.lookup}[ ${localVars.key} ] = ${renderer}( ${params}, ${listName}, ${listName}[${i}], ${i}, ${generator.current.component}${node.key ? `, ${localVars.key}` : `` } );
${localVars.iterations}[${i}] = ${localVars.lookup}[ ${localVars.key} ] = ${renderer}( ${params}, ${listName}, ${listName}[${i}], ${i}, ${fragment.component}${node.key ? `, ${localVars.key}` : `` } );
` );
} else {
initialRender.addLine(
`${localVars.iterations}[${i}] = ${renderer}( ${params}, ${listName}, ${listName}[${i}], ${i}, ${generator.current.component} );`
`${localVars.iterations}[${i}] = ${renderer}( ${params}, ${listName}, ${listName}[${i}], ${i}, ${fragment.component} );`
);
}
@ -58,29 +58,29 @@ export default function visitEachBlock ( generator, node ) {
);
}
generator.current.builders.create.addBlock( deindent`
fragment.builders.create.addBlock( deindent`
for ( var ${i} = 0; ${i} < ${listName}.length; ${i} += 1 ) {
${initialRender}
}
` );
if ( node.else ) {
generator.current.builders.create.addBlock( deindent`
fragment.builders.create.addBlock( deindent`
if ( !${listName}.length ) {
${elseName} = ${renderElse}( ${params}, ${generator.current.component} );
${elseName} = ${renderElse}( ${params}, ${fragment.component} );
${!isToplevel ? `${elseName}.mount( ${anchor}.parentNode, ${anchor} );` : ''}
}
` );
}
if ( isToplevel ) {
generator.current.builders.mount.addBlock( deindent`
fragment.builders.mount.addBlock( deindent`
for ( var ${i} = 0; ${i} < ${localVars.iterations}.length; ${i} += 1 ) {
${localVars.iterations}[${i}].mount( ${anchor}.parentNode, ${anchor} );
}
` );
if ( node.else ) {
generator.current.builders.mount.addBlock( deindent`
fragment.builders.mount.addBlock( deindent`
if ( ${elseName} ) {
${elseName}.mount( ${anchor}.parentNode, ${anchor} );
}
@ -89,7 +89,7 @@ export default function visitEachBlock ( generator, node ) {
}
if ( node.key ) {
generator.current.builders.update.addBlock( deindent`
fragment.builders.update.addBlock( deindent`
var ${listName} = ${snippet};
var ${localVars._iterations} = [];
var ${localVars._lookup} = Object.create( null );
@ -105,7 +105,7 @@ export default function visitEachBlock ( generator, node ) {
${localVars._iterations}[${i}] = ${localVars._lookup}[ ${localVars.key} ] = ${localVars.lookup}[ ${localVars.key} ];
${localVars._lookup}[ ${localVars.key} ].update( changed, ${params}, ${listName}, ${listName}[${i}], ${i} );
} else {
${localVars._iterations}[${i}] = ${localVars._lookup}[ ${localVars.key} ] = ${renderer}( ${params}, ${listName}, ${listName}[${i}], ${i}, ${generator.current.component}${node.key ? `, ${localVars.key}` : `` } );
${localVars._iterations}[${i}] = ${localVars._lookup}[ ${localVars.key} ] = ${renderer}( ${params}, ${listName}, ${listName}[${i}], ${i}, ${fragment.component}${node.key ? `, ${localVars.key}` : `` } );
}
${localVars._iterations}[${i}].mount( ${localVars.fragment}, null );
@ -125,12 +125,12 @@ export default function visitEachBlock ( generator, node ) {
${localVars.lookup} = ${localVars._lookup};
` );
} else {
generator.current.builders.update.addBlock( deindent`
fragment.builders.update.addBlock( deindent`
var ${listName} = ${snippet};
for ( var ${i} = 0; ${i} < ${listName}.length; ${i} += 1 ) {
if ( !${localVars.iterations}[${i}] ) {
${localVars.iterations}[${i}] = ${renderer}( ${params}, ${listName}, ${listName}[${i}], ${i}, ${generator.current.component} );
${localVars.iterations}[${i}] = ${renderer}( ${params}, ${listName}, ${listName}[${i}], ${i}, ${fragment.component} );
${localVars.iterations}[${i}].mount( ${anchor}.parentNode, ${anchor} );
} else {
${localVars.iterations}[${i}].update( changed, ${params}, ${listName}, ${listName}[${i}], ${i} );
@ -144,11 +144,11 @@ export default function visitEachBlock ( generator, node ) {
}
if ( node.else ) {
generator.current.builders.update.addBlock( deindent`
fragment.builders.update.addBlock( deindent`
if ( !${listName}.length && ${elseName} ) {
${elseName}.update( changed, ${params} );
} else if ( !${listName}.length ) {
${elseName} = ${renderElse}( ${params}, ${generator.current.component} );
${elseName} = ${renderElse}( ${params}, ${fragment.component} );
${elseName}.mount( ${anchor}.parentNode, ${anchor} );
} else if ( ${elseName} ) {
${elseName}.destroy( true );
@ -156,11 +156,11 @@ export default function visitEachBlock ( generator, node ) {
` );
}
generator.current.builders.destroy.addBlock(
fragment.builders.destroy.addBlock(
`${generator.helper( 'destroyEach' )}( ${localVars.iterations}, ${isToplevel ? 'detach' : 'false'} );` );
if ( node.else ) {
generator.current.builders.destroy.addBlock( deindent`
fragment.builders.destroy.addBlock( deindent`
if ( ${elseName} ) {
${elseName}.destroy( ${isToplevel ? 'detach' : 'false'} );
}
@ -171,28 +171,28 @@ export default function visitEachBlock ( generator, node ) {
generator.generateBlock( node.else, renderElse, 'block' );
}
const indexNames = new Map( generator.current.indexNames );
const indexName = node.index || generator.current.getUniqueName( `${node.context}_index` );
const indexNames = new Map( fragment.indexNames );
const indexName = node.index || fragment.getUniqueName( `${node.context}_index` );
indexNames.set( node.context, indexName );
const listNames = new Map( generator.current.listNames );
const listNames = new Map( fragment.listNames );
listNames.set( node.context, listName );
const context = generator.getUniqueName( node.context );
const contexts = new Map( generator.current.contexts );
const contexts = new Map( fragment.contexts );
contexts.set( node.context, context );
const indexes = new Map( generator.current.indexes );
const indexes = new Map( fragment.indexes );
if ( node.index ) indexes.set( indexName, node.context );
const contextDependencies = new Map( generator.current.contextDependencies );
const contextDependencies = new Map( fragment.contextDependencies );
contextDependencies.set( node.context, dependencies );
const blockParams = generator.current.params.concat( listName, context, indexName );
const blockParams = fragment.params.concat( listName, context, indexName );
const getUniqueName = generator.getUniqueNameMaker( blockParams );
const childFragment = generator.current.child({
const childFragment = fragment.child({
type: 'block',
name: renderer,
target: 'target',
@ -218,9 +218,9 @@ export default function visitEachBlock ( generator, node ) {
generator.push( childFragment );
node.children.forEach( child => {
visit( child, generator );
visit( generator, childFragment, child );
});
generator.addRenderer( generator.current );
generator.addRenderer( childFragment );
generator.pop();
}

@ -9,20 +9,20 @@ const meta = {
':Window': visitWindow
};
export default function visitElement ( generator, node ) {
export default function visitElement ( generator, fragment, node ) {
if ( node.name in meta ) {
return meta[ node.name ]( generator, node );
return meta[ node.name ]( generator, fragment, node );
}
if ( generator.components.has( node.name ) || node.name === ':Self' ) {
return visitComponent( generator, node );
return visitComponent( generator, fragment, node );
}
const name = generator.current.getUniqueName( node.name );
const name = fragment.getUniqueName( node.name );
const local = {
name,
namespace: node.name === 'svg' ? 'http://www.w3.org/2000/svg' : generator.current.namespace,
namespace: node.name === 'svg' ? 'http://www.w3.org/2000/svg' : fragment.namespace,
isComponent: false,
allUsedContexts: [],
@ -32,16 +32,16 @@ export default function visitElement ( generator, node ) {
destroy: new CodeBuilder()
};
const isToplevel = generator.current.localElementDepth === 0;
const isToplevel = fragment.localElementDepth === 0;
addElementAttributes( generator, node, local );
addElementAttributes( generator, fragment, node, local );
if ( local.allUsedContexts.length ) {
const initialProps = local.allUsedContexts.map( contextName => {
if ( contextName === 'root' ) return `root: root`;
const listName = generator.current.listNames.get( contextName );
const indexName = generator.current.indexNames.get( contextName );
const listName = fragment.listNames.get( contextName );
const indexName = fragment.indexNames.get( contextName );
return `${listName}: ${listName},\n${indexName}: ${indexName}`;
}).join( ',\n' );
@ -49,8 +49,8 @@ export default function visitElement ( generator, node ) {
const updates = local.allUsedContexts.map( contextName => {
if ( contextName === 'root' ) return `${name}.__svelte.root = root;`;
const listName = generator.current.listNames.get( contextName );
const indexName = generator.current.indexNames.get( contextName );
const listName = fragment.listNames.get( contextName );
const indexName = fragment.indexNames.get( contextName );
return `${name}.__svelte.${listName} = ${listName};\n${name}.__svelte.${indexName} = ${indexName};`;
}).join( '\n' );
@ -82,7 +82,7 @@ export default function visitElement ( generator, node ) {
local.create.addLineAtStart( render );
if ( isToplevel ) {
generator.current.builders.detach.addLine( `${generator.helper( 'detachNode' )}( ${name} );` );
fragment.builders.detach.addLine( `${generator.helper( 'detachNode' )}( ${name} );` );
}
// special case bound <option> without a value attribute
@ -92,18 +92,18 @@ export default function visitElement ( generator, node ) {
node.initialUpdate = statement;
}
generator.current.builders.create.addBlock( local.create );
if ( !local.update.isEmpty() ) generator.current.builders.update.addBlock( local.update );
if ( !local.destroy.isEmpty() ) generator.current.builders.destroy.addBlock( local.destroy );
fragment.builders.create.addBlock( local.create );
if ( !local.update.isEmpty() ) fragment.builders.update.addBlock( local.update );
if ( !local.destroy.isEmpty() ) fragment.builders.destroy.addBlock( local.destroy );
generator.createMountStatement( name );
fragment.createMountStatement( name );
const childFragment = generator.current.child({
const childFragment = fragment.child({
type: 'element',
namespace: local.namespace,
target: name,
parent: generator.current,
localElementDepth: generator.current.localElementDepth + 1,
parent: fragment,
localElementDepth: fragment.localElementDepth + 1,
key: null
});
@ -112,13 +112,13 @@ export default function visitElement ( generator, node ) {
generator.elementDepth += 1;
node.children.forEach( child => {
visit( child, generator );
visit( generator, childFragment, child );
});
generator.elementDepth -= 1;
if ( node.initialUpdate ) {
generator.current.builders.create.addBlock( node.initialUpdate );
fragment.builders.create.addBlock( node.initialUpdate );
}
generator.pop();

@ -30,20 +30,20 @@ function getConditionsAndBlocks ( generator, node, _name, i = 0 ) {
return conditionsAndBlocks;
}
export default function visitIfBlock ( generator, node ) {
const params = generator.current.params.join( ', ' );
export default function visitIfBlock ( generator, fragment, node ) {
const params = fragment.params.join( ', ' );
const name = generator.getUniqueName( `if_block` );
const getBlock = generator.current.getUniqueName( `get_block` );
const currentBlock = generator.current.getUniqueName( `current_block` );
const _currentBlock = generator.current.getUniqueName( `_current_block` );
const getBlock = fragment.getUniqueName( `get_block` );
const currentBlock = fragment.getUniqueName( `current_block` );
const _currentBlock = fragment.getUniqueName( `_current_block` );
const isToplevel = generator.current.localElementDepth === 0;
const isToplevel = fragment.localElementDepth === 0;
const conditionsAndBlocks = getConditionsAndBlocks( generator, node, generator.getUniqueName( `render_if_block` ) );
const anchor = `${name}_anchor`;
generator.createAnchor( anchor );
fragment.createAnchor( anchor );
generator.current.builders.create.addBlock( deindent`
fragment.builders.create.addBlock( deindent`
function ${getBlock} ( ${params} ) {
${conditionsAndBlocks.map( ({ condition, block }) => {
return `${condition ? `if ( ${condition} ) ` : ''}return ${block};`;
@ -51,29 +51,29 @@ export default function visitIfBlock ( generator, node ) {
}
var ${currentBlock} = ${getBlock}( ${params} );
var ${name} = ${currentBlock} && ${currentBlock}( ${params}, ${generator.current.component} );
var ${name} = ${currentBlock} && ${currentBlock}( ${params}, ${fragment.component} );
` );
const mountStatement = `if ( ${name} ) ${name}.mount( ${anchor}.parentNode, ${anchor} );`;
if ( isToplevel ) {
generator.current.builders.mount.addLine( mountStatement );
fragment.builders.mount.addLine( mountStatement );
} else {
generator.current.builders.create.addLine( mountStatement );
fragment.builders.create.addLine( mountStatement );
}
generator.current.builders.update.addBlock( deindent`
fragment.builders.update.addBlock( deindent`
var ${_currentBlock} = ${currentBlock};
${currentBlock} = ${getBlock}( ${params} );
if ( ${_currentBlock} === ${currentBlock} && ${name}) {
${name}.update( changed, ${params} );
} else {
if ( ${name} ) ${name}.destroy( true );
${name} = ${currentBlock} && ${currentBlock}( ${params}, ${generator.current.component} );
${name} = ${currentBlock} && ${currentBlock}( ${params}, ${fragment.component} );
if ( ${name} ) ${name}.mount( ${anchor}.parentNode, ${anchor} );
}
` );
generator.current.builders.destroy.addLine(
fragment.builders.destroy.addLine(
`if ( ${name} ) ${name}.destroy( ${isToplevel ? 'detach' : 'false'} );`
);
}

@ -1,20 +1,21 @@
import deindent from '../../../utils/deindent.js';
import findBlock from '../utils/findBlock.js';
export default function visitMustacheTag ( generator, node ) {
const name = generator.current.getUniqueName( 'text' );
export default function visitMustacheTag ( generator, fragment, node ) {
const name = fragment.getUniqueName( 'text' );
const { snippet } = generator.contextualise( node.expression );
generator.current.builders.create.addLine( `var last_${name} = ${snippet};` );
generator.addElement( name, `${generator.helper( 'createText' )}( last_${name} )`, true );
fragment.builders.create.addLine( `var last_${name} = ${snippet};` );
fragment.addElement( name, `${generator.helper( 'createText' )}( last_${name} )`, true );
const fragment = findBlock( generator.current );
if ( !fragment.tmp ) fragment.tmp = fragment.getUniqueName( 'tmp' );
// TODO this should be unnecessary once we separate fragments from state
const parentFragment = findBlock( fragment );
if ( !parentFragment.tmp ) parentFragment.tmp = parentFragment.getUniqueName( 'tmp' );
generator.current.builders.update.addBlock( deindent`
if ( ( ${fragment.tmp} = ${snippet} ) !== last_${name} ) {
${name}.data = last_${name} = ${fragment.tmp};
fragment.builders.update.addBlock( deindent`
if ( ( ${parentFragment.tmp} = ${snippet} ) !== last_${name} ) {
${name}.data = last_${name} = ${parentFragment.tmp};
}
` );
}

@ -1,41 +1,42 @@
import deindent from '../../../utils/deindent.js';
import findBlock from '../utils/findBlock.js';
export default function visitRawMustacheTag ( generator, node ) {
const name = generator.current.getUniqueName( 'raw' );
export default function visitRawMustacheTag ( generator, fragment, node ) {
const name = fragment.getUniqueName( 'raw' );
const { snippet } = generator.contextualise( node.expression );
// we would have used comments here, but the `insertAdjacentHTML` api only
// exists for `Element`s.
const before = `${name}_before`;
generator.addElement( before, `${generator.helper( 'createElement' )}( 'noscript' )`, true );
fragment.addElement( before, `${generator.helper( 'createElement' )}( 'noscript' )`, true );
const after = `${name}_after`;
generator.addElement( after, `${generator.helper( 'createElement' )}( 'noscript' )`, true );
fragment.addElement( after, `${generator.helper( 'createElement' )}( 'noscript' )`, true );
const isToplevel = generator.current.localElementDepth === 0;
const isToplevel = fragment.localElementDepth === 0;
generator.current.builders.create.addLine( `var last_${name} = ${snippet};` );
fragment.builders.create.addLine( `var last_${name} = ${snippet};` );
const mountStatement = `${before}.insertAdjacentHTML( 'afterend', last_${name} );`;
const detachStatement = `${generator.helper( 'detachBetween' )}( ${before}, ${after} );`;
if ( isToplevel ) {
generator.current.builders.mount.addLine( mountStatement );
fragment.builders.mount.addLine( mountStatement );
} else {
generator.current.builders.create.addLine( mountStatement );
fragment.builders.create.addLine( mountStatement );
}
const fragment = findBlock( generator.current );
if ( !fragment.tmp ) fragment.tmp = fragment.getUniqueName( 'tmp' );
// TODO this should be unnecessary once we separate fragments from state
const parentFragment = findBlock( fragment );
if ( !parentFragment.tmp ) parentFragment.tmp = parentFragment.getUniqueName( 'tmp' );
generator.current.builders.update.addBlock( deindent`
if ( ( ${fragment.tmp} = ${snippet} ) !== last_${name} ) {
last_${name} = ${fragment.tmp};
fragment.builders.update.addBlock( deindent`
if ( ( ${parentFragment.tmp} = ${snippet} ) !== last_${name} ) {
last_${name} = ${parentFragment.tmp};
${detachStatement}
${mountStatement}
}
` );
generator.current.builders.detachRaw.addBlock( detachStatement );
fragment.builders.detachRaw.addBlock( detachStatement );
}

@ -1,8 +1,8 @@
export default function visitText ( generator, node ) {
if ( generator.current.namespace && !/\S/.test( node.data ) ) {
export default function visitText ( generator, fragment, node ) {
if ( fragment.namespace && !/\S/.test( node.data ) ) {
return;
}
const name = generator.current.getUniqueName( `text` );
generator.addElement( name, `${generator.helper( 'createText' )}( ${JSON.stringify( node.data )} )`, false );
const name = fragment.getUniqueName( `text` );
fragment.addElement( name, `${generator.helper( 'createText' )}( ${JSON.stringify( node.data )} )`, false );
}

@ -1,12 +1,12 @@
export default function visitYieldTag ( generator ) {
export default function visitYieldTag ( generator, fragment ) {
const anchor = `yield_anchor`;
generator.createAnchor( anchor );
fragment.createAnchor( anchor );
generator.current.builders.mount.addLine(
`${generator.current.component}._yield && ${generator.current.component}._yield.mount( ${generator.current.target}, ${anchor} );`
fragment.builders.mount.addLine(
`${fragment.component}._yield && ${fragment.component}._yield.mount( ${fragment.target}, ${anchor} );`
);
generator.current.builders.destroy.addLine(
`${generator.current.component}._yield && ${generator.current.component}._yield.destroy( detach );`
fragment.builders.destroy.addLine(
`${fragment.component}._yield && ${fragment.component}._yield.destroy( detach );`
);
}

@ -1,7 +1,7 @@
import addComponentBinding from './addComponentBinding.js';
import deindent from '../../../../utils/deindent.js';
export default function addComponentAttributes ( generator, node, local ) {
export default function addComponentAttributes ( generator, fragment, node, local ) {
local.staticAttributes = [];
local.dynamicAttributes = [];
local.bindings = [];
@ -78,7 +78,7 @@ export default function addComponentAttributes ( generator, node, local ) {
else if ( attribute.type === 'EventHandler' ) {
// TODO verify that it's a valid callee (i.e. built-in or declared method)
generator.addSourcemapLocations( attribute.expression );
generator.code.prependRight( attribute.expression.start, `${generator.current.component}.` );
generator.code.prependRight( attribute.expression.start, `${fragment.component}.` );
const usedContexts = [];
attribute.expression.arguments.forEach( arg => {
@ -94,8 +94,8 @@ export default function addComponentAttributes ( generator, node, local ) {
const declarations = usedContexts.map( name => {
if ( name === 'root' ) return 'var root = this._context.root;';
const listName = generator.current.listNames.get( name );
const indexName = generator.current.indexNames.get( name );
const listName = fragment.listNames.get( name );
const indexName = fragment.indexNames.get( name );
return `var ${listName} = this._context.${listName}, ${indexName} = this._context.${indexName}, ${name} = ${listName}[${indexName}]`;
});
@ -110,18 +110,18 @@ export default function addComponentAttributes ( generator, node, local ) {
}
else if ( attribute.type === 'Binding' ) {
addComponentBinding( generator, node, attribute, generator.current, local );
addComponentBinding( generator, node, attribute, fragment, local );
}
else if ( attribute.type === 'Ref' ) {
generator.usesRefs = true;
local.create.addLine(
`${generator.current.component}.refs.${attribute.name} = ${local.name};`
`${fragment.component}.refs.${attribute.name} = ${local.name};`
);
generator.current.builders.destroy.addLine( deindent`
if ( ${generator.current.component}.refs.${attribute.name} === ${local.name} ) ${generator.current.component}.refs.${attribute.name} = null;
fragment.builders.destroy.addLine( deindent`
if ( ${fragment.component}.refs.${attribute.name} === ${local.name} ) ${fragment.component}.refs.${attribute.name} = null;
` );
}

@ -2,7 +2,7 @@ import deindent from '../../../../utils/deindent.js';
import flattenReference from '../../../../utils/flattenReference.js';
import getSetter from './binding/getSetter.js';
export default function createBinding ( generator, node, attribute, current, local ) {
export default function createBinding ( generator, node, attribute, fragment, local ) {
const { name, keypath } = flattenReference( attribute.value );
const { snippet, contexts, dependencies } = generator.contextualise( attribute.value );
@ -12,14 +12,14 @@ export default function createBinding ( generator, node, attribute, current, loc
if ( !~local.allUsedContexts.indexOf( context ) ) local.allUsedContexts.push( context );
});
const contextual = current.contexts.has( name );
const contextual = fragment.contexts.has( name );
let obj;
let prop;
if ( contextual ) {
obj = current.listNames.get( name );
prop = current.indexNames.get( name );
obj = fragment.listNames.get( name );
prop = fragment.indexNames.get( name );
} else if ( attribute.value.type === 'MemberExpression' ) {
prop = `'[✂${attribute.value.property.start}-${attribute.value.property.end}✂]'`;
obj = `[✂${attribute.value.object.start}-${attribute.value.object.end}✂]`;
@ -35,16 +35,16 @@ export default function createBinding ( generator, node, attribute, current, loc
prop
});
const setter = getSetter({ current, name, keypath, context: '_context', attribute, dependencies, value: 'value' });
const setter = getSetter({ fragment, name, keypath, context: '_context', attribute, dependencies, value: 'value' });
generator.hasComplexBindings = true;
const updating = generator.current.getUniqueName( `${local.name}_updating` );
const updating = fragment.getUniqueName( `${local.name}_updating` );
local.create.addBlock( deindent`
var ${updating} = false;
${generator.current.component}._bindings.push( function () {
${fragment.component}._bindings.push( function () {
if ( ${local.name}._torndown ) return;
${local.name}.observe( '${attribute.name}', function ( value ) {
if ( ${updating} ) return;

@ -5,7 +5,7 @@ import flattenReference from '../../../../utils/flattenReference.js';
import getStaticAttributeValue from './binding/getStaticAttributeValue.js';
import findBlock from '../../utils/findBlock.js';
export default function addElementAttributes ( generator, node, local ) {
export default function addElementAttributes ( generator, fragment, node, local ) {
node.attributes.forEach( attribute => {
const name = attribute.name;
@ -43,7 +43,7 @@ export default function addElementAttributes ( generator, node, local ) {
// special case autofocus. has to be handled in a bit of a weird way
if ( name === 'autofocus' ) {
generator.current.autofocus = local.name;
fragment.autofocus = local.name;
}
}
@ -106,12 +106,14 @@ export default function addElementAttributes ( generator, node, local ) {
}
local.create.addLine( updater );
const fragment = findBlock( generator.current );
if ( !fragment.tmp ) fragment.tmp = fragment.getUniqueName( 'tmp' );
// TODO unnecessary soon...
const parentFragment = findBlock( fragment );
if ( !parentFragment.tmp ) parentFragment.tmp = parentFragment.getUniqueName( 'tmp' );
local.update.addBlock( deindent`
if ( ( ${fragment.tmp} = ${snippet} ) !== ${last} ) {
${last} = ${fragment.tmp};
if ( ( ${parentFragment.tmp} = ${snippet} ) !== ${last} ) {
${last} = ${parentFragment.tmp};
${updater}
}
` );
@ -158,7 +160,7 @@ export default function addElementAttributes ( generator, node, local ) {
const flattened = flattenReference( attribute.expression.callee );
if ( flattened.name !== 'event' && flattened.name !== 'this' ) {
// allow event.stopPropagation(), this.select() etc
generator.code.prependRight( attribute.expression.start, `${generator.current.component}.` );
generator.code.prependRight( attribute.expression.start, `${fragment.component}.` );
}
const usedContexts = [];
@ -175,23 +177,23 @@ export default function addElementAttributes ( generator, node, local ) {
const declarations = usedContexts.map( name => {
if ( name === 'root' ) return 'var root = this.__svelte.root;';
const listName = generator.current.listNames.get( name );
const indexName = generator.current.indexNames.get( name );
const listName = fragment.listNames.get( name );
const indexName = fragment.indexNames.get( name );
return `var ${listName} = this.__svelte.${listName}, ${indexName} = this.__svelte.${indexName}, ${name} = ${listName}[${indexName}]`;
});
const handlerName = generator.current.getUniqueName( `${name}_handler` );
const handlerName = fragment.getUniqueName( `${name}_handler` );
const handlerBody = ( declarations.length ? declarations.join( '\n' ) + '\n\n' : '' ) + `[✂${attribute.expression.start}-${attribute.expression.end}✂];`;
if ( generator.events.has( name ) ) {
local.create.addBlock( deindent`
var ${handlerName} = ${generator.alias( 'template' )}.events.${name}.call( ${generator.current.component}, ${local.name}, function ( event ) {
var ${handlerName} = ${generator.alias( 'template' )}.events.${name}.call( ${fragment.component}, ${local.name}, function ( event ) {
${handlerBody}
}.bind( ${local.name} ) );
` );
generator.current.builders.destroy.addLine( deindent`
fragment.builders.destroy.addLine( deindent`
${handlerName}.teardown();
` );
} else {
@ -203,25 +205,25 @@ export default function addElementAttributes ( generator, node, local ) {
${generator.helper( 'addEventListener' )}( ${local.name}, '${name}', ${handlerName} );
` );
generator.current.builders.destroy.addLine( deindent`
fragment.builders.destroy.addLine( deindent`
${generator.helper( 'removeEventListener' )}( ${local.name}, '${name}', ${handlerName} );
` );
}
}
else if ( attribute.type === 'Binding' ) {
addElementBinding( generator, node, attribute, generator.current, local );
addElementBinding( generator, node, attribute, fragment, local );
}
else if ( attribute.type === 'Ref' ) {
generator.usesRefs = true;
local.create.addLine(
`${generator.current.component}.refs.${name} = ${local.name};`
`${fragment.component}.refs.${name} = ${local.name};`
);
generator.current.builders.destroy.addLine( deindent`
if ( ${generator.current.component}.refs.${name} === ${local.name} ) ${generator.current.component}.refs.${name} = null;
fragment.builders.destroy.addLine( deindent`
if ( ${fragment.component}.refs.${name} === ${local.name} ) ${fragment.component}.refs.${name} = null;
` );
}

@ -3,7 +3,7 @@ import flattenReference from '../../../../utils/flattenReference.js';
import getSetter from './binding/getSetter.js';
import getStaticAttributeValue from './binding/getStaticAttributeValue.js';
export default function createBinding ( generator, node, attribute, current, local ) {
export default function createBinding ( generator, node, attribute, fragment, local ) {
const { name, keypath } = flattenReference( attribute.value );
const { snippet, contexts, dependencies } = generator.contextualise( attribute.value );
@ -13,15 +13,15 @@ export default function createBinding ( generator, node, attribute, current, loc
if ( !~local.allUsedContexts.indexOf( context ) ) local.allUsedContexts.push( context );
});
const handler = current.getUniqueName( `${local.name}_change_handler` );
const handler = fragment.getUniqueName( `${local.name}_change_handler` );
const isMultipleSelect = node.name === 'select' && node.attributes.find( attr => attr.name.toLowerCase() === 'multiple' ); // TODO use getStaticAttributeValue
const type = getStaticAttributeValue( node, 'type' );
const bindingGroup = attribute.name === 'group' ? getBindingGroup( generator, current, attribute, keypath ) : null;
const bindingGroup = attribute.name === 'group' ? getBindingGroup( generator, fragment, attribute, keypath ) : null;
const value = getBindingValue( generator, local, node, attribute, isMultipleSelect, bindingGroup, type );
const eventName = getBindingEventName( node );
let setter = getSetter({ current, name, keypath, context: '__svelte', attribute, dependencies, value });
let setter = getSetter({ fragment, name, keypath, context: '__svelte', attribute, dependencies, value });
let updateElement;
// <select> special case
@ -30,9 +30,9 @@ export default function createBinding ( generator, node, attribute, current, loc
setter = `var selectedOption = ${local.name}.selectedOptions[0] || ${local.name}.options[0];\n${setter}`;
}
const value = current.getUniqueName( 'value' );
const i = current.getUniqueName( 'i' );
const option = current.getUniqueName( 'option' );
const value = fragment.getUniqueName( 'value' );
const i = fragment.getUniqueName( 'i' );
const option = fragment.getUniqueName( 'option' );
const ifStatement = isMultipleSelect ?
deindent`
@ -67,11 +67,11 @@ export default function createBinding ( generator, node, attribute, current, loc
`${local.name}.__value === ${snippet}`;
local.create.addLine(
`${current.component}._bindingGroups[${bindingGroup}].push( ${local.name} );`
`${fragment.component}._bindingGroups[${bindingGroup}].push( ${local.name} );`
);
local.destroy.addBlock(
`${current.component}._bindingGroups[${bindingGroup}].splice( ${current.component}._bindingGroups[${bindingGroup}].indexOf( ${local.name} ), 1 );`
`${fragment.component}._bindingGroups[${bindingGroup}].splice( ${fragment.component}._bindingGroups[${bindingGroup}].indexOf( ${local.name} ), 1 );`
);
updateElement = `${local.name}.checked = ${condition};`;
@ -82,7 +82,7 @@ export default function createBinding ( generator, node, attribute, current, loc
updateElement = `${local.name}.${attribute.name} = ${snippet};`;
}
const updating = generator.current.getUniqueName( `${local.name}_updating` );
const updating = generator.fragment.getUniqueName( `${local.name}_updating` );
local.create.addBlock( deindent`
var ${updating} = false;
@ -104,7 +104,7 @@ export default function createBinding ( generator, node, attribute, current, loc
}
` );
current.builders.destroy.addLine( deindent`
fragment.builders.destroy.addLine( deindent`
${generator.helper( 'removeEventListener' )}( ${local.name}, '${eventName}', ${handler} );
` );
}
@ -138,7 +138,7 @@ function getBindingValue ( generator, local, node, attribute, isMultipleSelect,
// <input type='checkbox' bind:group='foo'>
if ( attribute.name === 'group' ) {
if ( type === 'checkbox' ) {
return `${generator.helper( 'getBindingGroupValue' )}( ${generator.current.component}._bindingGroups[${bindingGroup}] )`;
return `${generator.helper( 'getBindingGroupValue' )}( ${generator.fragment.component}._bindingGroups[${bindingGroup}] )`;
}
return `${local.name}.__value`;
@ -153,7 +153,7 @@ function getBindingValue ( generator, local, node, attribute, isMultipleSelect,
return `${local.name}.${attribute.name}`;
}
function getBindingGroup ( generator, current, attribute, keypath ) {
function getBindingGroup ( generator, fragment, attribute, keypath ) {
// TODO handle contextual bindings — `keypath` should include unique ID of
// each block that provides context
let index = generator.bindingGroups.indexOf( keypath );

@ -1,28 +1,28 @@
import deindent from '../../../../../utils/deindent.js';
export default function getSetter ({ current, name, keypath, context, attribute, dependencies, value }) {
if ( current.contexts.has( name ) ) {
export default function getSetter ({ fragment, name, keypath, context, attribute, dependencies, value }) {
if ( fragment.contexts.has( name ) ) {
const prop = dependencies[0];
const tail = attribute.value.type === 'MemberExpression' ? getTailSnippet( attribute.value ) : '';
return deindent`
var list = this.${context}.${current.listNames.get( name )};
var index = this.${context}.${current.indexNames.get( name )};
var list = this.${context}.${fragment.listNames.get( name )};
var index = this.${context}.${fragment.indexNames.get( name )};
list[index]${tail} = ${value};
${current.component}._set({ ${prop}: ${current.component}.get( '${prop}' ) });
${fragment.component}._set({ ${prop}: ${fragment.component}.get( '${prop}' ) });
`;
}
if ( attribute.value.type === 'MemberExpression' ) {
return deindent`
var ${name} = ${current.component}.get( '${name}' );
var ${name} = ${fragment.component}.get( '${name}' );
${keypath} = ${value};
${current.component}._set({ ${name}: ${name} });
${fragment.component}._set({ ${name}: ${name} });
`;
}
return `${current.component}._set({ ${name}: ${value} });`;
return `${fragment.component}._set({ ${name}: ${value} });`;
}
function getTailSnippet ( node ) {

@ -11,7 +11,7 @@ const associatedEvents = {
scrollY: 'scroll'
};
export default function visitWindow ( generator, node ) {
export default function visitWindow ( generator, fragment, node ) {
const events = {};
node.attributes.forEach( attribute => {
@ -25,16 +25,16 @@ export default function visitWindow ( generator, node ) {
generator.code.prependRight( attribute.expression.start, 'component.' );
}
const handlerName = generator.current.getUniqueName( `onwindow${attribute.name}` );
const handlerName = fragment.getUniqueName( `onwindow${attribute.name}` );
generator.current.builders.create.addBlock( deindent`
fragment.builders.create.addBlock( deindent`
var ${handlerName} = function ( event ) {
[${attribute.expression.start}-${attribute.expression.end}];
};
window.addEventListener( '${attribute.name}', ${handlerName} );
` );
generator.current.builders.destroy.addBlock( deindent`
fragment.builders.destroy.addBlock( deindent`
window.removeEventListener( '${attribute.name}', ${handlerName} );
` );
}
@ -62,11 +62,11 @@ export default function visitWindow ( generator, node ) {
});
Object.keys( events ).forEach( event => {
const handlerName = generator.current.getUniqueName( `onwindow${event}` );
const handlerName = fragment.getUniqueName( `onwindow${event}` );
const props = events[ event ].join( ',\n' );
generator.current.builders.create.addBlock( deindent`
fragment.builders.create.addBlock( deindent`
var ${handlerName} = function ( event ) {
component.set({
${props}
@ -75,7 +75,7 @@ export default function visitWindow ( generator, node ) {
window.addEventListener( '${event}', ${handlerName} );
` );
generator.current.builders.destroy.addBlock( deindent`
fragment.builders.destroy.addBlock( deindent`
window.removeEventListener( '${event}', ${handlerName} );
` );
});

Loading…
Cancel
Save