Skip to content

Commit

Permalink
Merge pull request #281 from sveltejs/gh-275
Browse files Browse the repository at this point in the history
two-way component binding in SSR
  • Loading branch information
Rich-Harris authored Feb 8, 2017
2 parents d5bb267 + 23265d5 commit 744a864
Show file tree
Hide file tree
Showing 13 changed files with 118 additions and 6 deletions.
2 changes: 1 addition & 1 deletion rollup/rollup.config.ssr.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default {
nodeResolve({ jsnext: true, module: true }),
commonjs()
],
external: [ path.resolve( 'src/index.js' ), 'fs', 'magic-string' ],
external: [ path.resolve( 'src/index.js' ), 'fs', 'path', 'magic-string' ],
paths: {
[ path.resolve( 'src/index.js' ) ]: '../compiler/svelte.js'
},
Expand Down
40 changes: 39 additions & 1 deletion src/generators/server-side-rendering/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,26 @@ import Generator from '../Generator.js';
class SsrGenerator extends Generator {
constructor ( parsed, source, names, visitors ) {
super( parsed, source, names, visitors );
this.bindings = [];
this.renderCode = '';
}

addBinding ( binding, name ) {
const conditions = [ `!( '${binding.name}' in root )`].concat( // TODO handle contextual bindings...
this.current.conditions.map( c => `(${c})` )
);

this.bindings.push( deindent`
if ( ${conditions.join( '&&' )} ) {
tmp = template.components.${name}.data();
if ( '${binding.value}' in tmp ) {
root.${binding.name} = tmp.${binding.value};
settled = false;
}
}
` );
}

append ( code ) {
this.renderCode += code;
}
Expand All @@ -25,14 +42,16 @@ export default function ssr ( parsed, source, options, names ) {

const builders = {
main: new CodeBuilder(),
bindings: new CodeBuilder(),
render: new CodeBuilder(),
renderCss: new CodeBuilder()
};

// create main render() function
generator.push({
contexts: {},
indexes: {}
indexes: {},
conditions: []
});

parsed.html.children.forEach( node => generator.visit( node ) );
Expand All @@ -47,6 +66,21 @@ export default function ssr ( parsed, source, options, names ) {
);
});

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}\`;`
);
Expand Down Expand Up @@ -102,6 +136,10 @@ export default function ssr ( parsed, source, options, names ) {
${name}.filename = ${JSON.stringify( options.filename )};
${name}.data = function () {
return ${templateProperties.data ? `template.data()` : `{}`};
};
${name}.render = function ( root, options ) {
${builders.render}
};
Expand Down
20 changes: 16 additions & 4 deletions src/generators/server-side-rendering/visitors/Component.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,19 @@ export default {
}
}

const props = node.attributes
.map( attribute => {
if ( attribute.type !== 'Attribute' ) return;
const attributes = [];
const bindings = [];

node.attributes.forEach( attribute => {
if ( attribute.type === 'Attribute' ) {
attributes.push( attribute );
} else if ( attribute.type === 'Binding' ) {
bindings.push( attribute );
}
});

const props = attributes
.map( attribute => {
let value;

if ( attribute.value === true ) {
Expand All @@ -32,9 +41,12 @@ export default {

return `${attribute.name}: ${value}`;
})
.filter( Boolean )
.join( ', ' );

bindings.forEach( binding => {
generator.addBinding( binding, node.name );
});

let open = `\${template.components.${node.name}.render({${props}}`;

if ( node.children.length ) {
Expand Down
7 changes: 7 additions & 0 deletions src/generators/server-side-rendering/visitors/IfBlock.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
export default {
enter ( generator, node ) {
const { snippet } = generator.contextualise( node.expression );

generator.append( '${ ' + snippet + ' ? `' );

generator.push({
conditions: generator.current.conditions.concat( snippet )
});
},

leave ( generator, node ) {
generator.append( '` : `' );
if ( node.else ) node.else.children.forEach( child => generator.visit( child ) );
generator.append( '` }' );

generator.pop();
}
};
7 changes: 7 additions & 0 deletions src/server-side-rendering/register.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import * as fs from 'fs';
import * as path from 'path';
import { compile } from '../index.js';

function capitalise ( name ) {
return name[0].toUpperCase() + name.slice( 1 );
}

require.extensions[ '.html' ] = function ( module, filename ) {
const { code } = compile( fs.readFileSync( filename, 'utf-8' ), {
filename,
name: capitalise( path.basename( filename ).replace( /\.html$/, '' ) ),
generate: 'ssr'
});

return module._compile( code, filename );
};
11 changes: 11 additions & 0 deletions test/server-side-rendering/component-binding-renamed/Foo.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
:foo:

<script>
export default {
data () {
return {
x: 1
};
}
};
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1:foo:1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1:foo:1
11 changes: 11 additions & 0 deletions test/server-side-rendering/component-binding-renamed/main.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{{y}}<Foo bind:y='x'/>{{y}}

<script>
import Foo from './Foo.html';

export default {
components: {
Foo
}
};
</script>
11 changes: 11 additions & 0 deletions test/server-side-rendering/component-binding/Foo.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
:foo:

<script>
export default {
data () {
return {
x: 1
};
}
};
</script>
1 change: 1 addition & 0 deletions test/server-side-rendering/component-binding/_actual.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1:foo:1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1:foo:1
11 changes: 11 additions & 0 deletions test/server-side-rendering/component-binding/main.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{{x}}<Foo bind:x/>{{x}}

<script>
import Foo from './Foo.html';

export default {
components: {
Foo
}
};
</script>

0 comments on commit 744a864

Please sign in to comment.