@ -6,6 +6,9 @@ import Renderer from './Renderer';
import { INode as TemplateNode } from '../nodes/interfaces' ; // TODO
import Text from '../nodes/Text' ;
import { LabeledStatement , Statement , Node } from 'estree' ;
import { walk } from 'estree-walker' ;
import { extract_names } from 'periscopic' ;
import { invalidate } from '../render_dom/invalidate' ;
export default function ssr (
component : Component ,
@ -38,24 +41,94 @@ export default function ssr(
const slots = uses_slots ? b ` let $ $ slots = @compute_slots(#slots); ` : null ;
const reactive_stores = component . vars . filter ( variable = > variable . name [ 0 ] === '$' && variable . name [ 1 ] !== '$' ) ;
const reactive_store_values = reactive_stores
const reactive_store_subscriptions = reactive_stores
. filter ( store = > {
const variable = component . var_lookup . get ( store . name . slice ( 1 ) ) ;
return ! variable || variable . hoistable ;
} )
. map ( ( { name } ) = > {
const store_name = name . slice ( 1 ) ;
return b `
$ { component . compile_options . dev && b ` @validate_store( ${ store_name } , ' ${ store_name } '); ` }
$ { ` $ $ unsubscribe_ ${ store_name } ` } = @subscribe ( $ { store_name } , # value = > $ { name } = # value )
$ { store_name } . subscribe ( $ $value = > $ { name } = $ $value ) ;
` ;
} ) ;
const reactive_store_unsubscriptions = reactive_stores . map (
( { name } ) = > b ` ${ ` $ $ unsubscribe_ ${ name . slice ( 1 ) } ` } () `
) ;
const reactive_store_declarations = reactive_stores
. map ( ( { name } ) = > {
const store_name = name . slice ( 1 ) ;
const store = component . var_lookup . get ( store_name ) ;
if ( store && store . hoistable ) return null ;
const assignment = b ` ${ name } = @get_store_value( ${ store_name } ); ` ;
if ( store && store . reassigned ) {
const unsubscribe = ` $ $ unsubscribe_ ${ store_name } ` ;
const subscribe = ` $ $ subscribe_ ${ store_name } ` ;
return component . compile_options . dev
? b ` @validate_store( ${ store_name } , ' ${ store_name } '); ${ assignment } `
: assignment ;
} )
. filter ( Boolean ) ;
return b ` let ${ name } , ${ unsubscribe } = @noop, ${ subscribe } = () => ( ${ unsubscribe } (), ${ unsubscribe } = @subscribe( ${ store_name } , $ $ value => ${ name } = $ $ value), ${ store_name } ) ` ;
}
return b ` let ${ name } , ${ ` $ $ unsubscribe_ ${ store_name } ` } ; ` ;
} ) ;
component . rewrite_props ( ( { name } ) = > {
// instrument get/set store value
if ( component . ast . instance ) {
let scope = component . instance_scope ;
const map = component . instance_scope_map ;
walk ( component . ast . instance . content , {
enter ( node : Node ) {
if ( map . has ( node ) ) {
scope = map . get ( node ) ;
}
} ,
leave ( node : Node ) {
if ( map . has ( node ) ) {
scope = scope . parent ;
}
if ( node . type === 'AssignmentExpression' || node . type === 'UpdateExpression' ) {
const assignee = node . type === 'AssignmentExpression' ? node.left : node.argument ;
const names = new Set ( extract_names ( assignee ) ) ;
const to_invalidate = new Set < string > ( ) ;
for ( const name of names ) {
const variable = component . var_lookup . get ( name ) ;
if ( variable &&
! variable . hoistable &&
! variable . global &&
! variable . module &&
(
variable . subscribable || variable . name [ 0 ] === '$'
) ) {
to_invalidate . add ( variable . name ) ;
}
}
if ( to_invalidate . size ) {
this . replace (
invalidate (
{ component } as any ,
scope ,
node ,
to_invalidate ,
true
)
) ;
}
}
}
} ) ;
}
component . rewrite_props ( ( { name , reassigned } ) = > {
const value = ` $ ${ name } ` ;
let insert = b ` ${ value } = @get_store_value( ${ name } ) ` ;
let insert = reassigned
? b ` ${ ` $ $ subscribe_ ${ name } ` } () `
: b ` ${ ` $ $ unsubscribe_ ${ name } ` } = @subscribe( ${ name } , #value => $ ${ value } = #value) ` ;
if ( component . compile_options . dev ) {
insert = b ` @validate_store( ${ name } , ' ${ name } '); ${ insert } ` ;
}
@ -99,35 +172,28 @@ export default function ssr(
do {
$ $settled = true ;
$ { reactive_store_values }
$ { reactive_declarations }
$ $rendered = $ { literal } ;
} while ( ! $ $settled ) ;
$ { reactive_store_unsubscriptions }
return $ $rendered ;
`
: b `
$ { reactive_store_values }
$ { reactive_declarations }
$ { reactive_store_unsubscriptions }
return $ { literal } ; ` ;
const blocks = [
. . . injected . map ( name = > b ` let ${ name } ; ` ) ,
rest ,
slots ,
. . . reactive_stores . map ( ( { name } ) = > {
const store_name = name . slice ( 1 ) ;
const store = component . var_lookup . get ( store_name ) ;
if ( store && store . hoistable ) {
return b ` let ${ name } = @get_store_value( ${ store_name } ); ` ;
}
return b ` let ${ name } ; ` ;
} ) ,
. . . reactive_store_declarations ,
. . . reactive_store_subscriptions ,
instance_javascript ,
. . . parent_bindings ,
css . code && b ` $ $ result.css.add(#css); ` ,