separate current *fragment* from current *generator state*

pull/453/head
Rich-Harris 8 years ago
parent abf774bc57
commit 421f3d698f

@ -30,8 +30,6 @@ export default class Generator {
// in dev mode
this.expectedProperties = new Set();
this.elementDepth = 0;
this.code = new MagicString( source );
this.css = parsed.css ? processCss( parsed, this.code ) : null;
this.cssId = parsed.css ? `svelte-${parsed.hash}` : '';

@ -3,16 +3,16 @@ export default class Fragment {
Object.assign( this, options );
}
addElement ( name, renderStatement, needsIdentifier = false ) {
const isToplevel = this.localElementDepth === 0;
addElement ( name, renderStatement, target, localElementDepth, needsIdentifier = false ) {
const isToplevel = localElementDepth === 0;
if ( needsIdentifier || isToplevel ) {
this.builders.create.addLine(
`var ${name} = ${renderStatement};`
);
this.createMountStatement( name );
this.createMountStatement( name, target );
} else {
this.builders.create.addLine( `${this.generator.helper( 'appendNode' )}( ${renderStatement}, ${this.target} );` );
this.builders.create.addLine( `${this.generator.helper( 'appendNode' )}( ${renderStatement}, ${target} );` );
}
if ( isToplevel ) {
@ -24,16 +24,16 @@ export default class Fragment {
return new Fragment( Object.assign( {}, this, options, { parent: this } ) );
}
createAnchor ( name ) {
createAnchor ( name, target, localElementDepth ) {
const renderStatement = `${this.generator.helper( 'createComment' )}()`;
this.addElement( name, renderStatement, true );
this.addElement( name, renderStatement, target, localElementDepth, true );
}
createMountStatement ( name ) {
if ( this.target === 'target' ) {
createMountStatement ( name, target ) {
if ( 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} );` );
this.builders.create.addLine( `${this.generator.helper( 'appendNode' )}( ${name}, ${target} );` );
}
}
}

@ -112,9 +112,6 @@ export default function dom ( parsed, source, options ) {
type: 'block',
generator,
name: generator.alias( 'create_main_fragment' ),
namespace,
target: 'target',
localElementDepth: 0,
key: null,
component,
@ -130,8 +127,14 @@ export default function dom ( parsed, source, options ) {
getUniqueName
});
const state = {
namespace,
target: 'target',
localElementDepth: 0
};
parsed.html.children.forEach( node => {
visit( generator, mainFragment, node );
visit( generator, mainFragment, state, node );
});
generator.addRenderer( mainFragment );

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

@ -20,13 +20,13 @@ function stringifyProps ( props ) {
return `{ ${joined} }`;
}
export default function visitComponent ( generator, fragment, node ) {
export default function visitComponent ( generator, fragment, state, node ) {
const hasChildren = node.children.length > 0;
const name = fragment.getUniqueName( capDown( node.name === ':Self' ? generator.name : node.name ) );
const local = {
name,
namespace: fragment.namespace,
namespace: state.namespace,
isComponent: true,
allUsedContexts: [],
@ -35,7 +35,7 @@ export default function visitComponent ( generator, fragment, node ) {
update: new CodeBuilder()
};
const isToplevel = fragment.localElementDepth === 0;
const isToplevel = state.localElementDepth === 0;
generator.hasComponents = true;
@ -70,7 +70,7 @@ export default function visitComponent ( generator, fragment, node ) {
}
const componentInitProperties = [
`target: ${!isToplevel ? fragment.target: 'null'}`,
`target: ${!isToplevel ? state.target: 'null'}`,
`_root: ${fragment.component}._root || ${fragment.component}`
];
@ -81,13 +81,16 @@ export default function visitComponent ( generator, fragment, node ) {
const childFragment = fragment.child({
type: 'component',
name: generator.getUniqueName( `render_${name}_yield_fragment` ), // TODO should getUniqueName happen inside Fragment? probably
target: 'target',
localElementDepth: 0,
builders: getBuilders()
});
const childState = Object.assign( {}, state, {
target: 'target',
localElementDepth: 0
});
node.children.forEach( child => {
visit( generator, childFragment, child );
visit( generator, childFragment, childState, child );
});
const yieldFragment = fragment.getUniqueName( `${name}_yield_fragment` );

@ -3,7 +3,7 @@ import deindent from '../../../utils/deindent.js';
import getBuilders from '../utils/getBuilders.js';
import visit from '../visit.js';
export default function visitEachBlock ( generator, fragment, node ) {
export default function visitEachBlock ( generator, fragment, state, node ) {
const name = generator.getUniqueName( `each_block` );
const renderer = generator.getUniqueName( `render_each_block` );
const elseName = generator.getUniqueName( `${name}_else` );
@ -13,14 +13,14 @@ export default function visitEachBlock ( generator, fragment, node ) {
const listName = fragment.getUniqueName( `${name}_value` );
const isToplevel = fragment.localElementDepth === 0;
const isToplevel = state.localElementDepth === 0;
generator.addSourcemapLocations( node.expression );
const { dependencies, snippet } = generator.contextualise( fragment, node.expression );
const anchor = fragment.getUniqueName( `${name}_anchor` );
fragment.createAnchor( anchor );
fragment.createAnchor( anchor, state.target, state.localElementDepth );
const localVars = {};
@ -191,11 +191,9 @@ export default function visitEachBlock ( generator, fragment, node ) {
const childFragment = fragment.child({
type: 'block',
name: renderer,
target: 'target',
expression: node.expression,
context: node.context,
key: node.key,
localElementDepth: 0,
component: getUniqueName( 'component' ),
@ -211,8 +209,13 @@ export default function visitEachBlock ( generator, fragment, node ) {
getUniqueName
});
const childState = Object.assign( {}, state, {
target: 'target',
localElementDepth: 0
});
node.children.forEach( child => {
visit( generator, childFragment, child );
visit( generator, childFragment, childState, child );
});
generator.addRenderer( childFragment );
@ -221,14 +224,12 @@ export default function visitEachBlock ( generator, fragment, node ) {
const childFragment = fragment.child({
type: 'block',
name: renderElse,
target: 'target',
localElementDepth: 0,
builders: getBuilders(),
getUniqueName: generator.getUniqueNameMaker( fragment.params )
});
node.else.children.forEach( child => {
visit( generator, childFragment, child );
visit( generator, childFragment, childState, child );
});
generator.addRenderer( childFragment );

@ -9,20 +9,20 @@ const meta = {
':Window': visitWindow
};
export default function visitElement ( generator, fragment, node ) {
export default function visitElement ( generator, fragment, state, node ) {
if ( node.name in meta ) {
return meta[ node.name ]( generator, fragment, node );
}
if ( generator.components.has( node.name ) || node.name === ':Self' ) {
return visitComponent( generator, fragment, node );
return visitComponent( generator, fragment, state, node );
}
const name = fragment.getUniqueName( node.name );
const local = {
name,
namespace: node.name === 'svg' ? 'http://www.w3.org/2000/svg' : fragment.namespace,
namespace: node.name === 'svg' ? 'http://www.w3.org/2000/svg' : state.namespace,
isComponent: false,
allUsedContexts: [],
@ -32,7 +32,7 @@ export default function visitElement ( generator, fragment, node ) {
destroy: new CodeBuilder()
};
const isToplevel = fragment.localElementDepth === 0;
const isToplevel = state.localElementDepth === 0;
addElementAttributes( generator, fragment, node, local );
@ -76,7 +76,7 @@ export default function visitElement ( generator, fragment, node ) {
render = `var ${name} = ${generator.helper( 'createElement' )}( '${node.name}' );`;
}
if ( generator.cssId && !generator.elementDepth ) {
if ( generator.cssId && !state.elementDepth ) {
render += `\n${generator.helper( 'setAttribute' )}( ${name}, '${generator.cssId}', '' );`;
}
@ -96,25 +96,19 @@ export default function visitElement ( generator, fragment, node ) {
if ( !local.update.isEmpty() ) fragment.builders.update.addBlock( local.update );
if ( !local.destroy.isEmpty() ) fragment.builders.destroy.addBlock( local.destroy );
fragment.createMountStatement( name );
fragment.createMountStatement( name, state.target );
const childFragment = fragment.child({
type: 'element',
namespace: local.namespace,
const childState = Object.assign( {}, state, {
elementDepth: state.elementDepth + 1,
localElementDepth: state.localElementDepth + 1,
target: name,
parent: fragment,
localElementDepth: fragment.localElementDepth + 1,
key: null
namespace: local.namespace
});
generator.elementDepth += 1;
node.children.forEach( child => {
visit( generator, childFragment, child );
visit( generator, fragment, childState, child );
});
generator.elementDepth -= 1;
if ( node.initialUpdate ) {
fragment.builders.create.addBlock( node.initialUpdate );
}

@ -2,7 +2,7 @@ import deindent from '../../../utils/deindent.js';
import getBuilders from '../utils/getBuilders.js';
import visit from '../visit.js';
function getConditionsAndBlocks ( generator, fragment, node, _name, i = 0 ) {
function getConditionsAndBlocks ( generator, fragment, state, node, _name, i = 0 ) {
generator.addSourcemapLocations( node.expression );
const name = generator.getUniqueName( `${_name}_${i}` );
@ -11,12 +11,12 @@ function getConditionsAndBlocks ( generator, fragment, node, _name, i = 0 ) {
block: name
}];
generateBlock( generator, fragment, node, name, 'block' );
generateBlock( generator, fragment, state, node, name, 'block' );
if ( node.else && node.else.children.length === 1 &&
node.else.children[0].type === 'IfBlock' ) {
conditionsAndBlocks.push(
...getConditionsAndBlocks( generator, fragment, node.else.children[0], _name, i + 1 )
...getConditionsAndBlocks( generator, fragment, state, node.else.children[0], _name, i + 1 )
);
} else {
const name = generator.getUniqueName( `${_name}_${i + 1}` );
@ -26,42 +26,44 @@ function getConditionsAndBlocks ( generator, fragment, node, _name, i = 0 ) {
});
if ( node.else ) {
generateBlock( generator, fragment, node.else, name, 'block' );
generateBlock( generator, fragment, state, node.else, name, 'block' );
}
}
return conditionsAndBlocks;
}
function generateBlock ( generator, fragment, node, name, type ) {
function generateBlock ( generator, fragment, state, node, name, type ) {
const childFragment = fragment.child({
type,
isIfBlock: true,
name,
builders: getBuilders()
});
const childState = Object.assign( {}, state, {
target: 'target',
builders: getBuilders(),
localElementDepth: 0
});
// walk the children here
node.children.forEach( node => {
visit( generator, childFragment, node );
visit( generator, childFragment, childState, node );
});
generator.addRenderer( childFragment );
}
export default function visitIfBlock ( generator, fragment, node ) {
export default function visitIfBlock ( generator, fragment, state, node ) {
const params = fragment.params.join( ', ' );
const name = generator.getUniqueName( `if_block` );
const getBlock = fragment.getUniqueName( `get_block` );
const currentBlock = fragment.getUniqueName( `current_block` );
const _currentBlock = fragment.getUniqueName( `_current_block` );
const isToplevel = fragment.localElementDepth === 0;
const conditionsAndBlocks = getConditionsAndBlocks( generator, fragment, node, generator.getUniqueName( `render_if_block` ) );
const isToplevel = state.localElementDepth === 0;
const conditionsAndBlocks = getConditionsAndBlocks( generator, fragment, state, node, generator.getUniqueName( `render_if_block` ) );
const anchor = `${name}_anchor`;
fragment.createAnchor( anchor );
fragment.createAnchor( anchor, state.target, state.localElementDepth );
fragment.builders.create.addBlock( deindent`
function ${getBlock} ( ${params} ) {

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

@ -1,7 +1,7 @@
import deindent from '../../../utils/deindent.js';
import findBlock from '../utils/findBlock.js';
export default function visitRawMustacheTag ( generator, fragment, node ) {
export default function visitRawMustacheTag ( generator, fragment, state, node ) {
const name = fragment.getUniqueName( 'raw' );
const { snippet } = generator.contextualise( fragment, node.expression );
@ -9,12 +9,12 @@ export default function visitRawMustacheTag ( generator, fragment, node ) {
// we would have used comments here, but the `insertAdjacentHTML` api only
// exists for `Element`s.
const before = `${name}_before`;
fragment.addElement( before, `${generator.helper( 'createElement' )}( 'noscript' )`, true );
fragment.addElement( before, `${generator.helper( 'createElement' )}( 'noscript' )`, state.target, state.localElementDepth, true );
const after = `${name}_after`;
fragment.addElement( after, `${generator.helper( 'createElement' )}( 'noscript' )`, true );
fragment.addElement( after, `${generator.helper( 'createElement' )}( 'noscript' )`, state.target, state.localElementDepth, true );
const isToplevel = fragment.localElementDepth === 0;
const isToplevel = state.localElementDepth === 0;
fragment.builders.create.addLine( `var last_${name} = ${snippet};` );
const mountStatement = `${before}.insertAdjacentHTML( 'afterend', last_${name} );`;

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

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

@ -1,5 +1,6 @@
export default {
html: `<div style="color: red;">red</div>`,
test ( assert, component, target ) {
const div = target.querySelector( 'div' );

Loading…
Cancel
Save