simplify SSR codegen

pull/559/head
Rich-Harris 8 years ago
parent 3efb5ab993
commit 43091431d1

@ -19,9 +19,7 @@ class DomGenerator extends Generator {
this.readonly = new Set();
// initial values for e.g. window.innerWidth, if there's a <:Window> meta tag
this.builders = {
metaBindings: new CodeBuilder()
};
this.metaBindings = [];
}
helper ( name ) {
@ -91,11 +89,13 @@ export default function dom ( parsed, source, options ) {
if ( typeof newState !== 'object' ) {
throw new Error( 'Component .set was called without an object of data key-values to update.' );
}
`);
Array.from( generator.readonly ).forEach( prop => {
builders._set.addLine( `if ( '${prop}' in newState && !this._updatingReadonlyProperty ) throw new Error( "Cannot set read-only property '${prop}'" );` );
});
${
Array.from( generator.readonly ).map( prop =>
`if ( '${prop}' in newState && !this._updatingReadonlyProperty ) throw new Error( "Cannot set read-only property '${prop}'" );`
)
}
`);
}
// TODO is the `if ( this._fragment )` condition necessary?
@ -170,7 +170,7 @@ export default function dom ( parsed, source, options ) {
${options.dev && `if ( !options.target && !options._root ) throw new Error( "'target' is a required option" );`}
${generator.usesRefs && `this.refs = {};`}
this._state = ${templateProperties.data ? `${generator.helper( 'assign' )}( ${generator.alias( 'template' )}.data(), options.data )` : `options.data || {}`};
${generator.builders.metaBindings}
${generator.metaBindings}
${computations.length && `${generator.alias( 'recompute' )}( this._state, this._state, {}, true );`}
${options.dev && Array.from( generator.expectedProperties ).map( prop => `if ( !( '${prop}' in this._state ) ) console.warn( "Component was created without expected data property '${prop}'" );`)}
${generator.bindingGroups.length && `this._bindingGroups = [ ${Array( generator.bindingGroups.length ).fill( '[]' ).join( ', ' )} ];`}

@ -84,7 +84,7 @@ export default function visitWindow ( generator, block, node ) {
events[ associatedEvent ].push( `${attribute.value.name}: this.${attribute.name}` );
// add initial value
generator.builders.metaBindings.addLine(
generator.metaBindings.push(
`this._state.${attribute.value.name} = window.${attribute.name};`
);
}
@ -166,7 +166,7 @@ export default function visitWindow ( generator, block, node ) {
` );
// add initial value
generator.builders.metaBindings.addLine(
generator.metaBindings.push(
`this._state.${bindings.online} = navigator.onLine;`
);

@ -1,5 +1,4 @@
import deindent from '../../utils/deindent.js';
import CodeBuilder from '../../utils/CodeBuilder.js';
import Generator from '../Generator.js';
import Block from './Block.js';
import visit from './visit.js';
@ -24,13 +23,6 @@ export default function ssr ( parsed, source, options ) {
const { computations, hasJs, templateProperties } = generator.parseJs( true );
const builders = {
main: new CodeBuilder(),
bindings: new CodeBuilder(),
render: new CodeBuilder(),
renderCss: new CodeBuilder()
};
// create main render() function
const mainBlock = new Block({
generator,
@ -43,84 +35,9 @@ export default function ssr ( parsed, source, options ) {
visit( generator, mainBlock, node );
});
builders.render.addLine(
templateProperties.data ? `state = Object.assign( ${generator.alias( 'template' )}.data(), state || {} );` : `state = state || {};`
);
computations.forEach( ({ key, deps }) => {
builders.render.addLine(
`state.${key} = ${generator.alias( 'template' )}.computed.${key}( ${deps.map( dep => `state.${dep}` ).join( ', ' )} );`
);
});
if ( generator.bindings.length ) {
const bindings = generator.bindings.join( '\n\n' );
builders.render.addBlock( deindent`
var settled = false;
var tmp;
while ( !settled ) {
settled = true;
${bindings}
}
` );
}
builders.render.addBlock(
`return \`${generator.renderCode}\`;`
);
// create renderCss() function
builders.renderCss.addBlock(
`var components = [];`
);
if ( generator.css ) {
builders.renderCss.addBlock( deindent`
components.push({
filename: ${name}.filename,
css: ${JSON.stringify( generator.css )},
map: null // TODO
});
` );
}
if ( templateProperties.components ) {
builders.renderCss.addBlock( deindent`
var seen = {};
function addComponent ( component ) {
var result = component.renderCss();
result.components.forEach( x => {
if ( seen[ x.filename ] ) return;
seen[ x.filename ] = true;
components.push( x );
});
}
` );
templateProperties.components.value.properties.forEach( prop => {
const { name } = prop.key;
const expression = generator.importedComponents.get( name ) || `${generator.alias( 'template' )}.components.${name}`;
builders.renderCss.addLine( `addComponent( ${expression} );` );
});
}
builders.renderCss.addBlock( deindent`
return {
css: components.map( x => x.css ).join( '\\n' ),
map: null,
components
};
` );
const result = deindent`
${hasJs && `[✂${parsed.js.content.start}-${parsed.js.content.end}✂]`}
if ( hasJs ) {
builders.main.addBlock( `[✂${parsed.js.content.start}-${parsed.js.content.end}✂]` );
}
builders.main.addBlock( deindent`
var ${name} = {};
${name}.filename = ${JSON.stringify( options.filename )};
@ -130,11 +47,63 @@ export default function ssr ( parsed, source, options ) {
};
${name}.render = function ( state, options ) {
${builders.render}
${templateProperties.data ? `state = Object.assign( ${generator.alias( 'template' )}.data(), state || {} );` : `state = state || {};`}
${computations.map( ({ key, deps }) =>
`state.${key} = ${generator.alias( 'template' )}.computed.${key}( ${deps.map( dep => `state.${dep}` ).join( ', ' )} );`
)}
${generator.bindings.length && deindent`
var settled = false;
var tmp;
while ( !settled ) {
settled = true;
${generator.bindings.join( '\n\n' )}
}
`}
return \`${generator.renderCode}\`;
};
${name}.renderCss = function () {
${builders.renderCss}
var components = [];
${generator.css && deindent`
components.push({
filename: ${name}.filename,
css: ${JSON.stringify( generator.css )},
map: null // TODO
});
`}
${templateProperties.components && deindent`
var seen = {};
function addComponent ( component ) {
var result = component.renderCss();
result.components.forEach( x => {
if ( seen[ x.filename ] ) return;
seen[ x.filename ] = true;
components.push( x );
});
}
${
templateProperties.components.value.properties.map( prop => {
const { name } = prop.key;
const expression = generator.importedComponents.get( name ) || `${generator.alias( 'template' )}.components.${name}`;
return `addComponent( ${expression} );`;
})
}
`}
return {
css: components.map( x => x.css ).join( '\\n' ),
map: null,
components
};
};
var escaped = {
@ -148,9 +117,7 @@ export default function ssr ( parsed, source, options ) {
function __escape ( html ) {
return String( html ).replace( /["'&<>]/g, match => escaped[ match ] );
}
` );
const result = builders.main.toString();
`;
return generator.generate( result, options, { name, format } );
}

@ -29,7 +29,6 @@ function create_main_fragment ( state, component ) {
function SvelteComponent ( options ) {
options = options || {};
this._state = options.data || {};
recompute( this._state, this._state, {}, true );
this._observers = {

@ -1,3 +1,3 @@
<p>before</p>
<p>after</p>
<p>after</p>

@ -1,4 +1,4 @@
<div><p>foo: lol</p>
<p>baz: 42 (number)</p>
<p>qux: this is a piece of string</p>
<p>quux: core</p></div>
<p>baz: 42 (number)</p>
<p>qux: this is a piece of string</p>
<p>quux: core</p></div>

@ -1,2 +1,2 @@
<div><p>foo: bar</p>
<p>baz: 42 (number)</p></div>
<p>baz: 42 (number)</p></div>

@ -1,2 +1,2 @@
<p>1 + 2 = 3</p>
<p>3 * 3 = 9</p>
<p>3 * 3 = 9</p>

@ -1,2 +1,2 @@
<div>i got 99 problems</div>
<div>the answer is 42</div>
<div>the answer is 42</div>

@ -1,5 +1,5 @@
<div svelte-4188175681>red</div>
<div svelte-146600313>green: foo</div>
<div svelte-1506185237>blue: foo</div>
<div svelte-146600313>green: bar</div>
<div svelte-1506185237>blue: bar</div>
<div svelte-146600313>green: foo</div>
<div svelte-1506185237>blue: foo</div>
<div svelte-146600313>green: bar</div>
<div svelte-1506185237>blue: bar</div>
Loading…
Cancel
Save