pull/573/head
Rich-Harris 8 years ago
parent dc82db609c
commit 4486b93d88

@ -176,7 +176,7 @@ export default class Generator {
}; };
} }
findDependencies ( contextDependencies, indexes, expression ) { findDependencies ( contextDependencies: Map<string, string[]>, indexes: Map<string, string>, expression: Node ) {
if ( expression._dependencies ) return expression._dependencies; if ( expression._dependencies ) return expression._dependencies;
let scope = annotateWithScopes( expression ); let scope = annotateWithScopes( expression );
@ -340,23 +340,23 @@ export default class Generator {
removeNode( this.code, js.content, node ); removeNode( this.code, js.content, node );
imports.push( node ); imports.push( node );
node.specifiers.forEach( specifier => { node.specifiers.forEach( ( specifier: Node ) => {
this.importedNames.add( specifier.local.name ); this.importedNames.add( specifier.local.name );
}); });
} }
} }
const defaultExport = body.find( node => node.type === 'ExportDefaultDeclaration' ); const defaultExport = body.find( ( node: Node ) => node.type === 'ExportDefaultDeclaration' );
if ( defaultExport ) { if ( defaultExport ) {
defaultExport.declaration.properties.forEach( prop => { defaultExport.declaration.properties.forEach( ( prop: Node ) => {
templateProperties[ prop.key.name ] = prop; templateProperties[ prop.key.name ] = prop;
}); });
} }
[ 'helpers', 'events', 'components', 'transitions' ].forEach( key => { [ 'helpers', 'events', 'components', 'transitions' ].forEach( key => {
if ( templateProperties[ key ] ) { if ( templateProperties[ key ] ) {
templateProperties[ key ].value.properties.forEach( prop => { templateProperties[ key ].value.properties.forEach( ( prop: node ) => {
this[ key ].add( prop.key.name ); this[ key ].add( prop.key.name );
}); });
} }
@ -365,11 +365,11 @@ export default class Generator {
if ( templateProperties.computed ) { if ( templateProperties.computed ) {
const dependencies = new Map(); const dependencies = new Map();
templateProperties.computed.value.properties.forEach( prop => { templateProperties.computed.value.properties.forEach( ( prop: Node ) => {
const key = prop.key.name; const key = prop.key.name;
const value = prop.value; const value = prop.value;
const deps = value.params.map( param => param.type === 'AssignmentPattern' ? param.left.name : param.name ); const deps = value.params.map( ( param: Node ) => param.type === 'AssignmentPattern' ? param.left.name : param.name );
dependencies.set( key, deps ); dependencies.set( key, deps );
}); });
@ -387,7 +387,7 @@ export default class Generator {
computations.push({ key, deps }); computations.push({ key, deps });
} }
templateProperties.computed.value.properties.forEach( prop => visit( prop.key.name ) ); templateProperties.computed.value.properties.forEach( ( prop: Node ) => visit( prop.key.name ) );
} }
if ( templateProperties.namespace ) { if ( templateProperties.namespace ) {
@ -399,7 +399,7 @@ export default class Generator {
if ( templateProperties.components ) { if ( templateProperties.components ) {
let hasNonImportedComponent = false; let hasNonImportedComponent = false;
templateProperties.components.value.properties.forEach( property => { templateProperties.components.value.properties.forEach( ( property: Node ) => {
const key = property.key.name; const key = property.key.name;
const value = source.slice( property.value.start, property.value.end ); const value = source.slice( property.value.start, property.value.end );
if ( this.importedNames.has( value ) ) { if ( this.importedNames.has( value ) ) {

@ -4,11 +4,20 @@ import { DomGenerator } from './index';
import { Node } from '../../interfaces'; import { Node } from '../../interfaces';
export interface BlockOptions { export interface BlockOptions {
generator: DomGenerator;
name: string; name: string;
expression: Node; generator?: DomGenerator;
context: string; expression?: Node;
key: string; context?: string;
key?: string;
contexts?: Map<string, string>;
indexes?: Map<string, string>;
contextDependencies?: Map<string, string[]>;
params?: string[];
indexNames?: Map<string, string>;
listNames?: Map<string, string>;
indexName?: string;
listName?: string;
dependencies?: Set<string>;
} }
export default class Block { export default class Block {
@ -16,7 +25,19 @@ export default class Block {
name: string; name: string;
expression: Node; expression: Node;
context: string; context: string;
key: string; key: string;
first: string;
contexts: Map<string, string>;
indexes: Map<string, string>;
contextDependencies: Map<string, string[]>;
dependencies: Set<string>;
params: string[];
indexNames: Map<string, string>;
listNames: Map<string, string>;
indexName: string;
listName: string;
builders: { builders: {
create: CodeBuilder; create: CodeBuilder;
@ -41,6 +62,7 @@ export default class Block {
target: string; target: string;
hasUpdateMethod: boolean; hasUpdateMethod: boolean;
autofocus: string;
constructor ( options: BlockOptions ) { constructor ( options: BlockOptions ) {
this.generator = options.generator; this.generator = options.generator;
@ -95,7 +117,7 @@ export default class Block {
}); });
} }
addElement ( name: string, renderStatement: string, parentNode, needsIdentifier = false ) { addElement ( name: string, renderStatement: string, parentNode: string, needsIdentifier = false ) {
const isToplevel = !parentNode; const isToplevel = !parentNode;
if ( needsIdentifier || isToplevel ) { if ( needsIdentifier || isToplevel ) {
this.builders.create.addLine( this.builders.create.addLine(

@ -18,6 +18,10 @@ export class DomGenerator extends Generator {
readonly: Set<string>; readonly: Set<string>;
metaBindings: string[]; metaBindings: string[];
hasIntroTransitions: boolean;
hasOutroTransitions: boolean;
hasComplexBindings: boolean;
constructor ( parsed: Parsed, source: string, name: string, options: CompileOptions ) { constructor ( parsed: Parsed, source: string, name: string, options: CompileOptions ) {
super( parsed, source, name, options ); super( parsed, source, name, options );
this.blocks = []; this.blocks = [];
@ -48,13 +52,7 @@ export default function dom ( parsed: Parsed, source: string, options: CompileOp
const { computations, hasJs, templateProperties, namespace } = generator.parseJs(); const { computations, hasJs, templateProperties, namespace } = generator.parseJs();
const state = { const { block, state } = preprocess( generator, namespace, parsed.html );
namespace,
parentNode: null,
isTopLevel: true
};
const block = preprocess( generator, state, parsed.html );
parsed.html.children.forEach( ( node: Node ) => { parsed.html.children.forEach( ( node: Node ) => {
visit( generator, block, state, node ); visit( generator, block, state, node );

@ -0,0 +1,10 @@
export interface State {
namespace: string;
parentNode: string;
isTopLevel: boolean
parentNodeName?: string;
basename?: string;
inEachBlock?: boolean;
allUsedContexts?: string[];
usesComponent?: boolean;
}

@ -3,12 +3,13 @@ import { trimStart, trimEnd } from '../../utils/trim';
import { assign } from '../../shared/index.js'; import { assign } from '../../shared/index.js';
import { DomGenerator } from './index'; import { DomGenerator } from './index';
import { Node } from '../../interfaces'; import { Node } from '../../interfaces';
import { State } from './interfaces';
function isElseIf ( node: Node ) { function isElseIf ( node: Node ) {
return node && node.children.length === 1 && node.children[0].type === 'IfBlock'; return node && node.children.length === 1 && node.children[0].type === 'IfBlock';
} }
function getChildState ( parent, child = {} ) { function getChildState ( parent: State, child = {} ) {
return assign( {}, parent, { name: null, parentNode: null }, child || {} ); return assign( {}, parent, { name: null, parentNode: null }, child || {} );
} }
@ -27,7 +28,7 @@ const elementsWithoutText = new Set([
]); ]);
const preprocessors = { const preprocessors = {
MustacheTag: ( generator: DomGenerator, block, state, node: Node ) => { MustacheTag: ( generator: DomGenerator, block: Block, state: State, node: Node ) => {
const dependencies = block.findDependencies( node.expression ); const dependencies = block.findDependencies( node.expression );
block.addDependencies( dependencies ); block.addDependencies( dependencies );
@ -36,7 +37,7 @@ const preprocessors = {
}); });
}, },
RawMustacheTag: ( generator: DomGenerator, block, state, node: Node ) => { RawMustacheTag: ( generator: DomGenerator, block: Block, state: State, node: Node ) => {
const dependencies = block.findDependencies( node.expression ); const dependencies = block.findDependencies( node.expression );
block.addDependencies( dependencies ); block.addDependencies( dependencies );
@ -46,7 +47,7 @@ const preprocessors = {
node._state = getChildState( state, { basename, name }); node._state = getChildState( state, { basename, name });
}, },
Text: ( generator: DomGenerator, block, state, node: Node ) => { Text: ( generator: DomGenerator, block: Block, state: State, node: Node ) => {
node._state = getChildState( state ); node._state = getChildState( state );
if ( !/\S/.test( node.data ) ) { if ( !/\S/.test( node.data ) ) {
@ -58,8 +59,8 @@ const preprocessors = {
node._state.name = block.getUniqueName( `text` ); node._state.name = block.getUniqueName( `text` );
}, },
IfBlock: ( generator: DomGenerator, block, state, node: Node ) => { IfBlock: ( generator: DomGenerator, block: Block, state: State, node: Node ) => {
const blocks = []; const blocks: Block[] = [];
let dynamic = false; let dynamic = false;
let hasIntros = false; let hasIntros = false;
let hasOutros = false; let hasOutros = false;
@ -115,7 +116,7 @@ const preprocessors = {
generator.blocks.push( ...blocks ); generator.blocks.push( ...blocks );
}, },
EachBlock: ( generator: DomGenerator, block, state, node: Node ) => { EachBlock: ( generator: DomGenerator, block: Block, state: State, node: Node ) => {
const dependencies = block.findDependencies( node.expression ); const dependencies = block.findDependencies( node.expression );
block.addDependencies( dependencies ); block.addDependencies( dependencies );
@ -177,7 +178,7 @@ const preprocessors = {
} }
}, },
Element: ( generator: DomGenerator, block, state, node: Node ) => { Element: ( generator: DomGenerator, block: Block, state: State, node: Node ) => {
const isComponent = generator.components.has( node.name ) || node.name === ':Self'; const isComponent = generator.components.has( node.name ) || node.name === ':Self';
if ( isComponent ) { if ( isComponent ) {
@ -240,7 +241,7 @@ const preprocessors = {
} }
}; };
function preprocessChildren ( generator: DomGenerator, block, state, node: Node, isTopLevel: boolean ) { function preprocessChildren ( generator: DomGenerator, block: Block, state: State, node: Node, isTopLevel: boolean = false ) {
// glue text nodes together // glue text nodes together
const cleaned: Node[] = []; const cleaned: Node[] = [];
let lastChild: Node; let lastChild: Node;
@ -294,7 +295,7 @@ function preprocessChildren ( generator: DomGenerator, block, state, node: Node,
node.children = cleaned; node.children = cleaned;
} }
export default function preprocess ( generator: DomGenerator, state, node ) { export default function preprocess ( generator: DomGenerator, namespace: string, node: Node ) {
const block = new Block({ const block = new Block({
generator, generator,
name: generator.alias( 'create_main_fragment' ), name: generator.alias( 'create_main_fragment' ),
@ -311,9 +312,15 @@ export default function preprocess ( generator: DomGenerator, state, node ) {
dependencies: new Set() dependencies: new Set()
}); });
const state: State = {
namespace,
parentNode: null,
isTopLevel: true
};
generator.blocks.push( block ); generator.blocks.push( block );
preprocessChildren( generator, block, state, node, true ); preprocessChildren( generator, block, state, node, true );
block.hasUpdateMethod = block.dependencies.size > 0; block.hasUpdateMethod = block.dependencies.size > 0;
return block; return { block, state };
} }

@ -1,8 +1,9 @@
import { DomGenerator } from '../../index'; import { DomGenerator } from '../../index';
import Block from '../../Block'; import Block from '../../Block';
import { Node } from '../../../../interfaces'; import { Node } from '../../../../interfaces';
import { State } from '../../interfaces';
export default function visitAttribute ( generator: DomGenerator, block: Block, state, node: Node, attribute, local ) { export default function visitAttribute ( generator: DomGenerator, block: Block, state: State, node: Node, attribute, local ) {
if ( attribute.value === true ) { if ( attribute.value === true ) {
// attributes without values, e.g. <textarea readonly> // attributes without values, e.g. <textarea readonly>
local.staticAttributes.push({ local.staticAttributes.push({

@ -4,8 +4,9 @@ import getSetter from '../shared/binding/getSetter';
import { DomGenerator } from '../../index'; import { DomGenerator } from '../../index';
import Block from '../../Block'; import Block from '../../Block';
import { Node } from '../../../../interfaces'; import { Node } from '../../../../interfaces';
import { State } from '../../interfaces';
export default function visitBinding ( generator: DomGenerator, block: Block, state, node: Node, attribute, local ) { export default function visitBinding ( generator: DomGenerator, block: Block, state: State, node: Node, attribute, local ) {
const { name } = flattenReference( attribute.value ); const { name } = flattenReference( attribute.value );
const { snippet, contexts, dependencies } = block.contextualise( attribute.value ); const { snippet, contexts, dependencies } = block.contextualise( attribute.value );

@ -8,8 +8,9 @@ import visitRef from './Ref';
import { DomGenerator } from '../../index'; import { DomGenerator } from '../../index';
import Block from '../../Block'; import Block from '../../Block';
import { Node } from '../../../../interfaces'; import { Node } from '../../../../interfaces';
import { State } from '../../interfaces';
function stringifyProps ( props ) { function stringifyProps ( props: string[] ) {
if ( !props.length ) return '{}'; if ( !props.length ) return '{}';
const joined = props.join( ', ' ); const joined = props.join( ', ' );
@ -35,7 +36,7 @@ const visitors = {
Ref: visitRef Ref: visitRef
}; };
export default function visitComponent ( generator: DomGenerator, block: Block, state, node: Node ) { export default function visitComponent ( generator: DomGenerator, block: Block, state: State, node: Node ) {
const hasChildren = node.children.length > 0; const hasChildren = node.children.length > 0;
const name = block.getUniqueName( ( node.name === ':Self' ? generator.name : node.name ).toLowerCase() ); const name = block.getUniqueName( ( node.name === ':Self' ? generator.name : node.name ).toLowerCase() );
@ -123,7 +124,7 @@ export default function visitComponent ( generator: DomGenerator, block: Block,
componentInitProperties.push( `_yield: ${yieldFragment}`); componentInitProperties.push( `_yield: ${yieldFragment}`);
} }
const statements = []; const statements: string[] = [];
if ( local.staticAttributes.length || local.dynamicAttributes.length || local.bindings.length ) { if ( local.staticAttributes.length || local.dynamicAttributes.length || local.bindings.length ) {
const initialProps = local.staticAttributes const initialProps = local.staticAttributes

@ -2,14 +2,15 @@ import deindent from '../../../../utils/deindent.js';
import { DomGenerator } from '../../index'; import { DomGenerator } from '../../index';
import Block from '../../Block'; import Block from '../../Block';
import { Node } from '../../../../interfaces'; import { Node } from '../../../../interfaces';
import { State } from '../../interfaces';
export default function visitEventHandler ( generator: DomGenerator, block: Block, state, node: Node, attribute, local ) { export default function visitEventHandler ( generator: DomGenerator, block: Block, state: State, node: Node, attribute: Node, local ) {
// TODO verify that it's a valid callee (i.e. built-in or declared method) // TODO verify that it's a valid callee (i.e. built-in or declared method)
generator.addSourcemapLocations( attribute.expression ); generator.addSourcemapLocations( attribute.expression );
generator.code.prependRight( attribute.expression.start, `${block.component}.` ); generator.code.prependRight( attribute.expression.start, `${block.component}.` );
const usedContexts = []; const usedContexts: string[] = [];
attribute.expression.arguments.forEach( arg => { attribute.expression.arguments.forEach( ( arg: Node ) => {
const { contexts } = block.contextualise( arg, null, true ); const { contexts } = block.contextualise( arg, null, true );
contexts.forEach( context => { contexts.forEach( context => {

@ -2,8 +2,9 @@ import deindent from '../../../../utils/deindent.js';
import { DomGenerator } from '../../index'; import { DomGenerator } from '../../index';
import Block from '../../Block'; import Block from '../../Block';
import { Node } from '../../../../interfaces'; import { Node } from '../../../../interfaces';
import { State } from '../../interfaces';
export default function visitRef ( generator: DomGenerator, block: Block, state, node: Node, attribute, local ) { export default function visitRef ( generator: DomGenerator, block: Block, state: State, node: Node, attribute: Node, local ) {
generator.usesRefs = true; generator.usesRefs = true;
local.create.addLine( local.create.addLine(

@ -3,8 +3,9 @@ import visit from '../visit';
import { DomGenerator } from '../index'; import { DomGenerator } from '../index';
import Block from '../Block'; import Block from '../Block';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
import { State } from '../interfaces';
export default function visitEachBlock ( generator: DomGenerator, block: Block, state, node: Node ) { export default function visitEachBlock ( generator: DomGenerator, block: Block, state: State, node: Node ) {
const each_block = generator.getUniqueName( `each_block` ); const each_block = generator.getUniqueName( `each_block` );
const create_each_block = node._block.name; const create_each_block = node._block.name;
const each_block_value = node._block.listName; const each_block_value = node._block.listName;
@ -100,7 +101,7 @@ export default function visitEachBlock ( generator: DomGenerator, block: Block,
} }
} }
function keyed ( generator: DomGenerator, block: Block, state, node: Node, snippet, { each_block, create_each_block, each_block_value, i, params, anchor, mountOrIntro } ) { function keyed ( generator: DomGenerator, block: Block, state: State, node: Node, snippet, { each_block, create_each_block, each_block_value, i, params, anchor, mountOrIntro } ) {
const key = block.getUniqueName( 'key' ); const key = block.getUniqueName( 'key' );
const lookup = block.getUniqueName( `${each_block}_lookup` ); const lookup = block.getUniqueName( `${each_block}_lookup` );
const iteration = block.getUniqueName( `${each_block}_iteration` ); const iteration = block.getUniqueName( `${each_block}_iteration` );
@ -274,7 +275,7 @@ function keyed ( generator: DomGenerator, block: Block, state, node: Node, snipp
` ); ` );
} }
function unkeyed ( generator: DomGenerator, block: Block, state, node: Node, snippet, { create_each_block, each_block_value, iterations, i, params, anchor, mountOrIntro } ) { function unkeyed ( generator: DomGenerator, block: Block, state: State, node: Node, snippet, { create_each_block, each_block_value, iterations, i, params, anchor, mountOrIntro } ) {
block.builders.create.addBlock( deindent` block.builders.create.addBlock( deindent`
var ${iterations} = []; var ${iterations} = [];

@ -4,8 +4,9 @@ import getStaticAttributeValue from './getStaticAttributeValue';
import { DomGenerator } from '../../index'; import { DomGenerator } from '../../index';
import Block from '../../Block'; import Block from '../../Block';
import { Node } from '../../../../interfaces'; import { Node } from '../../../../interfaces';
import { State } from '../../interfaces';
export default function visitAttribute ( generator: DomGenerator, block: Block, state, node: Node, attribute ) { export default function visitAttribute ( generator: DomGenerator, block: Block, state: State, node: Node, attribute: Node ) {
const name = attribute.name; const name = attribute.name;
let metadata = state.namespace ? null : attributeLookup[ name ]; let metadata = state.namespace ? null : attributeLookup[ name ];
@ -35,7 +36,7 @@ export default function visitAttribute ( generator: DomGenerator, block: Block,
} else { } else {
// '{{foo}} {{bar}}' — treat as string concatenation // '{{foo}} {{bar}}' — treat as string concatenation
value = ( attribute.value[0].type === 'Text' ? '' : `"" + ` ) + ( value = ( attribute.value[0].type === 'Text' ? '' : `"" + ` ) + (
attribute.value.map( chunk => { attribute.value.map( ( chunk: Node ) => {
if ( chunk.type === 'Text' ) { if ( chunk.type === 'Text' ) {
return JSON.stringify( chunk.data ); return JSON.stringify( chunk.data );
} else { } else {

@ -5,8 +5,9 @@ import getStaticAttributeValue from './getStaticAttributeValue';
import { DomGenerator } from '../../index'; import { DomGenerator } from '../../index';
import Block from '../../Block'; import Block from '../../Block';
import { Node } from '../../../../interfaces'; import { Node } from '../../../../interfaces';
import { State } from '../../interfaces';
export default function visitBinding ( generator: DomGenerator, block: Block, state, node: Node, attribute ) { export default function visitBinding ( generator: DomGenerator, block: Block, state: State, node: Node, attribute: Node ) {
const { name, parts } = flattenReference( attribute.value ); const { name, parts } = flattenReference( attribute.value );
const { snippet, contexts, dependencies } = block.contextualise( attribute.value ); const { snippet, contexts, dependencies } = block.contextualise( attribute.value );
@ -18,7 +19,7 @@ export default function visitBinding ( generator: DomGenerator, block: Block, st
const eventName = getBindingEventName( node, attribute ); const eventName = getBindingEventName( node, attribute );
const handler = block.getUniqueName( `${state.parentNode}_${eventName}_handler` ); const handler = block.getUniqueName( `${state.parentNode}_${eventName}_handler` );
const isMultipleSelect = node.name === 'select' && node.attributes.find( attr => attr.name.toLowerCase() === 'multiple' ); // TODO use getStaticAttributeValue const isMultipleSelect = node.name === 'select' && node.attributes.find( ( attr: Node ) => attr.name.toLowerCase() === 'multiple' ); // TODO use getStaticAttributeValue
const type = getStaticAttributeValue( node, 'type' ); const type = getStaticAttributeValue( node, 'type' );
const bindingGroup = attribute.name === 'group' ? getBindingGroup( generator, parts.join( '.' ) ) : null; const bindingGroup = attribute.name === 'group' ? getBindingGroup( generator, parts.join( '.' ) ) : null;
const value = getBindingValue( generator, block, state, node, attribute, isMultipleSelect, bindingGroup, type ); const value = getBindingValue( generator, block, state, node, attribute, isMultipleSelect, bindingGroup, type );
@ -144,9 +145,9 @@ export default function visitBinding ( generator: DomGenerator, block: Block, st
} }
} }
function getBindingEventName ( node, attribute ) { function getBindingEventName ( node: Node, attribute: Node ) {
if ( node.name === 'input' ) { if ( node.name === 'input' ) {
const typeAttribute = node.attributes.find( attr => attr.type === 'Attribute' && attr.name === 'type' ); const typeAttribute = node.attributes.find( ( attr: Node ) => attr.type === 'Attribute' && attr.name === 'type' );
const type = typeAttribute ? typeAttribute.value[0].data : 'text'; // TODO in validation, should throw if type attribute is not static const type = typeAttribute ? typeAttribute.value[0].data : 'text'; // TODO in validation, should throw if type attribute is not static
return type === 'checkbox' || type === 'radio' ? 'change' : 'input'; return type === 'checkbox' || type === 'radio' ? 'change' : 'input';
@ -160,7 +161,7 @@ function getBindingEventName ( node, attribute ) {
return 'change'; return 'change';
} }
function getBindingValue ( generator, block, state, node, attribute, isMultipleSelect, bindingGroup, type ) { function getBindingValue ( generator: DomGenerator, block: Block, state: State, node: Node, attribute: Node, isMultipleSelect: boolean, bindingGroup: number, type: string ) {
// <select multiple bind:value='selected> // <select multiple bind:value='selected>
if ( isMultipleSelect ) { if ( isMultipleSelect ) {
return `[].map.call( ${state.parentNode}.querySelectorAll(':checked'), function ( option ) { return option.__value; })`; return `[].map.call( ${state.parentNode}.querySelectorAll(':checked'), function ( option ) { return option.__value; })`;
@ -189,7 +190,7 @@ function getBindingValue ( generator, block, state, node, attribute, isMultipleS
return `${state.parentNode}.${attribute.name}`; return `${state.parentNode}.${attribute.name}`;
} }
function getBindingGroup ( generator, keypath ) { function getBindingGroup ( generator: DomGenerator, keypath: string ) {
// TODO handle contextual bindings — `keypath` should include unique ID of // TODO handle contextual bindings — `keypath` should include unique ID of
// each block that provides context // each block that provides context
let index = generator.bindingGroups.indexOf( keypath ); let index = generator.bindingGroups.indexOf( keypath );

@ -10,6 +10,7 @@ import addTransitions from './addTransitions';
import { DomGenerator } from '../../index'; import { DomGenerator } from '../../index';
import Block from '../../Block'; import Block from '../../Block';
import { Node } from '../../../../interfaces'; import { Node } from '../../../../interfaces';
import { State } from '../../interfaces';
const meta = { const meta = {
':Window': visitWindow ':Window': visitWindow
@ -29,7 +30,7 @@ const visitors = {
Ref: visitRef Ref: visitRef
}; };
export default function visitElement ( generator: DomGenerator, block: Block, state, node: Node ) { export default function visitElement ( generator: DomGenerator, block: Block, state: State, node: Node ) {
if ( node.name in meta ) { if ( node.name in meta ) {
return meta[ node.name ]( generator, block, node ); return meta[ node.name ]( generator, block, node );
} }
@ -54,8 +55,8 @@ export default function visitElement ( generator: DomGenerator, block: Block, st
let outro; let outro;
node.attributes node.attributes
.sort( ( a, b ) => order[ a.type ] - order[ b.type ] ) .sort( ( a: Node, b: Node ) => order[ a.type ] - order[ b.type ] )
.forEach( attribute => { .forEach( ( attribute: Node ) => {
if ( attribute.type === 'Transition' ) { if ( attribute.type === 'Transition' ) {
if ( attribute.intro ) intro = attribute; if ( attribute.intro ) intro = attribute;
if ( attribute.outro ) outro = attribute; if ( attribute.outro ) outro = attribute;
@ -68,14 +69,14 @@ export default function visitElement ( generator: DomGenerator, block: Block, st
if ( intro || outro ) addTransitions( generator, block, childState, node, intro, outro ); if ( intro || outro ) addTransitions( generator, block, childState, node, intro, outro );
if ( childState.allUsedContexts.length || childState.usesComponent ) { if ( childState.allUsedContexts.length || childState.usesComponent ) {
const initialProps = []; const initialProps: string[] = [];
const updates = []; const updates: string[] = [];
if ( childState.usesComponent ) { if ( childState.usesComponent ) {
initialProps.push( `component: ${block.component}` ); initialProps.push( `component: ${block.component}` );
} }
childState.allUsedContexts.forEach( contextName => { childState.allUsedContexts.forEach( ( contextName: string ) => {
if ( contextName === 'state' ) return; if ( contextName === 'state' ) return;
const listName = block.listNames.get( contextName ); const listName = block.listNames.get( contextName );
@ -112,12 +113,12 @@ export default function visitElement ( generator: DomGenerator, block: Block, st
} }
// special case bound <option> without a value attribute // special case bound <option> without a value attribute
if ( node.name === 'option' && !node.attributes.find( attribute => attribute.type === 'Attribute' && attribute.name === 'value' ) ) { // TODO check it's bound if ( node.name === 'option' && !node.attributes.find( ( attribute: Node ) => attribute.type === 'Attribute' && attribute.name === 'value' ) ) { // TODO check it's bound
const statement = `${name}.__value = ${name}.textContent;`; const statement = `${name}.__value = ${name}.textContent;`;
node.initialUpdate = node.lateUpdate = statement; node.initialUpdate = node.lateUpdate = statement;
} }
node.children.forEach( child => { node.children.forEach( ( child: Node ) => {
visit( generator, block, childState, child ); visit( generator, block, childState, child );
}); });
@ -134,7 +135,7 @@ export default function visitElement ( generator: DomGenerator, block: Block, st
} }
} }
function getRenderStatement ( generator: DomGenerator, namespace, name ) { function getRenderStatement ( generator: DomGenerator, namespace: string, name: string ) {
if ( namespace === 'http://www.w3.org/2000/svg' ) { if ( namespace === 'http://www.w3.org/2000/svg' ) {
return `${generator.helper( 'createSvgElement' )}( '${name}' )`; return `${generator.helper( 'createSvgElement' )}( '${name}' )`;
} }

@ -3,8 +3,9 @@ import flattenReference from '../../../../utils/flattenReference';
import { DomGenerator } from '../../index'; import { DomGenerator } from '../../index';
import Block from '../../Block'; import Block from '../../Block';
import { Node } from '../../../../interfaces'; import { Node } from '../../../../interfaces';
import { State } from '../../interfaces';
export default function visitEventHandler ( generator: DomGenerator, block: Block, state, node: Node, attribute ) { export default function visitEventHandler ( generator: DomGenerator, block: Block, state: State, node: Node, attribute: Node ) {
const name = attribute.name; const name = attribute.name;
const isCustomEvent = generator.events.has( name ); const isCustomEvent = generator.events.has( name );
const shouldHoist = !isCustomEvent && state.inEachBlock; const shouldHoist = !isCustomEvent && state.inEachBlock;
@ -20,8 +21,8 @@ export default function visitEventHandler ( generator: DomGenerator, block: Bloc
} }
const context = shouldHoist ? null : state.parentNode; const context = shouldHoist ? null : state.parentNode;
const usedContexts = []; const usedContexts: string[] = [];
attribute.expression.arguments.forEach( arg => { attribute.expression.arguments.forEach( ( arg: Node ) => {
const { contexts } = block.contextualise( arg, context, true ); const { contexts } = block.contextualise( arg, context, true );
contexts.forEach( context => { contexts.forEach( context => {

@ -2,8 +2,9 @@ import deindent from '../../../../utils/deindent.js';
import { DomGenerator } from '../../index'; import { DomGenerator } from '../../index';
import Block from '../../Block'; import Block from '../../Block';
import { Node } from '../../../../interfaces'; import { Node } from '../../../../interfaces';
import { State } from '../../interfaces';
export default function visitRef ( generator: DomGenerator, block: Block, state, node: Node, attribute ) { export default function visitRef ( generator: DomGenerator, block: Block, state: State, node: Node, attribute: Node ) {
const name = attribute.name; const name = attribute.name;
block.builders.create.addLine( block.builders.create.addLine(

@ -2,8 +2,9 @@ import deindent from '../../../../utils/deindent.js';
import { DomGenerator } from '../../index'; import { DomGenerator } from '../../index';
import Block from '../../Block'; import Block from '../../Block';
import { Node } from '../../../../interfaces'; import { Node } from '../../../../interfaces';
import { State } from '../../interfaces';
export default function addTransitions ( generator: DomGenerator, block: Block, state, node: Node, intro, outro ) { export default function addTransitions ( generator: DomGenerator, block: Block, state: State, node: Node, intro, outro ) {
const wrapTransition = generator.helper( 'wrapTransition' ); const wrapTransition = generator.helper( 'wrapTransition' );
if ( intro === outro ) { if ( intro === outro ) {

@ -3,6 +3,7 @@ import visit from '../visit';
import { DomGenerator } from '../index'; import { DomGenerator } from '../index';
import Block from '../Block'; import Block from '../Block';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
import { State } from '../interfaces';
function isElseIf ( node: Node ) { function isElseIf ( node: Node ) {
return node && node.children.length === 1 && node.children[0].type === 'IfBlock'; return node && node.children.length === 1 && node.children[0].type === 'IfBlock';
@ -12,7 +13,7 @@ function isElseBranch ( branch ) {
return branch.block && !branch.condition; return branch.block && !branch.condition;
} }
function getBranches ( generator: DomGenerator, block: Block, state, node: Node ) { function getBranches ( generator: DomGenerator, block: Block, state: State, node: Node ) {
const branches = [{ const branches = [{
condition: block.contextualise( node.expression ).snippet, condition: block.contextualise( node.expression ).snippet,
block: node._block.name, block: node._block.name,
@ -44,13 +45,13 @@ function getBranches ( generator: DomGenerator, block: Block, state, node: Node
return branches; return branches;
} }
function visitChildren ( generator: DomGenerator, block: Block, state, node: Node ) { function visitChildren ( generator: DomGenerator, block: Block, state: State, node: Node ) {
node.children.forEach( ( child: Node ) => { node.children.forEach( ( child: Node ) => {
visit( generator, node._block, node._state, child ); visit( generator, node._block, node._state, child );
}); });
} }
export default function visitIfBlock ( generator: DomGenerator, block: Block, state, node: Node ) { export default function visitIfBlock ( generator: DomGenerator, block: Block, state: State, node: Node ) {
const name = generator.getUniqueName( `if_block` ); const name = generator.getUniqueName( `if_block` );
const anchor = node.needsAnchor ? block.getUniqueName( `${name}_anchor` ) : ( node.next && node.next._state.name ) || 'null'; const anchor = node.needsAnchor ? block.getUniqueName( `${name}_anchor` ) : ( node.next && node.next._state.name ) || 'null';
const params = block.params.join( ', ' ); const params = block.params.join( ', ' );
@ -82,7 +83,7 @@ export default function visitIfBlock ( generator: DomGenerator, block: Block, st
} }
} }
function simple ( generator: DomGenerator, block: Block, state, node: Node, branch, dynamic, { name, anchor, params, if_name } ) { function simple ( generator: DomGenerator, block: Block, state: State, node: Node, branch, dynamic, { name, anchor, params, if_name } ) {
block.builders.create.addBlock( deindent` block.builders.create.addBlock( deindent`
var ${name} = (${branch.condition}) && ${branch.block}( ${params}, ${block.component} ); var ${name} = (${branch.condition}) && ${branch.block}( ${params}, ${block.component} );
` ); ` );
@ -156,7 +157,7 @@ function simple ( generator: DomGenerator, block: Block, state, node: Node, bran
); );
} }
function compound ( generator: DomGenerator, block: Block, state, node: Node, branches, dynamic, { name, anchor, params, hasElse, if_name } ) { function compound ( generator: DomGenerator, block: Block, state: State, node: Node, branches, dynamic, { name, anchor, params, hasElse, if_name } ) {
const get_block = block.getUniqueName( `get_block` ); const get_block = block.getUniqueName( `get_block` );
const current_block = block.getUniqueName( `current_block` ); const current_block = block.getUniqueName( `current_block` );
const current_block_and = hasElse ? '' : `${current_block} && `; const current_block_and = hasElse ? '' : `${current_block} && `;
@ -212,7 +213,7 @@ function compound ( generator: DomGenerator, block: Block, state, node: Node, br
// if any of the siblings have outros, we need to keep references to the blocks // if any of the siblings have outros, we need to keep references to the blocks
// (TODO does this only apply to bidi transitions?) // (TODO does this only apply to bidi transitions?)
function compoundWithOutros ( generator: DomGenerator, block: Block, state, node: Node, branches, dynamic, { name, anchor, params, hasElse } ) { function compoundWithOutros ( generator: DomGenerator, block: Block, state: State, node: Node, branches, dynamic, { name, anchor, params, hasElse } ) {
const get_block = block.getUniqueName( `get_block` ); const get_block = block.getUniqueName( `get_block` );
const current_block_index = block.getUniqueName( `current_block_index` ); const current_block_index = block.getUniqueName( `current_block_index` );
const previous_block_index = block.getUniqueName( `previous_block_index` ); const previous_block_index = block.getUniqueName( `previous_block_index` );

@ -2,8 +2,9 @@ import deindent from '../../../utils/deindent.js';
import { DomGenerator } from '../index'; import { DomGenerator } from '../index';
import Block from '../Block'; import Block from '../Block';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
import { State } from '../interfaces';
export default function visitMustacheTag ( generator: DomGenerator, block: Block, state, node: Node ) { export default function visitMustacheTag ( generator: DomGenerator, block: Block, state: State, node: Node ) {
const name = node._state.name; const name = node._state.name;
const value = block.getUniqueName( `${name}_value` ); const value = block.getUniqueName( `${name}_value` );

@ -2,8 +2,9 @@ import deindent from '../../../utils/deindent.js';
import { DomGenerator } from '../index'; import { DomGenerator } from '../index';
import Block from '../Block'; import Block from '../Block';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
import { State } from '../interfaces';
export default function visitRawMustacheTag ( generator: DomGenerator, block: Block, state, node: Node ) { export default function visitRawMustacheTag ( generator: DomGenerator, block: Block, state: State, node: Node ) {
const name = node._state.basename; const name = node._state.basename;
const before = node._state.name; const before = node._state.name;
const value = block.getUniqueName( `${name}_value` ); const value = block.getUniqueName( `${name}_value` );

@ -1,8 +1,9 @@
import { DomGenerator } from '../index'; import { DomGenerator } from '../index';
import Block from '../Block'; import Block from '../Block';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
import { State } from '../interfaces';
export default function visitText ( generator: DomGenerator, block: Block, state, node: Node ) { export default function visitText ( generator: DomGenerator, block: Block, state: State, node: Node ) {
if ( !node._state.shouldCreate ) return; if ( !node._state.shouldCreate ) return;
block.addElement( node._state.name, `${generator.helper( 'createText' )}( ${JSON.stringify( node.data )} )`, state.parentNode, node.usedAsAnchor ); block.addElement( node._state.name, `${generator.helper( 'createText' )}( ${JSON.stringify( node.data )} )`, state.parentNode, node.usedAsAnchor );
} }

@ -1,7 +1,8 @@
import { DomGenerator } from '../index'; import { DomGenerator } from '../index';
import Block from '../Block'; import Block from '../Block';
import { State } from '../interfaces';
export default function visitYieldTag ( generator: DomGenerator, block: Block, state ) { export default function visitYieldTag ( generator: DomGenerator, block: Block, state: State ) {
const parentNode = state.parentNode || block.target; const parentNode = state.parentNode || block.target;
( state.parentNode ? block.builders.create : block.builders.mount ).addLine( ( state.parentNode ? block.builders.create : block.builders.mount ).addLine(

@ -2,20 +2,24 @@ import deindent from '../../utils/deindent.js';
import Generator from '../Generator'; import Generator from '../Generator';
import Block from './Block'; import Block from './Block';
import visit from './visit'; import visit from './visit';
import { Parsed, Node, CompileOptions } from '../../interfaces';
class SsrGenerator extends Generator { export class SsrGenerator extends Generator {
constructor ( parsed, source, name, options ) { bindings: string[];
renderCode: string;
constructor ( parsed: Parsed, source: string, name: string, options: CompileOptions ) {
super( parsed, source, name, options ); super( parsed, source, name, options );
this.bindings = []; this.bindings = [];
this.renderCode = ''; this.renderCode = '';
} }
append ( code ) { append ( code: string ) {
this.renderCode += code; this.renderCode += code;
} }
} }
export default function ssr ( parsed, source, options ) { export default function ssr ( parsed: Parsed, source: string, options: CompileOptions ) {
const format = options.format || 'cjs'; const format = options.format || 'cjs';
const name = options.name || 'SvelteComponent'; const name = options.name || 'SvelteComponent';
@ -31,7 +35,7 @@ export default function ssr ( parsed, source, options ) {
conditions: [] conditions: []
}); });
parsed.html.children.forEach( node => { parsed.html.children.forEach( ( node: Node ) => {
visit( generator, mainBlock, node ); visit( generator, mainBlock, node );
}); });

@ -48,7 +48,7 @@ export function compile ( source: string, _options: CompileOptions ) {
return compiler( parsed, source, options ); return compiler( parsed, source, options );
} }
export function create ( source, _options = {} ) { export function create ( source: string, _options: CompileOptions ) {
_options.format = 'eval'; _options.format = 'eval';
const compiled = compile( source, _options ); const compiled = compile( source, _options );

@ -36,6 +36,7 @@ export interface CompileOptions {
format?: string; format?: string;
name?: string; name?: string;
filename?: string; filename?: string;
generate?: string;
dev?: boolean; dev?: boolean;
shared?: boolean | string; shared?: boolean | string;

Loading…
Cancel
Save