pull/573/head
Rich-Harris 8 years ago
parent 78adc5b226
commit dc82db609c

@ -10,7 +10,9 @@ import getIntro from './shared/utils/getIntro';
import getOutro from './shared/utils/getOutro';
import processCss from './shared/processCss';
import annotateWithScopes from '../utils/annotateWithScopes';
import { Node, Parsed, CompileOptions } from '../../interfaces';
import DomBlock from './dom/Block';
import SsrBlock from './server-side-rendering/Block';
import { Node, Parsed, CompileOptions } from '../interfaces';
const test = typeof global !== 'undefined' && global.__svelte_test;
@ -85,7 +87,7 @@ export default class Generator {
return this.aliases.get( name );
}
contextualise ( block, expression: Node, context, isEventHandler ) {
contextualise ( block: DomBlock | SsrBlock, expression: Node, context: string, isEventHandler: boolean ) {
this.addSourcemapLocations( expression );
const usedContexts: string[] = [];
@ -219,7 +221,7 @@ export default class Generator {
generate ( result, options, { name, format } ) {
if ( this.imports.length ) {
const statements = [];
const statements: string[] = [];
this.imports.forEach( ( declaration, i ) => {
if ( format === 'es' ) {
@ -227,14 +229,14 @@ export default class Generator {
return;
}
const defaultImport = declaration.specifiers.find( x => x.type === 'ImportDefaultSpecifier' || x.type === 'ImportSpecifier' && x.imported.name === 'default' );
const namespaceImport = declaration.specifiers.find( x => x.type === 'ImportNamespaceSpecifier' );
const namedImports = declaration.specifiers.filter( x => x.type === 'ImportSpecifier' && x.imported.name !== 'default' );
const defaultImport = declaration.specifiers.find( ( x: Node ) => x.type === 'ImportDefaultSpecifier' || x.type === 'ImportSpecifier' && x.imported.name === 'default' );
const namespaceImport = declaration.specifiers.find( ( x: Node ) => x.type === 'ImportNamespaceSpecifier' );
const namedImports = declaration.specifiers.filter( ( x: Node ) => x.type === 'ImportSpecifier' && x.imported.name !== 'default' );
const name = ( defaultImport || namespaceImport ) ? ( defaultImport || namespaceImport ).local.name : `__import${i}`;
declaration.name = name; // hacky but makes life a bit easier later
namedImports.forEach( specifier => {
namedImports.forEach( ( specifier: Node ) => {
statements.push( `var ${specifier.local.name} = ${name}.${specifier.imported.name}` );
});
@ -253,7 +255,7 @@ export default class Generator {
const compiled = new Bundle({ separator: '' });
function addString ( str ) {
function addString ( str: string ) {
compiled.addSource({
content: new MagicString( str )
});
@ -273,7 +275,7 @@ export default class Generator {
});
}
parts.forEach( str => {
parts.forEach( ( str: string ) => {
const chunk = str.replace( pattern, '' );
if ( chunk ) addString( chunk );

@ -4,16 +4,43 @@ import { DomGenerator } from './index';
import { Node } from '../../interfaces';
export interface BlockOptions {
generator: DomGenerator
name: string
expression: Node
context: string
generator: DomGenerator;
name: string;
expression: Node;
context: string;
key: string;
}
export default class Block {
generator: DomGenerator;
name: string;
expression: Node;
context: string;
key: string;
builders: {
create: CodeBuilder;
mount: CodeBuilder;
update: CodeBuilder;
intro: CodeBuilder;
outro: CodeBuilder;
detach: CodeBuilder;
detachRaw: CodeBuilder;
destroy: CodeBuilder;
}
hasIntroMethod: boolean;
hasOutroMethod: boolean;
outros: number;
aliases: Map<string, string>;
variables: Map<string, string>;
getUniqueName: (name: string) => string;
component: string;
target: string;
hasUpdateMethod: boolean;
constructor ( options: BlockOptions ) {
this.generator = options.generator;
@ -85,7 +112,7 @@ export default class Block {
}
}
addVariable ( name: string, init ) {
addVariable ( name: string, init?: string ) {
if ( this.variables.has( name ) && this.variables.get( name ) !== init ) {
throw new Error( `Variable '${name}' already initialised with a different value` );
}
@ -101,11 +128,11 @@ export default class Block {
return this.aliases.get( name );
}
child ( options ) {
child ( options: BlockOptions ) {
return new Block( Object.assign( {}, this, options, { parent: this } ) );
}
contextualise ( expression, context, isEventHandler ) {
contextualise ( expression: Node, context?: string, isEventHandler?: boolean ) {
return this.generator.contextualise( this, expression, context, isEventHandler );
}

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

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

@ -5,6 +5,9 @@ import visitAttribute from './Attribute';
import visitEventHandler from './EventHandler';
import visitBinding from './Binding';
import visitRef from './Ref';
import { DomGenerator } from '../../index';
import Block from '../../Block';
import { Node } from '../../../../interfaces';
function stringifyProps ( props ) {
if ( !props.length ) return '{}';
@ -32,7 +35,7 @@ const visitors = {
Ref: visitRef
};
export default function visitComponent ( generator, block, state, node ) {
export default function visitComponent ( generator: DomGenerator, block: Block, state, node: Node ) {
const hasChildren = node.children.length > 0;
const name = block.getUniqueName( ( node.name === ':Self' ? generator.name : node.name ).toLowerCase() );

@ -1,6 +1,9 @@
import deindent from '../../../../utils/deindent.js';
import { DomGenerator } from '../../index';
import Block from '../../Block';
import { Node } from '../../../../interfaces';
export default function visitEventHandler ( generator, block, state, node, attribute, local ) {
export default function visitEventHandler ( generator: DomGenerator, block: Block, state, node: Node, attribute, local ) {
// TODO verify that it's a valid callee (i.e. built-in or declared method)
generator.addSourcemapLocations( attribute.expression );
generator.code.prependRight( attribute.expression.start, `${block.component}.` );

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

@ -1,7 +1,10 @@
import deindent from '../../../utils/deindent.js';
import visit from '../visit';
import { DomGenerator } from '../index';
import Block from '../Block';
import { Node } from '../../../interfaces';
export default function visitEachBlock ( generator, block, state, node ) {
export default function visitEachBlock ( generator: DomGenerator, block: Block, state, node: Node ) {
const each_block = generator.getUniqueName( `each_block` );
const create_each_block = node._block.name;
const each_block_value = node._block.listName;
@ -86,18 +89,18 @@ export default function visitEachBlock ( generator, block, state, node ) {
` );
}
node.children.forEach( child => {
node.children.forEach( ( child: Node ) => {
visit( generator, node._block, node._state, child );
});
if ( node.else ) {
node.else.children.forEach( child => {
node.else.children.forEach( ( child: Node ) => {
visit( generator, node.else._block, node.else._state, child );
});
}
}
function keyed ( generator, block, state, node, snippet, { each_block, create_each_block, each_block_value, i, params, anchor, mountOrIntro } ) {
function keyed ( generator: DomGenerator, block: Block, state, node: Node, snippet, { each_block, create_each_block, each_block_value, i, params, anchor, mountOrIntro } ) {
const key = block.getUniqueName( 'key' );
const lookup = block.getUniqueName( `${each_block}_lookup` );
const iteration = block.getUniqueName( `${each_block}_iteration` );
@ -271,7 +274,7 @@ function keyed ( generator, block, state, node, snippet, { each_block, create_ea
` );
}
function unkeyed ( generator, block, state, node, snippet, { create_each_block, each_block_value, iterations, i, params, anchor, mountOrIntro } ) {
function unkeyed ( generator: DomGenerator, block: Block, state, node: Node, snippet, { create_each_block, each_block_value, iterations, i, params, anchor, mountOrIntro } ) {
block.builders.create.addBlock( deindent`
var ${iterations} = [];

@ -1,8 +1,11 @@
import attributeLookup from './lookup';
import deindent from '../../../../utils/deindent.js';
import getStaticAttributeValue from './getStaticAttributeValue';
import { DomGenerator } from '../../index';
import Block from '../../Block';
import { Node } from '../../../../interfaces';
export default function visitAttribute ( generator, block, state, node, attribute ) {
export default function visitAttribute ( generator: DomGenerator, block: Block, state, node: Node, attribute ) {
const name = attribute.name;
let metadata = state.namespace ? null : attributeLookup[ name ];

@ -2,8 +2,11 @@ import deindent from '../../../../utils/deindent.js';
import flattenReference from '../../../../utils/flattenReference';
import getSetter from '../shared/binding/getSetter';
import getStaticAttributeValue from './getStaticAttributeValue';
import { DomGenerator } from '../../index';
import Block from '../../Block';
import { Node } from '../../../../interfaces';
export default function visitBinding ( generator, block, state, node, attribute ) {
export default function visitBinding ( generator: DomGenerator, block: Block, state, node: Node, attribute ) {
const { name, parts } = flattenReference( attribute.value );
const { snippet, contexts, dependencies } = block.contextualise( attribute.value );

@ -7,6 +7,9 @@ import visitEventHandler from './EventHandler';
import visitBinding from './Binding';
import visitRef from './Ref';
import addTransitions from './addTransitions';
import { DomGenerator } from '../../index';
import Block from '../../Block';
import { Node } from '../../../../interfaces';
const meta = {
':Window': visitWindow
@ -26,7 +29,7 @@ const visitors = {
Ref: visitRef
};
export default function visitElement ( generator, block, state, node ) {
export default function visitElement ( generator: DomGenerator, block: Block, state, node: Node ) {
if ( node.name in meta ) {
return meta[ node.name ]( generator, block, node );
}
@ -131,7 +134,7 @@ export default function visitElement ( generator, block, state, node ) {
}
}
function getRenderStatement ( generator, namespace, name ) {
function getRenderStatement ( generator: DomGenerator, namespace, name ) {
if ( namespace === 'http://www.w3.org/2000/svg' ) {
return `${generator.helper( 'createSvgElement' )}( '${name}' )`;
}

@ -1,7 +1,10 @@
import deindent from '../../../../utils/deindent.js';
import flattenReference from '../../../../utils/flattenReference';
import { DomGenerator } from '../../index';
import Block from '../../Block';
import { Node } from '../../../../interfaces';
export default function visitEventHandler ( generator, block, state, node, attribute ) {
export default function visitEventHandler ( generator: DomGenerator, block: Block, state, node: Node, attribute ) {
const name = attribute.name;
const isCustomEvent = generator.events.has( name );
const shouldHoist = !isCustomEvent && state.inEachBlock;

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

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

@ -1,5 +1,7 @@
export default function getStaticAttributeValue ( node, name ) {
const attribute = node.attributes.find( attr => attr.name.toLowerCase() === name );
import { Node } from '../../../../interfaces';
export default function getStaticAttributeValue ( node: Node, name: string ) {
const attribute = node.attributes.find( ( attr: Node ) => attr.name.toLowerCase() === name );
if ( !attribute ) return null;
if ( attribute.value.length !== 1 || attribute.value[0].type !== 'Text' ) {

@ -1,5 +1,8 @@
import flattenReference from '../../../../../utils/flattenReference';
import deindent from '../../../../../utils/deindent.js';
import { DomGenerator } from '../../../index';
import Block from '../../../Block';
import { Node } from '../../../../../interfaces';
const associatedEvents = {
innerWidth: 'resize',
@ -19,18 +22,18 @@ const readonly = new Set([
'online'
]);
export default function visitWindow ( generator, block, node ) {
export default function visitWindow ( generator: DomGenerator, block: Block, node: Node ) {
const events = {};
const bindings = {};
node.attributes.forEach( attribute => {
node.attributes.forEach( ( attribute: Node ) => {
if ( attribute.type === 'EventHandler' ) {
// TODO verify that it's a valid callee (i.e. built-in or declared method)
generator.addSourcemapLocations( attribute.expression );
let usesState = false;
attribute.expression.arguments.forEach( arg => {
attribute.expression.arguments.forEach( ( arg: Node ) => {
const { contexts } = block.contextualise( arg, null, true );
if ( contexts.length ) usesState = true;
});

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

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

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

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