populate state in preprocess step, including naming DOM nodes

pull/512/head
Rich-Harris 8 years ago
parent e345287a76
commit 162281ef4f

@ -43,14 +43,14 @@ export default function dom ( parsed, source, options ) {
const { computations, hasJs, templateProperties, namespace } = generator.parseJs();
const block = preprocess( generator, parsed.html );
const state = {
namespace,
parentNode: null,
isTopLevel: true
};
const block = preprocess( generator, state, parsed.html );
parsed.html.children.forEach( node => {
visit( generator, block, state, node );
});

@ -1,17 +1,62 @@
import Block from './Block.js';
import { trimStart, trimEnd } from '../../utils/trim.js';
import { assign } from '../../shared/index.js';
function isElseIf ( node ) {
return node && node.children.length === 1 && node.children[0].type === 'IfBlock';
}
function getChildState ( parent, child ) {
return assign( {}, parent, { name: null, parentNode: null }, child || {} );
}
// Whitespace inside one of these elements will not result in
// a whitespace node being created in any circumstances. (This
// list is almost certainly very incomplete)
const elementsWithoutText = new Set([
'audio',
'datalist',
'dl',
'ol',
'optgroup',
'select',
'ul',
'video'
]);
const preprocessors = {
MustacheTag: ( generator, block, node ) => {
MustacheTag: ( generator, block, state, node ) => {
const dependencies = block.findDependencies( node.expression );
block.addDependencies( dependencies );
node._state = getChildState( state, {
name: block.getUniqueName( 'text' )
});
},
RawMustacheTag: ( generator, block, state, node ) => {
const dependencies = block.findDependencies( node.expression );
block.addDependencies( dependencies );
const basename = block.getUniqueName( 'raw' );
const name = block.getUniqueName( `${basename}_before` );
node._state = getChildState( state, { basename, name });
},
Text: ( generator, block, state, node ) => {
node._state = getChildState( state );
if ( !/\S/.test( node.data ) ) {
if ( state.namespace ) return;
if ( elementsWithoutText.has( state.parentNodeName ) ) return;
}
node._state.shouldCreate = true;
node._state.name = block.getUniqueName( `text` );
},
IfBlock: ( generator, block, node ) => {
IfBlock: ( generator, block, state, node ) => {
const blocks = [];
let dynamic = false;
@ -23,8 +68,10 @@ const preprocessors = {
name: generator.getUniqueName( `create_if_block` )
});
node._state = getChildState( state );
blocks.push( node._block );
preprocessChildren( generator, node._block, node );
preprocessChildren( generator, node._block, node._state, node );
if ( node._block.dependencies.size > 0 ) {
dynamic = true;
@ -38,8 +85,10 @@ const preprocessors = {
name: generator.getUniqueName( `create_if_block` )
});
node.else._state = getChildState( state );
blocks.push( node.else._block );
preprocessChildren( generator, node.else._block, node.else );
preprocessChildren( generator, node.else._block, node.else._state, node.else );
if ( node.else._block.dependencies.size > 0 ) {
dynamic = true;
@ -57,7 +106,7 @@ const preprocessors = {
generator.blocks.push( ...blocks );
},
EachBlock: ( generator, block, node ) => {
EachBlock: ( generator, block, state, node ) => {
const dependencies = block.findDependencies( node.expression );
block.addDependencies( dependencies );
@ -97,8 +146,12 @@ const preprocessors = {
params: block.params.concat( listName, context, indexName )
});
node._state = getChildState( state, {
inEachBlock: true
});
generator.blocks.push( node._block );
preprocessChildren( generator, node._block, node );
preprocessChildren( generator, node._block, node._state, node );
block.addDependencies( node._block.dependencies );
node._block.hasUpdateMethod = node._block.dependencies.size > 0;
@ -107,13 +160,32 @@ const preprocessors = {
name: generator.getUniqueName( `${node._block.name}_else` )
});
node.else._state = getChildState( state );
generator.blocks.push( node.else._block );
preprocessChildren( generator, node.else._block, node.else );
preprocessChildren( generator, node.else._block, node.else._state, node.else );
node.else._block.hasUpdateMethod = node.else._block.dependencies.size > 0;
}
},
Element: ( generator, block, node ) => {
Element: ( generator, block, state, node ) => {
const isComponent = generator.components.has( node.name ) || node.name === ':Self';
if ( isComponent ) {
node._state = getChildState( state );
} else {
const name = block.getUniqueName( node.name );
node._state = getChildState( state, {
isTopLevel: false,
name,
parentNode: name,
parentNodeName: node.name,
namespace: node.name === 'svg' ? 'http://www.w3.org/2000/svg' : state.namespace,
allUsedContexts: []
});
}
node.attributes.forEach( attribute => {
if ( attribute.type === 'Attribute' && attribute.value !== true ) {
attribute.value.forEach( chunk => {
@ -130,8 +202,6 @@ const preprocessors = {
}
});
const isComponent = generator.components.has( node.name ) || node.name === ':Self';
if ( node.children.length ) {
if ( isComponent ) {
const name = block.getUniqueName( ( node.name === ':Self' ? generator.name : node.name ).toLowerCase() );
@ -141,21 +211,19 @@ const preprocessors = {
});
generator.blocks.push( node._block );
preprocessChildren( generator, node._block, node );
preprocessChildren( generator, node._block, node._state, node );
block.addDependencies( node._block.dependencies );
node._block.hasUpdateMethod = node._block.dependencies.size > 0;
}
else {
preprocessChildren( generator, block, node );
preprocessChildren( generator, block, node._state, node );
}
}
}
};
preprocessors.RawMustacheTag = preprocessors.MustacheTag;
function preprocessChildren ( generator, block, node ) {
function preprocessChildren ( generator, block, state, node ) {
// glue text nodes together
const cleaned = [];
let lastChild;
@ -170,6 +238,7 @@ function preprocessChildren ( generator, block, node ) {
cleaned.push( child );
}
if ( lastChild ) lastChild.next = child;
lastChild = child;
});
@ -177,11 +246,11 @@ function preprocessChildren ( generator, block, node ) {
cleaned.forEach( child => {
const preprocess = preprocessors[ child.type ];
if ( preprocess ) preprocess( generator, block, child );
if ( preprocess ) preprocess( generator, block, state, child );
});
}
export default function preprocess ( generator, node ) {
export default function preprocess ( generator, state, node ) {
const block = new Block({
generator,
name: generator.alias( 'create_main_fragment' ),
@ -199,7 +268,7 @@ export default function preprocess ( generator, node ) {
});
generator.blocks.push( block );
preprocessChildren( generator, block, node );
preprocessChildren( generator, block, state, node );
block.hasUpdateMethod = block.dependencies.size > 0;
// trim leading and trailing whitespace from the top level

@ -36,9 +36,7 @@ export default function visitComponent ( generator, block, state, node ) {
const hasChildren = node.children.length > 0;
const name = block.getUniqueName( ( node.name === ':Self' ? generator.name : node.name ).toLowerCase() );
const childState = Object.assign( {}, state, {
parentNode: null
});
const childState = node._state;
const local = {
name,

@ -87,22 +87,13 @@ export default function visitEachBlock ( generator, block, state, node ) {
` );
}
const childState = Object.assign( {}, state, {
parentNode: null,
inEachBlock: true
});
node.children.forEach( child => {
visit( generator, node._block, childState, child );
visit( generator, node._block, node._state, child );
});
if ( node.else ) {
const childState = Object.assign( {}, state, {
parentNode: null
});
node.else.children.forEach( child => {
visit( generator, node.else._block, childState, child );
visit( generator, node.else._block, node.else._state, child );
});
}
}

@ -34,15 +34,8 @@ export default function visitElement ( generator, block, state, node ) {
return visitComponent( generator, block, state, node );
}
const name = block.getUniqueName( node.name );
const childState = Object.assign( {}, state, {
isTopLevel: false,
parentNode: name,
parentNodeName: node.name,
namespace: node.name === 'svg' ? 'http://www.w3.org/2000/svg' : state.namespace,
allUsedContexts: []
});
const childState = node._state;
const name = childState.parentNode;
block.builders.create.addLine( `var ${name} = ${getRenderStatement( generator, childState.namespace, node.name )};` );
block.mount( name, state.parentNode );

@ -34,12 +34,8 @@ function getBranches ( generator, block, state, node ) {
}
function visitChildren ( generator, block, state, node ) {
const childState = Object.assign( {}, state, {
parentNode: null
});
node.children.forEach( child => {
visit( generator, node._block, childState, child );
visit( generator, node._block, node._state, child );
});
}
@ -76,7 +72,7 @@ function simple ( generator, block, state, node, branch, dynamic, { name, anchor
if ( isToplevel ) {
block.builders.mount.addLine( `if ( ${name} ) ${name}.mount( ${block.target}, ${anchor} );` );
} else {
block.builders.create.addLine( `if ( ${name} ) ${name}.mount( ${state.parentNode}, ${anchor} );` );
block.builders.create.addLine( `if ( ${name} ) ${name}.mount( ${state.parentNode}, null );` );
}
if ( dynamic ) {
@ -128,7 +124,7 @@ function compound ( generator, block, state, node, branches, dynamic, { name, an
if ( isToplevel ) {
block.builders.mount.addLine( `if ( ${name} ) ${name}.mount( ${block.target}, ${anchor} );` );
} else {
block.builders.create.addLine( `if ( ${name} ) ${name}.mount( ${state.parentNode}, ${anchor} );` );
block.builders.create.addLine( `if ( ${name} ) ${name}.mount( ${state.parentNode}, null );` );
}
if ( dynamic ) {

@ -1,7 +1,7 @@
import deindent from '../../../utils/deindent.js';
export default function visitMustacheTag ( generator, block, state, node ) {
const name = block.getUniqueName( 'text' );
const name = node._state.name;
const value = block.getUniqueName( `${name}_value` );
const { snippet } = block.contextualise( node.expression );

@ -1,9 +1,9 @@
import deindent from '../../../utils/deindent.js';
export default function visitRawMustacheTag ( generator, block, state, node ) {
const name = block.getUniqueName( 'raw' );
const name = node._state.basename;
const before = node._state.name;
const value = block.getUniqueName( `${name}_value` );
const before = block.getUniqueName( `${name}_before` );
const after = block.getUniqueName( `${name}_after` );
const { snippet } = block.contextualise( node.expression );

@ -1,23 +1,6 @@
// Whitespace inside one of these elements will not result in
// a whitespace node being created in any circumstances. (This
// list is almost certainly very incomplete)
const elementsWithoutText = new Set([
'audio',
'datalist',
'dl',
'ol',
'optgroup',
'select',
'ul',
'video'
]);
export default function visitText ( generator, block, state, node ) {
if ( !/\S/.test( node.data ) ) {
if ( state.namespace ) return;
if ( elementsWithoutText.has( state.parentNodeName) ) return;
}
const name = block.getUniqueName( `text` );
block.addElement( name, `${generator.helper( 'createText' )}( ${JSON.stringify( node.data )} )`, state.parentNode, false );
export default function visitText ( generator, block, state, node ) {
if ( !node._state.shouldCreate ) return;
block.addElement( node._state.name, `${generator.helper( 'createText' )}( ${JSON.stringify( node.data )} )`, state.parentNode, false );
}

@ -0,0 +1,244 @@
import { appendNode, assign, createComment, createElement, createText, detachNode, dispatchObservers, insertNode, proto } from "svelte/shared.js";
function create_main_fragment ( state, component ) {
var div = createElement( 'div' );
var if_block_anchor = createComment();
appendNode( if_block_anchor, div );
var if_block = state.a && create_if_block( state, component );
if ( if_block ) if_block.mount( div, null );
appendNode( createText( "\n\n\t" ), div );
var p = createElement( 'p' );
appendNode( p, div );
appendNode( createText( "this can be used as an anchor" ), p );
appendNode( createText( "\n\n\t" ), div );
var if_block_1 = state.b && create_if_block_1( state, component );
if ( if_block_1 ) if_block_1.mount( div, null );
var if_block_1_anchor = createComment();
appendNode( if_block_1_anchor, div );
appendNode( createText( "\n\n\t" ), div );
var if_block_2 = state.c && create_if_block_2( state, component );
if ( if_block_2 ) if_block_2.mount( div, null );
appendNode( createText( "\n\n\t" ), div );
var p_1 = createElement( 'p' );
appendNode( p_1, div );
appendNode( createText( "so can this" ), p_1 );
appendNode( createText( "\n\n\t" ), div );
var if_block_3 = state.d && create_if_block_3( state, component );
if ( if_block_3 ) if_block_3.mount( div, null );
appendNode( createText( "\n\n\t" ), div );
var text_8 = createText( "\n\n" );
var if_block_4_anchor = createComment();
var if_block_4 = state.e && create_if_block_4( state, component );
return {
mount: function ( target, anchor ) {
insertNode( div, target, anchor );
insertNode( text_8, target, anchor );
insertNode( if_block_4_anchor, target, anchor );
if ( if_block_4 ) if_block_4.mount( target, if_block_4_anchor );
},
update: function ( changed, state ) {
if ( state.a ) {
if ( !if_block ) {
if_block = create_if_block( state, component );
if_block.mount( if_block_anchor.parentNode, p );
}
} else if ( if_block ) {
if_block.destroy( true );
if_block = null;
}
if ( state.b ) {
if ( !if_block_1 ) {
if_block_1 = create_if_block_1( state, component );
if_block_1.mount( if_block_1_anchor.parentNode, if_block_1_anchor );
}
} else if ( if_block_1 ) {
if_block_1.destroy( true );
if_block_1 = null;
}
if ( state.c ) {
if ( !if_block_2 ) {
if_block_2 = create_if_block_2( state, component );
if_block_2.mount( if_block_2_anchor.parentNode, p_2 );
}
} else if ( if_block_2 ) {
if_block_2.destroy( true );
if_block_2 = null;
}
if ( state.d ) {
if ( !if_block_3 ) {
if_block_3 = create_if_block_3( state, component );
if_block_3.mount( if_block_3_anchor.parentNode, null );
}
} else if ( if_block_3 ) {
if_block_3.destroy( true );
if_block_3 = null;
}
if ( state.e ) {
if ( !if_block_4 ) {
if_block_4 = create_if_block_4( state, component );
if_block_4.mount( if_block_4_anchor.parentNode, if_block_4_anchor );
}
} else if ( if_block_4 ) {
if_block_4.destroy( true );
if_block_4 = null;
}
},
destroy: function ( detach ) {
if ( if_block ) if_block.destroy( false );
if ( if_block_1 ) if_block_1.destroy( false );
if ( if_block_2 ) if_block_2.destroy( false );
if ( if_block_3 ) if_block_3.destroy( false );
if ( if_block_4 ) if_block_4.destroy( detach );
if ( detach ) {
detachNode( div );
detachNode( text_8 );
detachNode( if_block_4_anchor );
}
}
};
}
function create_if_block ( state, component ) {
var p = createElement( 'p' );
appendNode( createText( "a" ), p );
return {
mount: function ( target, anchor ) {
insertNode( p, target, anchor );
},
destroy: function ( detach ) {
if ( detach ) {
detachNode( p );
}
}
};
}
function create_if_block_1 ( state, component ) {
var p = createElement( 'p' );
appendNode( createText( "b" ), p );
return {
mount: function ( target, anchor ) {
insertNode( p, target, anchor );
},
destroy: function ( detach ) {
if ( detach ) {
detachNode( p );
}
}
};
}
function create_if_block_2 ( state, component ) {
var p = createElement( 'p' );
appendNode( createText( "c" ), p );
return {
mount: function ( target, anchor ) {
insertNode( p, target, anchor );
},
destroy: function ( detach ) {
if ( detach ) {
detachNode( p );
}
}
};
}
function create_if_block_3 ( state, component ) {
var p = createElement( 'p' );
appendNode( createText( "d" ), p );
return {
mount: function ( target, anchor ) {
insertNode( p, target, anchor );
},
destroy: function ( detach ) {
if ( detach ) {
detachNode( p );
}
}
};
}
function create_if_block_4 ( state, component ) {
var p = createElement( 'p' );
appendNode( createText( "e" ), 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,27 @@
<div>
{{#if a}}
<p>a</p>
{{/if}}
<p>this can be used as an anchor</p>
{{#if b}}
<p>b</p>
{{/if}}
{{#if c}}
<p>c</p>
{{/if}}
<p>so can this</p>
{{#if d}}
<p>d</p>
{{/if}}
<!-- d can use 'null' as its anchor -->
</div>
{{#if e}}
<p>e</p>
{{/if}}
Loading…
Cancel
Save