all tests passing once more. now the real work begins

pull/453/head
Rich-Harris 8 years ago
parent cdb8b9d01d
commit d93a3698fb

@ -62,14 +62,14 @@ export default class Generator {
return alias;
}
contextualise ( expression, isEventHandler ) {
contextualise ( fragment, expression, isEventHandler ) {
this.addSourcemapLocations( expression );
const usedContexts = [];
const dependencies = [];
const { code, helpers } = this;
const { contextDependencies, contexts, indexes } = this.current;
const { contextDependencies, contexts, indexes } = fragment;
let scope = annotateWithScopes( expression );

@ -17,7 +17,7 @@ export default function visitEachBlock ( generator, fragment, node ) {
generator.addSourcemapLocations( node.expression );
const { dependencies, snippet } = generator.contextualise( node.expression );
const { dependencies, snippet } = generator.contextualise( fragment, node.expression );
const anchor = fragment.getUniqueName( `${name}_anchor` );
fragment.createAnchor( anchor );
@ -221,17 +221,19 @@ export default function visitEachBlock ( generator, fragment, node ) {
generator.pop();
if ( node.else ) {
const childFragment = this.current.child({
const childFragment = fragment.child({
type: 'block',
name: renderElse,
target: 'target',
localElementDepth: 0,
builders: getBuilders(),
getUniqueName: this.getUniqueNameMaker( this.current.params )
getUniqueName: generator.getUniqueNameMaker( fragment.params )
});
node.else.children.forEach( child => {
visit( generator, childFragment, child );
});
generator.addRenderer( childFragment );
}
}

@ -7,7 +7,7 @@ function getConditionsAndBlocks ( generator, fragment, node, _name, i = 0 ) {
const name = generator.getUniqueName( `${_name}_${i}` );
const conditionsAndBlocks = [{
condition: generator.contextualise( node.expression ).snippet,
condition: generator.contextualise( fragment, node.expression ).snippet,
block: name
}];
@ -35,7 +35,7 @@ function getConditionsAndBlocks ( generator, fragment, node, _name, i = 0 ) {
function generateBlock ( generator, fragment, node, name, type ) {
const childFragment = fragment.child({
type,
isIfBlock: true,
isIfBlock: true,
name,
target: 'target',
builders: getBuilders(),

@ -4,7 +4,7 @@ import findBlock from '../utils/findBlock.js';
export default function visitMustacheTag ( generator, fragment, node ) {
const name = fragment.getUniqueName( 'text' );
const { snippet } = generator.contextualise( node.expression );
const { snippet } = generator.contextualise( fragment, node.expression );
fragment.builders.create.addLine( `var last_${name} = ${snippet};` );
fragment.addElement( name, `${generator.helper( 'createText' )}( last_${name} )`, true );

@ -4,7 +4,7 @@ import findBlock from '../utils/findBlock.js';
export default function visitRawMustacheTag ( generator, fragment, node ) {
const name = fragment.getUniqueName( 'raw' );
const { snippet } = generator.contextualise( node.expression );
const { snippet } = generator.contextualise( fragment, node.expression );
// we would have used comments here, but the `insertAdjacentHTML` api only
// exists for `Element`s.

@ -37,7 +37,7 @@ export default function addComponentAttributes ( generator, fragment, node, loca
else {
// simple dynamic attributes
const { dependencies, string } = generator.contextualise( value.expression );
const { dependencies, string } = generator.contextualise( fragment, value.expression );
// TODO only update attributes that have changed
local.dynamicAttributes.push({
@ -57,7 +57,7 @@ export default function addComponentAttributes ( generator, fragment, node, loca
if ( chunk.type === 'Text' ) {
return JSON.stringify( chunk.data );
} else {
const { dependencies, string } = generator.contextualise( chunk.expression );
const { dependencies, string } = generator.contextualise( fragment, chunk.expression );
dependencies.forEach( dependency => {
if ( !~allDependencies.indexOf( dependency ) ) allDependencies.push( dependency );
});
@ -82,7 +82,7 @@ export default function addComponentAttributes ( generator, fragment, node, loca
const usedContexts = [];
attribute.expression.arguments.forEach( arg => {
const { contexts } = generator.contextualise( arg, true );
const { contexts } = generator.contextualise( fragment, arg, true );
contexts.forEach( context => {
if ( !~usedContexts.indexOf( context ) ) usedContexts.push( context );

@ -4,7 +4,7 @@ import getSetter from './binding/getSetter.js';
export default function addComponentBinding ( generator, node, attribute, fragment, local ) {
const { name, keypath } = flattenReference( attribute.value );
const { snippet, contexts, dependencies } = generator.contextualise( attribute.value );
const { snippet, contexts, dependencies } = generator.contextualise( fragment, attribute.value );
if ( dependencies.length > 1 ) throw new Error( 'An unexpected situation arose. Please raise an issue at https://github.com/sveltejs/svelte/issues — thanks!' );

@ -93,7 +93,7 @@ export default function addElementAttributes ( generator, fragment, node, local
dynamic = true;
// dynamic but potentially non-string attributes
const { snippet } = generator.contextualise( value.expression );
const { snippet } = generator.contextualise( fragment, value.expression );
const last = `last_${local.name}_${name.replace( /-/g, '_')}`;
local.create.addLine( `var ${last} = ${snippet};` );
@ -128,7 +128,7 @@ export default function addElementAttributes ( generator, fragment, node, local
if ( chunk.type === 'Text' ) {
return JSON.stringify( chunk.data );
} else {
const { snippet } = generator.contextualise( chunk.expression );
const { snippet } = generator.contextualise( fragment, chunk.expression );
return `( ${snippet} )`;
}
}).join( ' + ' )
@ -165,7 +165,7 @@ export default function addElementAttributes ( generator, fragment, node, local
const usedContexts = [];
attribute.expression.arguments.forEach( arg => {
const { contexts } = generator.contextualise( arg, true );
const { contexts } = generator.contextualise( fragment, arg, true );
contexts.forEach( context => {
if ( !~usedContexts.indexOf( context ) ) usedContexts.push( context );

@ -5,7 +5,7 @@ import getStaticAttributeValue from './binding/getStaticAttributeValue.js';
export default function addElementBinding ( generator, node, attribute, fragment, local ) {
const { name, keypath } = flattenReference( attribute.value );
const { snippet, contexts, dependencies } = generator.contextualise( attribute.value );
const { snippet, contexts, dependencies } = generator.contextualise( fragment, attribute.value );
if ( dependencies.length > 1 ) throw new Error( 'An unexpected situation arose. Please raise an issue at https://github.com/sveltejs/svelte/issues — thanks!' );

@ -0,0 +1,30 @@
import deindent from '../../utils/deindent.js';
import flattenReference from '../../utils/flattenReference.js';
export default class Fragment {
constructor ( options ) {
Object.assign( this, options );
}
addBinding ( binding, name ) {
const conditions = [ `!( '${binding.name}' in root )`].concat( // TODO handle contextual bindings...
this.conditions.map( c => `(${c})` )
);
const { keypath } = flattenReference( binding.value );
this.generator.bindings.push( deindent`
if ( ${conditions.join( '&&' )} ) {
tmp = ${name}.data();
if ( '${keypath}' in tmp ) {
root.${binding.name} = tmp.${keypath};
settled = false;
}
}
` );
}
child ( options ) {
return new Fragment( Object.assign( {}, this, options, { parent: this } ) );
}
}

@ -2,6 +2,7 @@ import deindent from '../../utils/deindent.js';
import CodeBuilder from '../../utils/CodeBuilder.js';
import flattenReference from '../../utils/flattenReference.js';
import Generator from '../Generator.js';
import Fragment from './Fragment.js';
import visit from './visit.js';
class SsrGenerator extends Generator {
@ -50,14 +51,15 @@ export default function ssr ( parsed, source, options ) {
};
// create main render() function
generator.push({
const mainFragment = new Fragment({
generator,
contexts: new Map(),
indexes: new Map(),
conditions: []
});
parsed.html.children.forEach( node => {
visit( node, generator );
visit( generator, mainFragment, node );
});
builders.render.addLine(

@ -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 );
}

@ -1,11 +1,11 @@
import flattenReference from '../../../utils/flattenReference.js';
import visit from '../visit.js';
export default function visitComponent ( generator, node ) {
export default function visitComponent ( generator, fragment, node ) {
function stringify ( chunk ) {
if ( chunk.type === 'Text' ) return chunk.data;
if ( chunk.type === 'MustacheTag' ) {
const { snippet } = generator.contextualise( chunk.expression );
const { snippet } = generator.contextualise( fragment, chunk.expression );
return '${__escape( ' + snippet + ')}';
}
}
@ -34,7 +34,7 @@ export default function visitComponent ( generator, node ) {
if ( chunk.type === 'Text' ) {
value = isNaN( chunk.data ) ? JSON.stringify( chunk.data ) : chunk.data;
} else {
const { snippet } = generator.contextualise( chunk.expression );
const { snippet } = generator.contextualise( fragment, chunk.expression );
value = snippet;
}
} else {
@ -45,7 +45,7 @@ export default function visitComponent ( generator, node ) {
})
.concat( bindings.map( binding => {
const { name, keypath } = flattenReference( binding.value );
const value = generator.current.contexts.has( name ) ? keypath : `root.${keypath}`;
const value = fragment.contexts.has( name ) ? keypath : `root.${keypath}`;
return `${binding.name}: ${value}`;
}))
.join( ', ' );
@ -53,7 +53,7 @@ export default function visitComponent ( generator, node ) {
const expression = node.name === ':Self' ? generator.name : generator.importedComponents.get( node.name ) || `${generator.alias( 'template' )}.components.${node.name}`;
bindings.forEach( binding => {
generator.addBinding( binding, expression );
fragment.addBinding( binding, expression );
});
let open = `\${${expression}.render({${props}}`;
@ -67,7 +67,7 @@ export default function visitComponent ( generator, node ) {
generator.elementDepth += 1;
node.children.forEach( child => {
visit( child, generator );
visit( generator, fragment, child );
});
generator.elementDepth -= 1;

@ -1,34 +1,32 @@
import visit from '../visit.js';
export default function visitEachBlock ( generator, node ) {
const { dependencies, snippet } = generator.contextualise( node.expression );
export default function visitEachBlock ( generator, fragment, node ) {
const { dependencies, snippet } = generator.contextualise( fragment, node.expression );
const open = `\${ ${snippet}.map( ${ node.index ? `( ${node.context}, ${node.index} )` : node.context} => \``;
generator.append( open );
// TODO should this be the generator's job? It's duplicated between
// here and the equivalent DOM compiler visitor
const contexts = new Map( generator.current.contexts );
const contexts = new Map( fragment.contexts );
contexts.set( node.context, node.context );
const indexes = new Map( generator.current.indexes );
const indexes = new Map( fragment.indexes );
if ( node.index ) indexes.set( node.index, node.context );
const contextDependencies = new Map( generator.current.contextDependencies );
const contextDependencies = new Map( fragment.contextDependencies );
contextDependencies.set( node.context, dependencies );
generator.push({
const childFragment = fragment.child({
contexts,
indexes,
contextDependencies
});
node.children.forEach( child => {
visit( child, generator );
visit( generator, childFragment, child );
});
const close = `\` ).join( '' )}`;
generator.append( close );
generator.pop();
}

@ -7,13 +7,13 @@ 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' ) {
visitComponent( generator, node );
visitComponent( generator, fragment, node );
return;
}
@ -30,7 +30,7 @@ export default function visitElement ( generator, node ) {
return chunk.data;
}
const { snippet } = generator.contextualise( chunk.expression );
const { snippet } = generator.contextualise( fragment, chunk.expression );
return '${' + snippet + '}';
}).join( '' ) + `"`;
}
@ -49,7 +49,7 @@ export default function visitElement ( generator, node ) {
generator.elementDepth += 1;
node.children.forEach( child => {
visit( child, generator );
visit( generator, fragment, child );
});
generator.elementDepth -= 1;

@ -1,27 +1,25 @@
import visit from '../visit.js';
export default function visitIfBlock ( generator, node ) {
const { snippet } = generator.contextualise( node.expression );
export default function visitIfBlock ( generator, fragment, node ) {
const { snippet } = generator.contextualise( fragment, node.expression );
generator.append( '${ ' + snippet + ' ? `' );
generator.push({
conditions: generator.current.conditions.concat( snippet )
const childFragment = fragment.child({
conditions: fragment.conditions.concat( snippet )
});
node.children.forEach( child => {
visit( child, generator );
visit( generator, childFragment, child );
});
generator.append( '` : `' );
if ( node.else ) {
node.else.children.forEach( child => {
visit( child, generator );
visit( generator, childFragment, child );
});
}
generator.append( '` }' );
generator.pop();
}

@ -1,4 +1,4 @@
export default function visitMustacheTag ( generator, node ) {
const { snippet } = generator.contextualise( node.expression );
export default function visitMustacheTag ( generator, fragment, node ) {
const { snippet } = generator.contextualise( fragment, node.expression );
generator.append( '${__escape( ' + snippet + ' )}' );
}

@ -1,4 +1,4 @@
export default function visitRawMustacheTag ( generator, node ) {
const { snippet } = generator.contextualise( node.expression );
export default function visitRawMustacheTag ( generator, fragment, node ) {
const { snippet } = generator.contextualise( fragment, node.expression );
generator.append( '${' + snippet + '}' );
}

@ -1,3 +1,3 @@
export default function visitText ( generator, node ) {
export default function visitText ( generator, fragment, node ) {
generator.append( node.data.replace( /\${/g, '\\${' ) );
}

@ -3,15 +3,35 @@ export default {
animals: [ 'alpaca', 'baboon', 'capybara' ],
foo: 'something else'
},
html: 'before\n<p>alpaca</p><p>baboon</p><p>capybara</p>\nafter',
html: `
before
<p>alpaca</p>
<p>baboon</p>
<p>capybara</p>
after
`,
test ( assert, component, target ) {
component.set({ animals: [] });
assert.htmlEqual( target.innerHTML, 'before\n<p>no animals, but rather something else</p>\nafter' );
assert.htmlEqual( target.innerHTML, `
before
<p>no animals, but rather something else</p>
after
` );
component.set({ foo: 'something other' });
assert.htmlEqual( target.innerHTML, 'before\n<p>no animals, but rather something other</p>\nafter' );
assert.htmlEqual( target.innerHTML, `
before
<p>no animals, but rather something other</p>
after
` );
component.set({ animals: ['wombat'] });
assert.htmlEqual( target.innerHTML, 'before\n<p>wombat</p>\nafter' );
assert.htmlEqual( target.innerHTML, `
before
<p>wombat</p>
after
` );
}
};

Loading…
Cancel
Save