Merge branch 'master' into gh-679

pull/689/head
Rich Harris 8 years ago
commit 72f39fd7f9

@ -1 +1,2 @@
test/test.js --bail
test/test.js

@ -2,6 +2,7 @@ import CodeBuilder from '../../utils/CodeBuilder';
import deindent from '../../utils/deindent'; import deindent from '../../utils/deindent';
import { DomGenerator } from './index'; import { DomGenerator } from './index';
import { Node } from '../../interfaces'; import { Node } from '../../interfaces';
import shared from './shared';
export interface BlockOptions { export interface BlockOptions {
name: string; name: string;
@ -61,9 +62,6 @@ export default class Block {
variables: Map<string, string>; variables: Map<string, string>;
getUniqueName: (name: string) => string; getUniqueName: (name: string) => string;
component: string;
target: string;
hasUpdateMethod: boolean; hasUpdateMethod: boolean;
autofocus: string; autofocus: string;
@ -110,10 +108,6 @@ export default class Block {
this.variables = new Map(); this.variables = new Map();
this.getUniqueName = this.generator.getUniqueNameMaker(options.params); this.getUniqueName = this.generator.getUniqueNameMaker(options.params);
// unique names
this.component = this.getUniqueName('component');
this.target = this.getUniqueName('target');
this.hasUpdateMethod = false; // determined later this.hasUpdateMethod = false; // determined later
} }
@ -134,14 +128,12 @@ export default class Block {
this.addVariable(name); this.addVariable(name);
this.builders.create.addLine(`${name} = ${renderStatement};`); this.builders.create.addLine(`${name} = ${renderStatement};`);
this.builders.claim.addLine(`${name} = ${claimStatement};`) this.builders.claim.addLine(`${name} = ${claimStatement};`);
this.mount(name, parentNode); this.mount(name, parentNode);
if (isToplevel) { if (isToplevel) {
this.builders.unmount.addLine( this.builders.unmount.addLine(`@detachNode( ${name} );`);
`${this.generator.helper('detachNode')}( ${name} );`
);
} }
} }
@ -186,14 +178,9 @@ export default class Block {
mount(name: string, parentNode: string) { mount(name: string, parentNode: string) {
if (parentNode) { if (parentNode) {
this.builders.mount.addLine( this.builders.mount.addLine(`@appendNode( ${name}, ${parentNode} );`);
`${this.generator.helper('appendNode')}( ${name}, ${parentNode} );`
);
} else { } else {
this.builders.mount.addLine( this.builders.mount.addLine(`@insertNode( ${name}, #target, anchor );`);
`${this.generator.helper('insertNode')}( ${name}, ${this
.target}, anchor );`
);
} }
} }
@ -229,11 +216,11 @@ export default class Block {
if (this.first) { if (this.first) {
properties.addBlock(`first: null,`); properties.addBlock(`first: null,`);
this.builders.hydrate.addLine( `this.first = ${this.first};` ); this.builders.hydrate.addLine(`this.first = ${this.first};`);
} }
if (this.builders.create.isEmpty()) { if (this.builders.create.isEmpty()) {
properties.addBlock(`create: ${this.generator.helper('noop')},`); properties.addBlock(`create: @noop,`);
} else { } else {
properties.addBlock(deindent` properties.addBlock(deindent`
create: function () { create: function () {
@ -245,7 +232,7 @@ export default class Block {
if (this.generator.hydratable) { if (this.generator.hydratable) {
if (this.builders.claim.isEmpty()) { if (this.builders.claim.isEmpty()) {
properties.addBlock(`claim: ${this.generator.helper('noop')},`); properties.addBlock(`claim: @noop,`);
} else { } else {
properties.addBlock(deindent` properties.addBlock(deindent`
claim: function ( nodes ) { claim: function ( nodes ) {
@ -265,10 +252,10 @@ export default class Block {
} }
if (this.builders.mount.isEmpty()) { if (this.builders.mount.isEmpty()) {
properties.addBlock(`mount: ${this.generator.helper('noop')},`); properties.addBlock(`mount: @noop,`);
} else { } else {
properties.addBlock(deindent` properties.addBlock(deindent`
mount: function ( ${this.target}, anchor ) { mount: function ( #target, anchor ) {
${this.builders.mount} ${this.builders.mount}
}, },
`); `);
@ -276,7 +263,7 @@ export default class Block {
if (this.hasUpdateMethod) { if (this.hasUpdateMethod) {
if (this.builders.update.isEmpty()) { if (this.builders.update.isEmpty()) {
properties.addBlock(`update: ${this.generator.helper('noop')},`); properties.addBlock(`update: @noop,`);
} else { } else {
properties.addBlock(deindent` properties.addBlock(deindent`
update: function ( changed, ${this.params.join(', ')} ) { update: function ( changed, ${this.params.join(', ')} ) {
@ -289,20 +276,20 @@ export default class Block {
if (this.hasIntroMethod) { if (this.hasIntroMethod) {
if (hasIntros) { if (hasIntros) {
properties.addBlock(deindent` properties.addBlock(deindent`
intro: function ( ${this.target}, anchor ) { intro: function ( #target, anchor ) {
if ( ${introing} ) return; if ( ${introing} ) return;
${introing} = true; ${introing} = true;
${hasOutros && `${outroing} = false;`} ${hasOutros && `${outroing} = false;`}
${this.builders.intro} ${this.builders.intro}
this.mount( ${this.target}, anchor ); this.mount( #target, anchor );
}, },
`); `);
} else { } else {
properties.addBlock(deindent` properties.addBlock(deindent`
intro: function ( ${this.target}, anchor ) { intro: function ( #target, anchor ) {
this.mount( ${this.target}, anchor ); this.mount( #target, anchor );
}, },
`); `);
} }
@ -331,7 +318,7 @@ export default class Block {
} }
if (this.builders.unmount.isEmpty()) { if (this.builders.unmount.isEmpty()) {
properties.addBlock(`unmount: ${this.generator.helper('noop')},`); properties.addBlock(`unmount: @noop,`);
} else { } else {
properties.addBlock(deindent` properties.addBlock(deindent`
unmount: function () { unmount: function () {
@ -341,7 +328,7 @@ export default class Block {
} }
if (this.builders.destroy.isEmpty()) { if (this.builders.destroy.isEmpty()) {
properties.addBlock(`destroy: ${this.generator.helper('noop')}`); properties.addBlock(`destroy: @noop`);
} else { } else {
properties.addBlock(deindent` properties.addBlock(deindent`
destroy: function () { destroy: function () {
@ -351,15 +338,16 @@ export default class Block {
} }
return deindent` return deindent`
function ${this.name} ( ${this.params.join(', ')}, ${this.component}${this function ${this.name} ( ${this.params.join(', ')}, #component${this.key
.key
? `, ${localKey}` ? `, ${localKey}`
: ''} ) { : ''} ) {
${this.variables.size > 0 && ( ${this.variables.size > 0 &&
`var ${Array.from(this.variables.keys()).map(key => { `var ${Array.from(this.variables.keys())
const init = this.variables.get(key); .map(key => {
return init !== undefined ? `${key} = ${init}` : key; const init = this.variables.get(key);
}).join(', ')};`)} return init !== undefined ? `${key} = ${init}` : key;
})
.join(', ')};`}
${!this.builders.init.isEmpty() && this.builders.init} ${!this.builders.init.isEmpty() && this.builders.init}
@ -367,6 +355,8 @@ export default class Block {
${properties} ${properties}
}; };
} }
`; `.replace(/(\\)?#(\w*)/g, (match, escaped, name) => {
return escaped ? match.slice(1) : this.alias(name);
});
} }
} }

@ -4,6 +4,7 @@ import annotateWithScopes from '../../utils/annotateWithScopes';
import isReference from '../../utils/isReference'; import isReference from '../../utils/isReference';
import { walk } from 'estree-walker'; import { walk } from 'estree-walker';
import deindent from '../../utils/deindent'; import deindent from '../../utils/deindent';
import stringify from '../../utils/stringify';
import CodeBuilder from '../../utils/CodeBuilder'; import CodeBuilder from '../../utils/CodeBuilder';
import visit from './visit'; import visit from './visit';
import shared from './shared'; import shared from './shared';
@ -14,7 +15,6 @@ import { Parsed, CompileOptions, Node } from '../../interfaces';
export class DomGenerator extends Generator { export class DomGenerator extends Generator {
blocks: Block[]; blocks: Block[];
uses: Set<string>;
readonly: Set<string>; readonly: Set<string>;
metaBindings: string[]; metaBindings: string[];
@ -32,7 +32,6 @@ export class DomGenerator extends Generator {
) { ) {
super(parsed, source, name, options); super(parsed, source, name, options);
this.blocks = []; this.blocks = [];
this.uses = new Set();
this.readonly = new Set(); this.readonly = new Set();
@ -41,16 +40,6 @@ export class DomGenerator extends Generator {
// initial values for e.g. window.innerWidth, if there's a <:Window> meta tag // initial values for e.g. window.innerWidth, if there's a <:Window> meta tag
this.metaBindings = []; this.metaBindings = [];
} }
helper(name: string) {
if (this.options.dev && `${name}Dev` in shared) {
name = `${name}Dev`;
}
this.uses.add(name);
return this.alias(name);
}
} }
export default function dom( export default function dom(
@ -76,14 +65,10 @@ export default function dom(
visit(generator, block, state, node, []); visit(generator, block, state, node, []);
}); });
const builders = { const builder = new CodeBuilder();
main: new CodeBuilder(),
_set: new CodeBuilder(),
};
if (computations.length) { if (computations.length) {
const builder = new CodeBuilder(); const computationBuilder = new CodeBuilder();
const differs = generator.helper('differs');
computations.forEach(({ key, deps }) => { computations.forEach(({ key, deps }) => {
if (generator.readonly.has(key)) { if (generator.readonly.has(key)) {
@ -98,26 +83,24 @@ export default function dom(
const condition = `isInitial || ${deps const condition = `isInitial || ${deps
.map( .map(
dep => dep =>
`( '${dep}' in newState && ${differs}( state.${dep}, oldState.${dep} ) )` `( '${dep}' in newState && @differs( state.${dep}, oldState.${dep} ) )`
) )
.join(' || ')}`; .join(' || ')}`;
const statement = `state.${key} = newState.${key} = ${generator.alias( const statement = `state.${key} = newState.${key} = @template.computed.${key}( ${deps
'template' .map(dep => `state.${dep}`)
)}.computed.${key}( ${deps.map(dep => `state.${dep}`).join(', ')} );`; .join(', ')} );`;
builder.addConditionalLine(condition, statement); computationBuilder.addConditionalLine(condition, statement);
}); });
builders.main.addBlock(deindent` builder.addBlock(deindent`
function ${generator.alias( function @recompute ( state, newState, oldState, isInitial ) {
'recompute' ${computationBuilder}
)} ( state, newState, oldState, isInitial ) {
${builder}
} }
`); `);
} }
builders._set.addBlock(deindent` const _set = deindent`
${options.dev && ${options.dev &&
deindent` deindent`
if ( typeof newState !== 'object' ) { if ( typeof newState !== 'object' ) {
@ -131,43 +114,35 @@ export default function dom(
`} `}
var oldState = this._state; var oldState = this._state;
this._state = ${generator.helper('assign')}( {}, oldState, newState ); this._state = @assign( {}, oldState, newState );
${computations.length && ${computations.length &&
`${generator.alias( `@recompute( this._state, newState, oldState, false )`}
'recompute' @dispatchObservers( this, this._observers.pre, newState, oldState );
)}( this._state, newState, oldState, false )`}
${generator.helper(
'dispatchObservers'
)}( this, this._observers.pre, newState, oldState );
${block.hasUpdateMethod && `this._fragment.update( newState, this._state );`} ${block.hasUpdateMethod && `this._fragment.update( newState, this._state );`}
${generator.helper( @dispatchObservers( this, this._observers.post, newState, oldState );
'dispatchObservers'
)}( this, this._observers.post, newState, oldState );
${generator.hasComplexBindings && ${generator.hasComplexBindings &&
`while ( this._bindings.length ) this._bindings.pop()();`} `while ( this._bindings.length ) this._bindings.pop()();`}
${(generator.hasComponents || generator.hasIntroTransitions) && ${(generator.hasComponents || generator.hasIntroTransitions) &&
`this._flush();`} `this._flush();`}
`); `;
if (hasJs) { if (hasJs) {
builders.main.addBlock( builder.addBlock(`[✂${parsed.js.content.start}-${parsed.js.content.end}✂]`);
`[✂${parsed.js.content.start}-${parsed.js.content.end}✂]`
);
} }
if (generator.css && options.css !== false) { if (generator.css && options.css !== false) {
builders.main.addBlock(deindent` builder.addBlock(deindent`
function ${generator.alias('add_css')} () { function @add_css () {
var style = ${generator.helper('createElement')}( 'style' ); var style = @createElement( 'style' );
style.id = ${JSON.stringify(generator.cssId + '-style')}; style.id = '${generator.cssId}-style';
style.textContent = ${JSON.stringify(generator.css)}; style.textContent = ${stringify(generator.css)};
${generator.helper('appendNode')}( style, document.head ); @appendNode( style, document.head );
} }
`); `);
} }
generator.blocks.forEach(block => { generator.blocks.forEach(block => {
builders.main.addBlock(block.render()); builder.addBlock(block.render());
}); });
const sharedPath = options.shared === true const sharedPath = options.shared === true
@ -176,35 +151,28 @@ export default function dom(
const prototypeBase = const prototypeBase =
`${name}.prototype` + `${name}.prototype` +
(templateProperties.methods (templateProperties.methods ? `, @template.methods` : '');
? `, ${generator.alias('template')}.methods`
: '');
const proto = sharedPath const proto = sharedPath
? `${generator.helper('proto')} ` ? `@proto `
: deindent` : deindent`
{ {
${['get', 'fire', 'observe', 'on', 'set', '_flush'] ${['get', 'fire', 'observe', 'on', 'set', '_flush']
.map(n => `${n}: ${generator.helper(n)}`) .map(n => `${n}: @${n}`)
.join(',\n')} .join(',\n')}
}`; }`;
// TODO deprecate component.teardown() // TODO deprecate component.teardown()
builders.main.addBlock(deindent` builder.addBlock(deindent`
function ${name} ( options ) { function ${name} ( options ) {
options = options || {}; options = options || {};
${options.dev && ${options.dev &&
`if ( !options.target && !options._root ) throw new Error( "'target' is a required option" );`} `if ( !options.target && !options._root ) throw new Error( "'target' is a required option" );`}
${generator.usesRefs && `this.refs = {};`} ${generator.usesRefs && `this.refs = {};`}
this._state = ${templateProperties.data this._state = ${templateProperties.data
? `${generator.helper('assign')}( ${generator.alias( ? `@assign( @template.data(), options.data )`
'template'
)}.data(), options.data )`
: `options.data || {}`}; : `options.data || {}`};
${generator.metaBindings} ${generator.metaBindings}
${computations.length && ${computations.length && `@recompute( this._state, this._state, {}, true );`}
`${generator.alias(
'recompute'
)}( this._state, this._state, {}, true );`}
${options.dev && ${options.dev &&
Array.from(generator.expectedProperties).map( Array.from(generator.expectedProperties).map(
prop => prop =>
@ -228,23 +196,19 @@ export default function dom(
this._torndown = false; this._torndown = false;
${generator.css && ${generator.css &&
options.css !== false && options.css !== false &&
`if ( !document.getElementById( ${JSON.stringify( `if ( !document.getElementById( '${generator.cssId}-style' ) ) @add_css();`}
generator.cssId + '-style'
)} ) ) ${generator.alias('add_css')}();`}
${(generator.hasComponents || generator.hasIntroTransitions) && ${(generator.hasComponents || generator.hasIntroTransitions) &&
`this._renderHooks = [];`} `this._renderHooks = [];`}
${generator.hasComplexBindings && `this._bindings = [];`} ${generator.hasComplexBindings && `this._bindings = [];`}
this._fragment = ${generator.alias( this._fragment = @create_main_fragment( this._state, this );
'create_main_fragment'
)}( this._state, this );
if ( options.target ) { if ( options.target ) {
${generator.hydratable ? ${generator.hydratable
deindent` ? deindent`
var nodes = ${generator.helper('children')}( options.target ); var nodes = @children( options.target );
options.hydrate ? this._fragment.claim( nodes ) : this._fragment.create(); options.hydrate ? this._fragment.claim( nodes ) : this._fragment.create();
nodes.forEach( ${generator.helper('detachNode')} ); nodes.forEach( @detachNode );
` : ` :
deindent` deindent`
${options.dev && `if ( options.hydrate ) throw new Error( 'options.hydrate only works if the component was compiled with the \`hydratable: true\` option' );`} ${options.dev && `if ( options.hydrate ) throw new Error( 'options.hydrate only works if the component was compiled with the \`hydratable: true\` option' );`}
@ -261,25 +225,22 @@ export default function dom(
${templateProperties.oncreate && ${templateProperties.oncreate &&
deindent` deindent`
if ( options._root ) { if ( options._root ) {
options._root._renderHooks.push( ${generator.alias( options._root._renderHooks.push( @template.oncreate.bind( this ) );
'template'
)}.oncreate.bind( this ) );
} else { } else {
${generator.alias('template')}.oncreate.call( this ); @template.oncreate.call( this );
} }
`} `}
} }
${generator.helper('assign')}( ${prototypeBase}, ${proto}); @assign( ${prototypeBase}, ${proto});
${name}.prototype._set = function _set ( newState ) { ${name}.prototype._set = function _set ( newState ) {
${builders._set} ${_set}
}; };
${name}.prototype.teardown = ${name}.prototype.destroy = function destroy ( detach ) { ${name}.prototype.teardown = ${name}.prototype.destroy = function destroy ( detach ) {
this.fire( 'destroy' ); this.fire( 'destroy' );
${templateProperties.ondestroy && ${templateProperties.ondestroy && `@template.ondestroy.call( this );`}
`${generator.alias('template')}.ondestroy.call( this );`}
if ( detach !== false ) this._fragment.unmount(); if ( detach !== false ) this._fragment.unmount();
this._fragment.destroy(); this._fragment.destroy();
@ -290,6 +251,21 @@ export default function dom(
}; };
`); `);
const usedHelpers = new Set();
let result = builder
.toString()
.replace(/(\\)?@(\w*)/g, (match: string, escaped: string, name: string) => {
if (escaped) return match.slice(1);
if (name in shared) {
if (options.dev && `${name}Dev` in shared) name = `${name}Dev`;
usedHelpers.add(name);
}
return generator.alias(name);
});
if (sharedPath) { if (sharedPath) {
if (format !== 'es') { if (format !== 'es') {
throw new Error( throw new Error(
@ -297,17 +273,17 @@ export default function dom(
); );
} }
const names = Array.from(generator.uses).sort().map(name => { const names = Array.from(usedHelpers).sort().map(name => {
return name !== generator.alias(name) return name !== generator.alias(name)
? `${name} as ${generator.alias(name)}` ? `${name} as ${generator.alias(name)}`
: name; : name;
}); });
builders.main.addLineAtStart( result =
`import { ${names.join(', ')} } from ${JSON.stringify(sharedPath)};` `import { ${names.join(', ')} } from ${stringify(sharedPath)};\n\n` +
); result;
} else { } else {
generator.uses.forEach(key => { usedHelpers.forEach(key => {
const str = shared[key]; const str = shared[key];
const code = new MagicString(str); const code = new MagicString(str);
const expression = parseExpressionAt(str, 0); const expression = parseExpressionAt(str, 0);
@ -326,7 +302,7 @@ export default function dom(
if (node.name in shared) { if (node.name in shared) {
// this helper function depends on another one // this helper function depends on another one
const dependency = node.name; const dependency = node.name;
generator.uses.add(dependency); usedHelpers.add(dependency);
const alias = generator.alias(dependency); const alias = generator.alias(dependency);
if (alias !== node.name) if (alias !== node.name)
@ -344,22 +320,20 @@ export default function dom(
// special case // special case
const global = `_svelteTransitionManager`; const global = `_svelteTransitionManager`;
builders.main.addBlock( result += `\n\nvar ${generator.alias(
`var ${generator.alias( 'transitionManager'
'transitionManager' )} = window.${global} || ( window.${global} = ${code});`;
)} = window.${global} || ( window.${global} = ${code});`
);
} else { } else {
const alias = generator.alias(expression.id.name); const alias = generator.alias(expression.id.name);
if (alias !== expression.id.name) if (alias !== expression.id.name)
code.overwrite(expression.id.start, expression.id.end, alias); code.overwrite(expression.id.start, expression.id.end, alias);
builders.main.addBlock(code.toString()); result += `\n\n${code}`;
} }
}); });
} }
return generator.generate(builders.main.toString(), options, { return generator.generate(result, options, {
name, name,
format, format,
}); });

@ -12,7 +12,12 @@ function isElseIf(node: Node) {
} }
function getChildState(parent: State, child = {}) { function getChildState(parent: State, child = {}) {
return assign({}, parent, { name: null, parentNode: null, parentNodes: 'nodes' }, child || {}); return assign(
{},
parent,
{ name: null, parentNode: null, parentNodes: 'nodes' },
child || {}
);
} }
// Whitespace inside one of these elements will not result in // Whitespace inside one of these elements will not result in
@ -34,7 +39,8 @@ const preprocessors = {
generator: DomGenerator, generator: DomGenerator,
block: Block, block: Block,
state: State, state: State,
node: Node node: Node,
stripWhitespace: boolean
) => { ) => {
const dependencies = block.findDependencies(node.expression); const dependencies = block.findDependencies(node.expression);
block.addDependencies(dependencies); block.addDependencies(dependencies);
@ -48,7 +54,8 @@ const preprocessors = {
generator: DomGenerator, generator: DomGenerator,
block: Block, block: Block,
state: State, state: State,
node: Node node: Node,
stripWhitespace: boolean
) => { ) => {
const dependencies = block.findDependencies(node.expression); const dependencies = block.findDependencies(node.expression);
block.addDependencies(dependencies); block.addDependencies(dependencies);
@ -59,7 +66,7 @@ const preprocessors = {
node._state = getChildState(state, { basename, name }); node._state = getChildState(state, { basename, name });
}, },
Text: (generator: DomGenerator, block: Block, state: State, node: Node) => { Text: (generator: DomGenerator, block: Block, state: State, node: Node, stripWhitespace: boolean) => {
node._state = getChildState(state); node._state = getChildState(state);
if (!/\S/.test(node.data)) { if (!/\S/.test(node.data)) {
@ -75,7 +82,9 @@ const preprocessors = {
generator: DomGenerator, generator: DomGenerator,
block: Block, block: Block,
state: State, state: State,
node: Node node: Node,
stripWhitespace: boolean,
nextSibling: Node
) => { ) => {
const blocks: Block[] = []; const blocks: Block[] = [];
let dynamic = false; let dynamic = false;
@ -93,7 +102,7 @@ const preprocessors = {
node._state = getChildState(state); node._state = getChildState(state);
blocks.push(node._block); blocks.push(node._block);
preprocessChildren(generator, node._block, node._state, node); preprocessChildren(generator, node._block, node._state, node, stripWhitespace, node);
if (node._block.dependencies.size > 0) { if (node._block.dependencies.size > 0) {
dynamic = true; dynamic = true;
@ -117,7 +126,9 @@ const preprocessors = {
generator, generator,
node.else._block, node.else._block,
node.else._state, node.else._state,
node.else node.else,
stripWhitespace,
nextSibling
); );
if (node.else._block.dependencies.size > 0) { if (node.else._block.dependencies.size > 0) {
@ -142,7 +153,9 @@ const preprocessors = {
generator: DomGenerator, generator: DomGenerator,
block: Block, block: Block,
state: State, state: State,
node: Node node: Node,
stripWhitespace: boolean,
nextSibling: Node
) => { ) => {
const dependencies = block.findDependencies(node.expression); const dependencies = block.findDependencies(node.expression);
block.addDependencies(dependencies); block.addDependencies(dependencies);
@ -189,7 +202,7 @@ const preprocessors = {
}); });
generator.blocks.push(node._block); generator.blocks.push(node._block);
preprocessChildren(generator, node._block, node._state, node); preprocessChildren(generator, node._block, node._state, node, stripWhitespace, nextSibling);
block.addDependencies(node._block.dependencies); block.addDependencies(node._block.dependencies);
node._block.hasUpdateMethod = node._block.dependencies.size > 0; node._block.hasUpdateMethod = node._block.dependencies.size > 0;
@ -205,7 +218,9 @@ const preprocessors = {
generator, generator,
node.else._block, node.else._block,
node.else._state, node.else._state,
node.else node.else,
stripWhitespace,
nextSibling
); );
node.else._block.hasUpdateMethod = node.else._block.dependencies.size > 0; node.else._block.hasUpdateMethod = node.else._block.dependencies.size > 0;
} }
@ -215,7 +230,9 @@ const preprocessors = {
generator: DomGenerator, generator: DomGenerator,
block: Block, block: Block,
state: State, state: State,
node: Node node: Node,
stripWhitespace: boolean,
nextSibling: Node
) => { ) => {
node.attributes.forEach((attribute: Node) => { node.attributes.forEach((attribute: Node) => {
if (attribute.type === 'Attribute' && attribute.value !== true) { if (attribute.type === 'Attribute' && attribute.value !== true) {
@ -225,7 +242,11 @@ const preprocessors = {
block.addDependencies(dependencies); block.addDependencies(dependencies);
// special case — <option value='{{foo}}'> — see below // special case — <option value='{{foo}}'> — see below
if (node.name === 'option' && attribute.name === 'value' && state.selectBindingDependencies) { if (
node.name === 'option' &&
attribute.name === 'value' &&
state.selectBindingDependencies
) {
state.selectBindingDependencies.forEach(prop => { state.selectBindingDependencies.forEach(prop => {
dependencies.forEach((dependency: string) => { dependencies.forEach((dependency: string) => {
generator.indirectDependencies.get(prop).add(dependency); generator.indirectDependencies.get(prop).add(dependency);
@ -258,7 +279,9 @@ const preprocessors = {
// so that if `foo.qux` changes, we know that we need to // so that if `foo.qux` changes, we know that we need to
// mark `bar` and `baz` as dirty too // mark `bar` and `baz` as dirty too
if (node.name === 'select') { if (node.name === 'select') {
const value = node.attributes.find((attribute: Node) => attribute.name === 'value'); const value = node.attributes.find(
(attribute: Node) => attribute.name === 'value'
);
if (value) { if (value) {
// TODO does this also apply to e.g. `<input type='checkbox' bind:group='foo'>`? // TODO does this also apply to e.g. `<input type='checkbox' bind:group='foo'>`?
const dependencies = block.findDependencies(value.value); const dependencies = block.findDependencies(value.value);
@ -305,11 +328,12 @@ const preprocessors = {
}); });
generator.blocks.push(node._block); generator.blocks.push(node._block);
preprocessChildren(generator, node._block, node._state, node); preprocessChildren(generator, node._block, node._state, node, stripWhitespace, nextSibling);
block.addDependencies(node._block.dependencies); block.addDependencies(node._block.dependencies);
node._block.hasUpdateMethod = node._block.dependencies.size > 0; node._block.hasUpdateMethod = node._block.dependencies.size > 0;
} else { } else {
preprocessChildren(generator, block, node._state, node); if (node.name === 'pre' || node.name === 'textarea') stripWhitespace = false;
preprocessChildren(generator, block, node._state, node, stripWhitespace, nextSibling);
} }
} }
}, },
@ -320,7 +344,8 @@ function preprocessChildren(
block: Block, block: Block,
state: State, state: State,
node: Node, node: Node,
isTopLevel: boolean = false stripWhitespace: boolean,
nextSibling: Node
) { ) {
// glue text nodes together // glue text nodes together
const cleaned: Node[] = []; const cleaned: Node[] = [];
@ -333,32 +358,22 @@ function preprocessChildren(
lastChild.data += child.data; lastChild.data += child.data;
lastChild.end = child.end; lastChild.end = child.end;
} else { } else {
cleaned.push(child); if (child.type === 'Text' && stripWhitespace && cleaned.length === 0) {
child.data = trimStart(child.data);
if (child.data) cleaned.push(child);
} else {
cleaned.push(child);
}
} }
lastChild = child; lastChild = child;
}); });
if (isTopLevel) {
// trim leading and trailing whitespace from the top level
const firstChild = cleaned[0];
if (firstChild && firstChild.type === 'Text') {
firstChild.data = trimStart(firstChild.data);
if (!firstChild.data) cleaned.shift();
}
const lastChild = cleaned[cleaned.length - 1];
if (lastChild && lastChild.type === 'Text') {
lastChild.data = trimEnd(lastChild.data);
if (!lastChild.data) cleaned.pop();
}
}
lastChild = null; lastChild = null;
cleaned.forEach((child: Node) => { cleaned.forEach((child: Node, i: number) => {
const preprocess = preprocessors[child.type]; const preprocessor = preprocessors[child.type];
if (preprocess) preprocess(generator, block, state, child); if (preprocessor) preprocessor(generator, block, state, child, stripWhitespace, cleaned[i + 1] || nextSibling);
if (lastChild) { if (lastChild) {
lastChild.next = child; lastChild.next = child;
@ -368,6 +383,19 @@ function preprocessChildren(
lastChild = child; lastChild = child;
}); });
// We want to remove trailing whitespace inside an element/component/block,
// *unless* there is no whitespace between this node and its next sibling
if (lastChild && lastChild.type === 'Text') {
if (stripWhitespace && (!nextSibling || (nextSibling.type === 'Text' && /^\s/.test(nextSibling.data)))) {
lastChild.data = trimEnd(lastChild.data);
if (!lastChild.data) {
cleaned.pop();
lastChild = cleaned[cleaned.length - 1];
lastChild.next = null;
}
}
}
if (lastChild) { if (lastChild) {
lastChild.needsAnchor = !state.parentNode; lastChild.needsAnchor = !state.parentNode;
} }
@ -382,7 +410,7 @@ export default function preprocess(
) { ) {
const block = new Block({ const block = new Block({
generator, generator,
name: generator.alias('create_main_fragment'), name: '@create_main_fragment',
key: null, key: null,
contexts: new Map(), contexts: new Map(),
@ -404,7 +432,7 @@ export default function preprocess(
}; };
generator.blocks.push(block); generator.blocks.push(block);
preprocessChildren(generator, block, state, node, true); preprocessChildren(generator, block, state, node, true, null);
block.hasUpdateMethod = block.dependencies.size > 0; block.hasUpdateMethod = block.dependencies.size > 0;
return { block, state }; return { block, state };

@ -2,6 +2,7 @@ import { DomGenerator } from '../../index';
import Block from '../../Block'; import Block from '../../Block';
import { Node } from '../../../../interfaces'; import { Node } from '../../../../interfaces';
import { State } from '../../interfaces'; import { State } from '../../interfaces';
import stringify from '../../../../utils/stringify';
export default function visitAttribute( export default function visitAttribute(
generator: DomGenerator, generator: DomGenerator,
@ -27,9 +28,7 @@ export default function visitAttribute(
if (value.type === 'Text') { if (value.type === 'Text') {
// static attributes // static attributes
const result = isNaN(value.data) const result = isNaN(value.data) ? stringify(value.data) : value.data;
? JSON.stringify(value.data)
: value.data;
local.staticAttributes.push({ local.staticAttributes.push({
name: attribute.name, name: attribute.name,
value: result, value: result,
@ -54,7 +53,7 @@ export default function visitAttribute(
attribute.value attribute.value
.map(chunk => { .map(chunk => {
if (chunk.type === 'Text') { if (chunk.type === 'Text') {
return JSON.stringify(chunk.data); return stringify(chunk.data);
} else { } else {
const { dependencies, snippet } = block.contextualise( const { dependencies, snippet } = block.contextualise(
chunk.expression chunk.expression

@ -66,16 +66,14 @@ export default function visitBinding(
block.addVariable(updating, 'false'); block.addVariable(updating, 'false');
local.create.addBlock(deindent` local.create.addBlock(deindent`
${block.component}._bindings.push( function () { #component._bindings.push( function () {
if ( ${local.name}._torndown ) return; if ( ${local.name}._torndown ) return;
${local.name}.observe( '${attribute.name}', function ( value ) { ${local.name}.observe( '${attribute.name}', function ( value ) {
if ( ${updating} ) return; if ( ${updating} ) return;
${updating} = true; ${updating} = true;
${setter} ${setter}
${updating} = false; ${updating} = false;
}, { init: ${generator.helper( }, { init: @differs( ${local.name}.get( '${attribute.name}' ), ${snippet} ) });
'differs'
)}( ${local.name}.get( '${attribute.name}' ), ${snippet} ) });
}); });
`); `);

@ -113,9 +113,7 @@ export default function visitComponent(
local.update.addBlock(updates); local.update.addBlock(updates);
} }
const componentInitProperties = [ const componentInitProperties = [`_root: #component._root`];
`_root: ${block.component}._root`,
];
// Component has children, put them in a separate {{yield}} block // Component has children, put them in a separate {{yield}} block
if (hasChildren) { if (hasChildren) {
@ -130,12 +128,10 @@ export default function visitComponent(
const yieldFragment = block.getUniqueName(`${name}_yield_fragment`); const yieldFragment = block.getUniqueName(`${name}_yield_fragment`);
block.builders.init.addLine( block.builders.init.addLine(
`var ${yieldFragment} = ${childBlock.name}( ${params}, ${block.component} );` `var ${yieldFragment} = ${childBlock.name}( ${params}, #component );`
); );
block.builders.create.addLine( block.builders.create.addLine(`${yieldFragment}.create();`);
`${yieldFragment}.create();`
);
block.builders.claim.addLine( block.builders.claim.addLine(
`${yieldFragment}.claim( ${state.parentNodes} );` `${yieldFragment}.claim( ${state.parentNodes} );`
@ -185,7 +181,7 @@ export default function visitComponent(
const expression = node.name === ':Self' const expression = node.name === ':Self'
? generator.name ? generator.name
: generator.importedComponents.get(node.name) || : generator.importedComponents.get(node.name) ||
`${generator.alias('template')}.components.${node.name}`; `@template.components.${node.name}`;
local.create.addBlockAtStart(deindent` local.create.addBlockAtStart(deindent`
${statements.join('\n')} ${statements.join('\n')}
@ -226,12 +222,16 @@ export default function visitComponent(
block.builders.init.addBlock(local.create); block.builders.init.addBlock(local.create);
const targetNode = state.parentNode || block.target; const targetNode = state.parentNode || '#target';
const anchorNode = state.parentNode ? 'null' : 'anchor'; const anchorNode = state.parentNode ? 'null' : 'anchor';
block.builders.create.addLine(`${name}._fragment.create();`); block.builders.create.addLine(`${name}._fragment.create();`);
block.builders.claim.addLine(`${name}._fragment.claim( ${state.parentNodes} );`); block.builders.claim.addLine(
block.builders.mount.addLine(`${name}._fragment.mount( ${targetNode}, ${anchorNode} );` ); `${name}._fragment.claim( ${state.parentNodes} );`
);
block.builders.mount.addLine(
`${name}._fragment.mount( ${targetNode}, ${anchorNode} );`
);
if (!local.update.isEmpty()) block.builders.update.addBlock(local.update); if (!local.update.isEmpty()) block.builders.update.addBlock(local.update);
} }

@ -16,7 +16,7 @@ export default function visitEventHandler(
generator.addSourcemapLocations(attribute.expression); generator.addSourcemapLocations(attribute.expression);
generator.code.prependRight( generator.code.prependRight(
attribute.expression.start, attribute.expression.start,
`${block.component}.` `${block.alias('component')}.`
); );
const usedContexts: string[] = []; const usedContexts: string[] = [];

@ -14,11 +14,9 @@ export default function visitRef(
) { ) {
generator.usesRefs = true; generator.usesRefs = true;
local.create.addLine( local.create.addLine(`#component.refs.${attribute.name} = ${local.name};`);
`${block.component}.refs.${attribute.name} = ${local.name};`
);
block.builders.destroy.addLine(deindent` block.builders.destroy.addLine(deindent`
if ( ${block.component}.refs.${attribute.name} === ${local.name} ) ${block.component}.refs.${attribute.name} = null; if ( #component.refs.${attribute.name} === ${local.name} ) #component.refs.${attribute.name} = null;
`); `);
} }

@ -16,7 +16,6 @@ export default function visitEachBlock(
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;
const iterations = block.getUniqueName(`${each_block}_iterations`); const iterations = block.getUniqueName(`${each_block}_iterations`);
const i = block.alias(`i`);
const params = block.params.join(', '); const params = block.params.join(', ');
const anchor = node.needsAnchor const anchor = node.needsAnchor
? block.getUniqueName(`${each_block}_anchor`) ? block.getUniqueName(`${each_block}_anchor`)
@ -28,7 +27,6 @@ export default function visitEachBlock(
create_each_block, create_each_block,
each_block_value, each_block_value,
iterations, iterations,
i,
params, params,
anchor, anchor,
mountOrIntro, mountOrIntro,
@ -49,8 +47,8 @@ export default function visitEachBlock(
if (node.needsAnchor) { if (node.needsAnchor) {
block.addElement( block.addElement(
anchor, anchor,
`${generator.helper('createComment')}()`, `@createComment()`,
`${generator.helper('createComment')}()`, `@createComment()`,
state.parentNode, state.parentNode,
true true
); );
@ -66,14 +64,15 @@ export default function visitEachBlock(
// TODO neaten this up... will end up with an empty line in the block // TODO neaten this up... will end up with an empty line in the block
block.builders.init.addBlock(deindent` block.builders.init.addBlock(deindent`
if ( !${each_block_value}.length ) { if ( !${each_block_value}.length ) {
${each_block_else} = ${node.else._block.name}( ${params}, ${block.component} ); ${each_block_else} = ${node.else._block.name}( ${params}, #component );
${each_block_else}.create(); ${each_block_else}.create();
} }
`); `);
block.builders.mount.addBlock(deindent` block.builders.mount.addBlock(deindent`
if ( ${each_block_else} ) { if ( ${each_block_else} ) {
${each_block_else}.${mountOrIntro}( ${state.parentNode || block.target}, null ); ${each_block_else}.${mountOrIntro}( ${state.parentNode ||
'#target'}, null );
} }
`); `);
@ -84,7 +83,7 @@ export default function visitEachBlock(
if ( !${each_block_value}.length && ${each_block_else} ) { if ( !${each_block_value}.length && ${each_block_else} ) {
${each_block_else}.update( changed, ${params} ); ${each_block_else}.update( changed, ${params} );
} else if ( !${each_block_value}.length ) { } else if ( !${each_block_value}.length ) {
${each_block_else} = ${node.else._block.name}( ${params}, ${block.component} ); ${each_block_else} = ${node.else._block.name}( ${params}, #component );
${each_block_else}.create(); ${each_block_else}.create();
${each_block_else}.${mountOrIntro}( ${parentNode}, ${anchor} ); ${each_block_else}.${mountOrIntro}( ${parentNode}, ${anchor} );
} else if ( ${each_block_else} ) { } else if ( ${each_block_else} ) {
@ -102,7 +101,7 @@ export default function visitEachBlock(
${each_block_else} = null; ${each_block_else} = null;
} }
} else if ( !${each_block_else} ) { } else if ( !${each_block_else} ) {
${each_block_else} = ${node.else._block.name}( ${params}, ${block.component} ); ${each_block_else} = ${node.else._block.name}( ${params}, #component );
${each_block_else}.create(); ${each_block_else}.create();
${each_block_else}.${mountOrIntro}( ${parentNode}, ${anchor} ); ${each_block_else}.${mountOrIntro}( ${parentNode}, ${anchor} );
} }
@ -139,7 +138,6 @@ function keyed(
each_block, each_block,
create_each_block, create_each_block,
each_block_value, each_block_value,
i,
params, params,
anchor, anchor,
mountOrIntro, mountOrIntro,
@ -163,27 +161,27 @@ function keyed(
node._block.first = node._block.getUniqueName('first'); node._block.first = node._block.getUniqueName('first');
node._block.addElement( node._block.addElement(
node._block.first, node._block.first,
`${generator.helper('createComment')}()`, `@createComment()`,
`${generator.helper('createComment')}()`, `@createComment()`,
null, null,
true true
); );
} }
block.builders.init.addBlock(deindent` block.builders.init.addBlock(deindent`
for ( var ${i} = 0; ${i} < ${each_block_value}.length; ${i} += 1 ) { for ( var #i = 0; #i < ${each_block_value}.length; #i += 1 ) {
var ${key} = ${each_block_value}[${i}].${node.key}; var ${key} = ${each_block_value}[#i].${node.key};
var ${iteration} = ${lookup}[${key}] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i}, ${block.component}, ${key} ); var ${iteration} = ${lookup}[${key}] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[#i], #i, #component, ${key} );
if ( ${last} ) ${last}.next = ${iteration}; if ( ${last} ) ${last}.next = ${iteration};
${iteration}.last = ${last}; ${iteration}.last = ${last};
${last} = ${iteration}; ${last} = ${iteration};
if ( ${i} === 0 ) ${head} = ${iteration}; if ( #i === 0 ) ${head} = ${iteration};
} }
`); `);
const targetNode = state.parentNode || block.target; const targetNode = state.parentNode || '#target';
const anchorNode = state.parentNode ? 'null' : 'anchor'; const anchorNode = state.parentNode ? 'null' : 'anchor';
block.builders.create.addBlock(deindent` block.builders.create.addBlock(deindent`
@ -232,9 +230,9 @@ function keyed(
${expected} = ${expected}.next; ${expected} = ${expected}.next;
} }
for ( ${i} = 0; ${i} < discard_pile.length; ${i} += 1 ) { for ( #i = 0; #i < discard_pile.length; #i += 1 ) {
if ( discard_pile[${i}].discard ) { if ( discard_pile[#i].discard ) {
${fn}( discard_pile[${i}] ); ${fn}( discard_pile[#i] );
} }
} }
`; `;
@ -254,8 +252,8 @@ function keyed(
${expected} = ${expected}.next; ${expected} = ${expected}.next;
} }
for ( ${i} = 0; ${i} < discard_pile.length; ${i} += 1 ) { for ( #i = 0; #i < discard_pile.length; #i += 1 ) {
var ${iteration} = discard_pile[${i}]; var ${iteration} = discard_pile[#i];
if ( ${iteration}.discard ) { if ( ${iteration}.discard ) {
${fn}( ${iteration} ); ${fn}( ${iteration} );
} }
@ -271,12 +269,12 @@ function keyed(
var discard_pile = []; var discard_pile = [];
for ( ${i} = 0; ${i} < ${each_block_value}.length; ${i} += 1 ) { for ( #i = 0; #i < ${each_block_value}.length; #i += 1 ) {
var ${key} = ${each_block_value}[${i}].${node.key}; var ${key} = ${each_block_value}[#i].${node.key};
var ${iteration} = ${lookup}[${key}]; var ${iteration} = ${lookup}[${key}];
${dynamic && ${dynamic &&
`if ( ${iteration} ) ${iteration}.update( changed, ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i} );`} `if ( ${iteration} ) ${iteration}.update( changed, ${params}, ${each_block_value}, ${each_block_value}[#i], #i );`}
if ( ${expected} ) { if ( ${expected} ) {
if ( ${key} === ${expected}.key ) { if ( ${key} === ${expected}.key ) {
@ -297,7 +295,7 @@ function keyed(
if (!${expected}) ${iteration}.mount( ${parentNode}, ${anchor} ); if (!${expected}) ${iteration}.mount( ${parentNode}, ${anchor} );
} else { } else {
// key is being inserted // key is being inserted
${iteration} = ${lookup}[${key}] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i}, ${block.component}, ${key} ); ${iteration} = ${lookup}[${key}] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[#i], #i, #component, ${key} );
${iteration}.create(); ${iteration}.create();
${iteration}.${mountOrIntro}( ${parentNode}, ${expected}.first ); ${iteration}.${mountOrIntro}( ${parentNode}, ${expected}.first );
@ -312,7 +310,7 @@ function keyed(
${iteration}.next = null; ${iteration}.next = null;
${iteration}.mount( ${parentNode}, ${anchor} ); ${iteration}.mount( ${parentNode}, ${anchor} );
} else { } else {
${iteration} = ${lookup}[${key}] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i}, ${block.component}, ${key} ); ${iteration} = ${lookup}[${key}] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[#i], #i, #component, ${key} );
${iteration}.create(); ${iteration}.create();
${iteration}.${mountOrIntro}( ${parentNode}, ${anchor} ); ${iteration}.${mountOrIntro}( ${parentNode}, ${anchor} );
} }
@ -361,7 +359,6 @@ function unkeyed(
create_each_block, create_each_block,
each_block_value, each_block_value,
iterations, iterations,
i,
params, params,
anchor, anchor,
mountOrIntro, mountOrIntro,
@ -370,29 +367,29 @@ function unkeyed(
block.builders.init.addBlock(deindent` block.builders.init.addBlock(deindent`
var ${iterations} = []; var ${iterations} = [];
for ( var ${i} = 0; ${i} < ${each_block_value}.length; ${i} += 1 ) { for ( var #i = 0; #i < ${each_block_value}.length; #i += 1 ) {
${iterations}[${i}] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i}, ${block.component} ); ${iterations}[#i] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[#i], #i, #component );
} }
`); `);
const targetNode = state.parentNode || block.target; const targetNode = state.parentNode || '#target';
const anchorNode = state.parentNode ? 'null' : 'anchor'; const anchorNode = state.parentNode ? 'null' : 'anchor';
block.builders.create.addBlock(deindent` block.builders.create.addBlock(deindent`
for ( var ${i} = 0; ${i} < ${iterations}.length; ${i} += 1 ) { for ( var #i = 0; #i < ${iterations}.length; #i += 1 ) {
${iterations}[${i}].create(); ${iterations}[#i].create();
} }
`); `);
block.builders.claim.addBlock(deindent` block.builders.claim.addBlock(deindent`
for ( var ${i} = 0; ${i} < ${iterations}.length; ${i} += 1 ) { for ( var #i = 0; #i < ${iterations}.length; #i += 1 ) {
${iterations}[${i}].claim( ${state.parentNodes} ); ${iterations}[#i].claim( ${state.parentNodes} );
} }
`); `);
block.builders.mount.addBlock(deindent` block.builders.mount.addBlock(deindent`
for ( var ${i} = 0; ${i} < ${iterations}.length; ${i} += 1 ) { for ( var #i = 0; #i < ${iterations}.length; #i += 1 ) {
${iterations}[${i}].${mountOrIntro}( ${targetNode}, ${anchorNode} ); ${iterations}[#i].${mountOrIntro}( ${targetNode}, ${anchorNode} );
} }
`); `);
@ -413,26 +410,26 @@ function unkeyed(
const forLoopBody = node._block.hasUpdateMethod const forLoopBody = node._block.hasUpdateMethod
? node._block.hasIntroMethod ? node._block.hasIntroMethod
? deindent` ? deindent`
if ( ${iterations}[${i}] ) { if ( ${iterations}[#i] ) {
${iterations}[${i}].update( changed, ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i} ); ${iterations}[#i].update( changed, ${params}, ${each_block_value}, ${each_block_value}[#i], #i );
} else { } else {
${iterations}[${i}] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i}, ${block.component} ); ${iterations}[#i] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[#i], #i, #component );
${iterations}[${i}].create(); ${iterations}[#i].create();
} }
${iterations}[${i}].intro( ${parentNode}, ${anchor} ); ${iterations}[#i].intro( ${parentNode}, ${anchor} );
` `
: deindent` : deindent`
if ( ${iterations}[${i}] ) { if ( ${iterations}[#i] ) {
${iterations}[${i}].update( changed, ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i} ); ${iterations}[#i].update( changed, ${params}, ${each_block_value}, ${each_block_value}[#i], #i );
} else { } else {
${iterations}[${i}] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i}, ${block.component} ); ${iterations}[#i] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[#i], #i, #component );
${iterations}[${i}].create(); ${iterations}[#i].create();
${iterations}[${i}].mount( ${parentNode}, ${anchor} ); ${iterations}[#i].mount( ${parentNode}, ${anchor} );
} }
` `
: deindent` : deindent`
${iterations}[${i}] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[${i}], ${i}, ${block.component} ); ${iterations}[#i] = ${create_each_block}( ${params}, ${each_block_value}, ${each_block_value}[#i], #i, #component );
${iterations}[${i}].${mountOrIntro}( ${parentNode}, ${anchor} ); ${iterations}[#i].${mountOrIntro}( ${parentNode}, ${anchor} );
`; `;
const start = node._block.hasUpdateMethod ? '0' : `${iterations}.length`; const start = node._block.hasUpdateMethod ? '0' : `${iterations}.length`;
@ -450,12 +447,12 @@ function unkeyed(
} }
} }
for ( ; ${i} < ${iterations}.length; ${i} += 1 ) ${outro}( ${i} ); for ( ; #i < ${iterations}.length; #i += 1 ) ${outro}( #i );
` `
: deindent` : deindent`
for ( ; ${i} < ${iterations}.length; ${i} += 1 ) { for ( ; #i < ${iterations}.length; #i += 1 ) {
${iterations}[${i}].unmount(); ${iterations}[#i].unmount();
${iterations}[${i}].destroy(); ${iterations}[#i].destroy();
} }
${iterations}.length = ${each_block_value}.length; ${iterations}.length = ${each_block_value}.length;
`; `;
@ -464,7 +461,7 @@ function unkeyed(
var ${each_block_value} = ${snippet}; var ${each_block_value} = ${snippet};
if ( ${condition} ) { if ( ${condition} ) {
for ( var ${i} = ${start}; ${i} < ${each_block_value}.length; ${i} += 1 ) { for ( var #i = ${start}; #i < ${each_block_value}.length; #i += 1 ) {
${forLoopBody} ${forLoopBody}
} }
@ -474,12 +471,10 @@ function unkeyed(
} }
block.builders.unmount.addBlock(deindent` block.builders.unmount.addBlock(deindent`
for ( var ${i} = 0; ${i} < ${iterations}.length; ${i} += 1 ) { for ( var #i = 0; #i < ${iterations}.length; #i += 1 ) {
${iterations}[${i}].unmount(); ${iterations}[#i].unmount();
} }
`); `);
block.builders.destroy.addBlock( block.builders.destroy.addBlock(`@destroyEach( ${iterations}, false, 0 );`);
`${generator.helper('destroyEach')}( ${iterations}, false, 0 );`
);
} }

@ -1,5 +1,6 @@
import attributeLookup from './lookup'; import attributeLookup from './lookup';
import deindent from '../../../../utils/deindent'; import deindent from '../../../../utils/deindent';
import stringify from '../../../../utils/stringify';
import getStaticAttributeValue from './getStaticAttributeValue'; import getStaticAttributeValue from './getStaticAttributeValue';
import { DomGenerator } from '../../index'; import { DomGenerator } from '../../index';
import Block from '../../Block'; import Block from '../../Block';
@ -36,8 +37,8 @@ export default function visitAttribute(
// namespaced attributes but I'm not sure that's applicable in // namespaced attributes but I'm not sure that's applicable in
// HTML5? // HTML5?
const method = name.slice(0, 6) === 'xlink:' const method = name.slice(0, 6) === 'xlink:'
? 'setXlinkAttribute' ? '@setXlinkAttribute'
: 'setAttribute'; : '@setAttribute';
const isDynamic = const isDynamic =
(attribute.value !== true && attribute.value.length > 1) || (attribute.value !== true && attribute.value.length > 1) ||
@ -57,7 +58,7 @@ export default function visitAttribute(
attribute.value attribute.value
.map((chunk: Node) => { .map((chunk: Node) => {
if (chunk.type === 'Text') { if (chunk.type === 'Text') {
return JSON.stringify(chunk.data); return stringify(chunk.data);
} else { } else {
const { snippet } = block.contextualise(chunk.expression); const { snippet } = block.contextualise(chunk.expression);
return `( ${snippet} )`; return `( ${snippet} )`;
@ -80,7 +81,9 @@ export default function visitAttribute(
// annoying special case // annoying special case
const isMultipleSelect = const isMultipleSelect =
node.name === 'select' && node.name === 'select' &&
node.attributes.find((attr: Node) => attr.name.toLowerCase() === 'multiple'); // TODO use getStaticAttributeValue node.attributes.find(
(attr: Node) => attr.name.toLowerCase() === 'multiple'
); // TODO use getStaticAttributeValue
const i = block.getUniqueName('i'); const i = block.getUniqueName('i');
const option = block.getUniqueName('option'); const option = block.getUniqueName('option');
@ -112,13 +115,9 @@ export default function visitAttribute(
updater = `${state.parentNode}.${propertyName} = ${last};`; updater = `${state.parentNode}.${propertyName} = ${last};`;
} else { } else {
block.builders.hydrate.addLine( block.builders.hydrate.addLine(
`${generator.helper( `${method}( ${state.parentNode}, '${name}', ${last} = ${value} );`
method
)}( ${state.parentNode}, '${name}', ${last} = ${value} );`
); );
updater = `${generator.helper( updater = `${method}( ${state.parentNode}, '${name}', ${last} );`;
method
)}( ${state.parentNode}, '${name}', ${last} );`;
} }
block.builders.update.addBlock(deindent` block.builders.update.addBlock(deindent`
@ -131,13 +130,11 @@ export default function visitAttribute(
? 'true' ? 'true'
: attribute.value.length === 0 : attribute.value.length === 0
? `''` ? `''`
: JSON.stringify(attribute.value[0].data); : stringify(attribute.value[0].data);
const statement = propertyName const statement = propertyName
? `${state.parentNode}.${propertyName} = ${value};` ? `${state.parentNode}.${propertyName} = ${value};`
: `${generator.helper( : `${method}( ${state.parentNode}, '${name}', ${value} );`;
method
)}( ${state.parentNode}, '${name}', ${value} );`;
block.builders.hydrate.addLine(statement); block.builders.hydrate.addLine(statement);

@ -16,7 +16,9 @@ export default function visitBinding(
attribute: Node attribute: Node
) { ) {
const { name } = getObject(attribute.value); const { name } = getObject(attribute.value);
const { snippet, contexts, dependencies } = block.contextualise(attribute.value); const { snippet, contexts, dependencies } = block.contextualise(
attribute.value
);
contexts.forEach(context => { contexts.forEach(context => {
if (!~state.allUsedContexts.indexOf(context)) if (!~state.allUsedContexts.indexOf(context))
@ -57,7 +59,7 @@ export default function visitBinding(
value, value,
}); });
let updateElement = `${state.parentNode}.${attribute.name} = ${snippet};`; let updateElement = `${state.parentNode}.${attribute.name} = ${snippet};`;
const lock = block.alias(`${state.parentNode}_updating`); const lock = `#${state.parentNode}_updating`;
let updateCondition = `!${lock}`; let updateCondition = `!${lock}`;
block.addVariable(lock, 'false'); block.addVariable(lock, 'false');
@ -69,7 +71,6 @@ export default function visitBinding(
} }
const value = block.getUniqueName('value'); const value = block.getUniqueName('value');
const i = block.alias('i');
const option = block.getUniqueName('option'); const option = block.getUniqueName('option');
const ifStatement = isMultipleSelect const ifStatement = isMultipleSelect
@ -83,8 +84,8 @@ export default function visitBinding(
updateElement = deindent` updateElement = deindent`
var ${value} = ${snippet}; var ${value} = ${snippet};
for ( var ${i} = 0; ${i} < ${state.parentNode}.options.length; ${i} += 1 ) { for ( var #i = 0; #i < ${state.parentNode}.options.length; #i += 1 ) {
var ${option} = ${state.parentNode}.options[${i}]; var ${option} = ${state.parentNode}.options[#i];
${ifStatement} ${ifStatement}
} }
@ -92,7 +93,7 @@ export default function visitBinding(
generator.hasComplexBindings = true; generator.hasComplexBindings = true;
block.builders.hydrate.addBlock( block.builders.hydrate.addBlock(
`if ( !('${name}' in state) ) ${block.component}._bindings.push( ${handler} );` `if ( !('${name}' in state) ) #component._bindings.push( ${handler} );`
); );
} else if (attribute.name === 'group') { } else if (attribute.name === 'group') {
// <input type='checkbox|radio' bind:group='selected'> special case // <input type='checkbox|radio' bind:group='selected'> special case
@ -108,19 +109,17 @@ export default function visitBinding(
: `${state.parentNode}.__value === ${snippet}`; : `${state.parentNode}.__value === ${snippet}`;
block.builders.hydrate.addLine( block.builders.hydrate.addLine(
`${block.component}._bindingGroups[${bindingGroup}].push( ${state.parentNode} );` `#component._bindingGroups[${bindingGroup}].push( ${state.parentNode} );`
); );
block.builders.destroy.addBlock( block.builders.destroy.addBlock(
`${block.component}._bindingGroups[${bindingGroup}].splice( ${block.component}._bindingGroups[${bindingGroup}].indexOf( ${state.parentNode} ), 1 );` `#component._bindingGroups[${bindingGroup}].splice( #component._bindingGroups[${bindingGroup}].indexOf( ${state.parentNode} ), 1 );`
); );
updateElement = `${state.parentNode}.checked = ${condition};`; updateElement = `${state.parentNode}.checked = ${condition};`;
} else if (node.name === 'audio' || node.name === 'video') { } else if (node.name === 'audio' || node.name === 'video') {
generator.hasComplexBindings = true; generator.hasComplexBindings = true;
block.builders.hydrate.addBlock( block.builders.hydrate.addBlock(`#component._bindings.push( ${handler} );`);
`${block.component}._bindings.push( ${handler} );`
);
if (attribute.name === 'currentTime') { if (attribute.name === 'currentTime') {
const frame = block.getUniqueName(`${state.parentNode}_animationframe`); const frame = block.getUniqueName(`${state.parentNode}_animationframe`);
@ -152,11 +151,9 @@ export default function visitBinding(
} }
`); `);
block.builders.hydrate.addBlock(deindent` block.builders.hydrate.addBlock(
${generator.helper( `@addListener( ${state.parentNode}, '${eventName}', ${handler} );`
'addListener' );
)}( ${state.parentNode}, '${eventName}', ${handler} );
`);
if (node.name !== 'audio' && node.name !== 'video') if (node.name !== 'audio' && node.name !== 'video')
node.initialUpdate = updateElement; node.initialUpdate = updateElement;
@ -170,22 +167,16 @@ export default function visitBinding(
`); `);
} }
block.builders.destroy.addLine(deindent` block.builders.destroy.addLine(
${generator.helper( `@removeListener( ${state.parentNode}, '${eventName}', ${handler} );`
'removeListener' );
)}( ${state.parentNode}, '${eventName}', ${handler} );
`);
if (attribute.name === 'paused') { if (attribute.name === 'paused') {
block.builders.create.addLine( block.builders.create.addLine(
`${generator.helper( `@addListener( ${state.parentNode}, 'play', ${handler} );`
'addListener'
)}( ${state.parentNode}, 'play', ${handler} );`
); );
block.builders.destroy.addLine( block.builders.destroy.addLine(
`${generator.helper( `@removeListener( ${state.parentNode}, 'play', ${handler} );`
'removeListener'
)}( ${state.parentNode}, 'play', ${handler} );`
); );
} }
} }
@ -231,9 +222,7 @@ function getBindingValue(
// <input type='checkbox' bind:group='foo'> // <input type='checkbox' bind:group='foo'>
if (attribute.name === 'group') { if (attribute.name === 'group') {
if (type === 'checkbox') { if (type === 'checkbox') {
return `${generator.helper( return `@getBindingGroupValue( #component._bindingGroups[${bindingGroup}] )`;
'getBindingGroupValue'
)}( ${block.component}._bindingGroups[${bindingGroup}] )`;
} }
return `${state.parentNode}.__value`; return `${state.parentNode}.__value`;
@ -241,9 +230,7 @@ function getBindingValue(
// <input type='range|number' bind:value> // <input type='range|number' bind:value>
if (type === 'range' || type === 'number') { if (type === 'range' || type === 'number') {
return `${generator.helper( return `@toNumber( ${state.parentNode}.${attribute.name} )`;
'toNumber'
)}( ${state.parentNode}.${attribute.name} )`;
} }
// everything else // everything else

@ -52,27 +52,38 @@ export default function visitElement(
const isToplevel = !state.parentNode; const isToplevel = !state.parentNode;
block.addVariable(name); block.addVariable(name);
block.builders.create.addLine(`${name} = ${getRenderStatement(generator, childState.namespace, node.name)};`); block.builders.create.addLine(
`${name} = ${getRenderStatement(
generator,
childState.namespace,
node.name
)};`
);
if (generator.hydratable) { if (generator.hydratable) {
block.builders.claim.addBlock(deindent` block.builders.claim.addBlock(deindent`
${name} = ${getClaimStatement(generator, childState.namespace, state.parentNodes, node)}; ${name} = ${getClaimStatement(
var ${childState.parentNodes} = ${generator.helper('children')}( ${name} ); generator,
childState.namespace,
state.parentNodes,
node
)};
var ${childState.parentNodes} = @children( ${name} );
`); `);
} }
if (state.parentNode) { if (state.parentNode) {
block.builders.mount.addLine(`${block.generator.helper('appendNode')}( ${name}, ${state.parentNode} );`); block.builders.mount.addLine(
`@appendNode( ${name}, ${state.parentNode} );`
);
} else { } else {
block.builders.mount.addLine(`${block.generator.helper('insertNode')}( ${name}, ${block.target}, anchor );`); block.builders.mount.addLine(`@insertNode( ${name}, #target, anchor );`);
} }
// add CSS encapsulation attribute // add CSS encapsulation attribute
if (generator.cssId && (generator.cascade ? state.isTopLevel : generator.cssAppliesTo(node, elementStack))) { if (generator.cssId && (generator.cascade ? state.isTopLevel : generator.cssAppliesTo(node, elementStack))) {
block.builders.hydrate.addLine( block.builders.hydrate.addLine(
`${generator.helper( `@setAttribute( ${name}, '${generator.cssId}', '' );`
'setAttribute'
)}( ${name}, '${generator.cssId}', '' );`
); );
} }
@ -100,7 +111,7 @@ export default function visitElement(
const updates: string[] = []; const updates: string[] = [];
if (childState.usesComponent) { if (childState.usesComponent) {
initialProps.push(`component: ${block.component}`); initialProps.push(`component: #component`);
} }
childState.allUsedContexts.forEach((contextName: string) => { childState.allUsedContexts.forEach((contextName: string) => {
@ -134,9 +145,7 @@ export default function visitElement(
if (isToplevel) { if (isToplevel) {
// TODO we eventually need to consider what happens to elements // TODO we eventually need to consider what happens to elements
// that belong to the same outgroup as an outroing element... // that belong to the same outgroup as an outroing element...
block.builders.unmount.addLine( block.builders.unmount.addLine(`@detachNode( ${name} );`);
`${generator.helper('detachNode')}( ${name} );`
);
} }
if (node.name !== 'select') { if (node.name !== 'select') {
@ -189,7 +198,7 @@ export default function visitElement(
} }
block.builders.claim.addLine( block.builders.claim.addLine(
`${childState.parentNodes}.forEach( ${generator.helper('detachNode')} );` `${childState.parentNodes}.forEach( @detachNode );`
); );
} }
@ -199,14 +208,14 @@ function getRenderStatement(
name: 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 `@createSvgElement( '${name}' )`;
} }
if (namespace) { if (namespace) {
return `document.createElementNS( '${namespace}', '${name}' )`; return `document.createElementNS( '${namespace}', '${name}' )`;
} }
return `${generator.helper('createElement')}( '${name}' )`; return `@createElement( '${name}' )`;
} }
function getClaimStatement( function getClaimStatement(
@ -222,10 +231,12 @@ function getClaimStatement(
const name = namespace ? node.name : node.name.toUpperCase(); const name = namespace ? node.name : node.name.toUpperCase();
return `${generator.helper('claimElement')}( ${nodes}, '${name}', ${attributes ? `{ ${attributes} }` : `{}`}, ${namespace === namespaces.svg ? true : false} )`; return `@claimElement( ${nodes}, '${name}', ${attributes
? `{ ${attributes} }`
: `{}`}, ${namespace === namespaces.svg ? true : false} )`;
} }
function quoteProp(name: string) { function quoteProp(name: string) {
if (/[^a-zA-Z_$0-9]/.test(name)) return `'${name}'`; if (/[^a-zA-Z_$0-9]/.test(name)) return `'${name}'`;
return name; return name;
} }

@ -24,7 +24,7 @@ export default function visitEventHandler(
// 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.code.prependRight( generator.code.prependRight(
attribute.expression.start, attribute.expression.start,
`${block.component}.` `${block.alias('component')}.`
); );
if (shouldHoist) state.usesComponent = true; // this feels a bit hacky but it works! if (shouldHoist) state.usesComponent = true; // this feels a bit hacky but it works!
} }
@ -45,7 +45,7 @@ export default function visitEventHandler(
const declarations = usedContexts.map(name => { const declarations = usedContexts.map(name => {
if (name === 'state') { if (name === 'state') {
if (shouldHoist) state.usesComponent = true; if (shouldHoist) state.usesComponent = true;
return `var state = ${block.component}.get();`; return `var state = #component.get();`;
} }
const listName = block.listNames.get(name); const listName = block.listNames.get(name);
@ -63,7 +63,8 @@ export default function visitEventHandler(
// create the handler body // create the handler body
const handlerBody = deindent` const handlerBody = deindent`
${state.usesComponent && `var ${block.component} = this._svelte.component;`} ${state.usesComponent &&
`var ${block.alias('component')} = this._svelte.component;`}
${declarations} ${declarations}
[${attribute.expression.start}-${attribute.expression.end}]; [${attribute.expression.start}-${attribute.expression.end}];
`; `;
@ -72,9 +73,7 @@ export default function visitEventHandler(
block.addVariable(handlerName); block.addVariable(handlerName);
block.builders.hydrate.addBlock(deindent` block.builders.hydrate.addBlock(deindent`
${handlerName} = ${generator.alias( ${handlerName} = @template.events.${name}.call( #component, ${state.parentNode}, function ( event ) {
'template'
)}.events.${name}.call( ${block.component}, ${state.parentNode}, function ( event ) {
${handlerBody} ${handlerBody}
}); });
`); `);
@ -99,16 +98,12 @@ export default function visitEventHandler(
block.builders.init.addBlock(handler); block.builders.init.addBlock(handler);
} }
block.builders.hydrate.addLine(deindent` block.builders.hydrate.addLine(
${generator.helper( `@addListener( ${state.parentNode}, '${name}', ${handlerName} );`
'addListener' );
)}( ${state.parentNode}, '${name}', ${handlerName} );
`);
block.builders.destroy.addLine(deindent` block.builders.destroy.addLine(
${generator.helper( `@removeListener( ${state.parentNode}, '${name}', ${handlerName} );`
'removeListener' );
)}( ${state.parentNode}, '${name}', ${handlerName} );
`);
} }
} }

@ -14,11 +14,11 @@ export default function visitRef(
const name = attribute.name; const name = attribute.name;
block.builders.mount.addLine( block.builders.mount.addLine(
`${block.component}.refs.${name} = ${state.parentNode};` `#component.refs.${name} = ${state.parentNode};`
); );
block.builders.unmount.addLine(deindent` block.builders.unmount.addLine(deindent`
if ( ${block.component}.refs.${name} === ${state.parentNode} ) ${block.component}.refs.${name} = null; if ( #component.refs.${name} === ${state.parentNode} ) #component.refs.${name} = null;
`); `);
generator.usesRefs = true; // so this component.refs object is created generator.usesRefs = true; // so this component.refs object is created

@ -12,8 +12,6 @@ export default function addTransitions(
intro, intro,
outro outro
) { ) {
const wrapTransition = generator.helper('wrapTransition');
if (intro === outro) { if (intro === outro) {
const name = block.getUniqueName(`${state.name}_transition`); const name = block.getUniqueName(`${state.name}_transition`);
const snippet = intro.expression const snippet = intro.expression
@ -22,21 +20,21 @@ export default function addTransitions(
block.addVariable(name); block.addVariable(name);
const fn = `${generator.alias('template')}.transitions.${intro.name}`; const fn = `@template.transitions.${intro.name}`;
block.builders.intro.addBlock(deindent` block.builders.intro.addBlock(deindent`
${block.component}._renderHooks.push( function () { #component._renderHooks.push( function () {
if ( !${name} ) ${name} = ${wrapTransition}( ${state.name}, ${fn}, ${snippet}, true, null ); if ( !${name} ) ${name} = @wrapTransition( ${state.name}, ${fn}, ${snippet}, true, null );
${name}.run( true, function () { ${name}.run( true, function () {
${block.component}.fire( 'intro.end', { node: ${state.name} }); #component.fire( 'intro.end', { node: ${state.name} });
}); });
}); });
`); `);
block.builders.outro.addBlock(deindent` block.builders.outro.addBlock(deindent`
${name}.run( false, function () { ${name}.run( false, function () {
${block.component}.fire( 'outro.end', { node: ${state.name} }); #component.fire( 'outro.end', { node: ${state.name} });
if ( --${block.alias('outros')} === 0 ) ${block.alias('outrocallback')}(); if ( --#outros === 0 ) #outrocallback();
${name} = null; ${name} = null;
}); });
`); `);
@ -50,7 +48,7 @@ export default function addTransitions(
? block.contextualise(intro.expression).snippet ? block.contextualise(intro.expression).snippet
: '{}'; : '{}';
const fn = `${generator.alias('template')}.transitions.${intro.name}`; // TODO add built-in transitions? const fn = `@template.transitions.${intro.name}`; // TODO add built-in transitions?
if (outro) { if (outro) {
block.builders.intro.addBlock(deindent` block.builders.intro.addBlock(deindent`
@ -60,10 +58,10 @@ export default function addTransitions(
} }
block.builders.intro.addBlock(deindent` block.builders.intro.addBlock(deindent`
${block.component}._renderHooks.push( function () { #component._renderHooks.push( function () {
${introName} = ${wrapTransition}( ${state.name}, ${fn}, ${snippet}, true, null ); ${introName} = @wrapTransition( ${state.name}, ${fn}, ${snippet}, true, null );
${introName}.run( true, function () { ${introName}.run( true, function () {
${block.component}.fire( 'intro.end', { node: ${state.name} }); #component.fire( 'intro.end', { node: ${state.name} });
}); });
}); });
`); `);
@ -75,15 +73,15 @@ export default function addTransitions(
? block.contextualise(outro.expression).snippet ? block.contextualise(outro.expression).snippet
: '{}'; : '{}';
const fn = `${generator.alias('template')}.transitions.${outro.name}`; const fn = `@template.transitions.${outro.name}`;
// TODO hide elements that have outro'd (unless they belong to a still-outroing // TODO hide elements that have outro'd (unless they belong to a still-outroing
// group) prior to their removal from the DOM // group) prior to their removal from the DOM
block.builders.outro.addBlock(deindent` block.builders.outro.addBlock(deindent`
${outroName} = ${wrapTransition}( ${state.name}, ${fn}, ${snippet}, false, null ); ${outroName} = @wrapTransition( ${state.name}, ${fn}, ${snippet}, false, null );
${outroName}.run( false, function () { ${outroName}.run( false, function () {
${block.component}.fire( 'outro.end', { node: ${state.name} }); #component.fire( 'outro.end', { node: ${state.name} });
if ( --${block.alias('outros')} === 0 ) ${block.alias('outrocallback')}(); if ( --#outros === 0 ) #outrocallback();
}); });
`); `);
} }

@ -47,13 +47,13 @@ export default function visitWindow(
// allow event.stopPropagation(), this.select() etc // allow event.stopPropagation(), this.select() etc
generator.code.prependRight( generator.code.prependRight(
attribute.expression.start, attribute.expression.start,
`${block.component}.` `${block.alias('component')}.`
); );
} }
const handlerName = block.getUniqueName(`onwindow${attribute.name}`); const handlerName = block.getUniqueName(`onwindow${attribute.name}`);
const handlerBody = deindent` const handlerBody = deindent`
${usesState && `var state = ${block.component}.get();`} ${usesState && `var state = #component.get();`}
[${attribute.expression.start}-${attribute.expression.end}]; [${attribute.expression.start}-${attribute.expression.end}];
`; `;
@ -113,7 +113,7 @@ export default function visitWindow(
${event === 'scroll' && `${lock} = true;`} ${event === 'scroll' && `${lock} = true;`}
${generator.options.dev && `component._updatingReadonlyProperty = true;`} ${generator.options.dev && `component._updatingReadonlyProperty = true;`}
${block.component}.set({ #component.set({
${props} ${props}
}); });
@ -141,10 +141,10 @@ export default function visitWindow(
function ${observerCallback} () { function ${observerCallback} () {
if ( ${lock} ) return; if ( ${lock} ) return;
var x = ${bindings.scrollX var x = ${bindings.scrollX
? `${block.component}.get( '${bindings.scrollX}' )` ? `#component.get( '${bindings.scrollX}' )`
: `window.scrollX`}; : `window.scrollX`};
var y = ${bindings.scrollY var y = ${bindings.scrollY
? `${block.component}.get( '${bindings.scrollY}' )` ? `#component.get( '${bindings.scrollY}' )`
: `window.scrollY`}; : `window.scrollY`};
window.scrollTo( x, y ); window.scrollTo( x, y );
}; };
@ -152,18 +152,18 @@ export default function visitWindow(
if (bindings.scrollX) if (bindings.scrollX)
block.builders.init.addLine( block.builders.init.addLine(
`${block.component}.observe( '${bindings.scrollX}', ${observerCallback} );` `#component.observe( '${bindings.scrollX}', ${observerCallback} );`
); );
if (bindings.scrollY) if (bindings.scrollY)
block.builders.init.addLine( block.builders.init.addLine(
`${block.component}.observe( '${bindings.scrollY}', ${observerCallback} );` `#component.observe( '${bindings.scrollY}', ${observerCallback} );`
); );
} else if (bindings.scrollX || bindings.scrollY) { } else if (bindings.scrollX || bindings.scrollY) {
const isX = !!bindings.scrollX; const isX = !!bindings.scrollX;
block.builders.init.addBlock(deindent` block.builders.init.addBlock(deindent`
${block.component}.observe( '${bindings.scrollX || #component.observe( '${bindings.scrollX ||
bindings.scrollY}', function ( ${isX ? 'x' : 'y'} ) { bindings.scrollY}', function ( ${isX ? 'x' : 'y'} ) {
if ( ${lock} ) return; if ( ${lock} ) return;
window.scrollTo( ${isX ? 'x, window.scrollY' : 'window.scrollX, y'} ); window.scrollTo( ${isX ? 'x, window.scrollY' : 'window.scrollX, y'} );
}); });
@ -175,7 +175,7 @@ export default function visitWindow(
const handlerName = block.getUniqueName(`onlinestatuschanged`); const handlerName = block.getUniqueName(`onlinestatuschanged`);
block.builders.init.addBlock(deindent` block.builders.init.addBlock(deindent`
function ${handlerName} ( event ) { function ${handlerName} ( event ) {
${block.component}.set({ ${bindings.online}: navigator.onLine }); #component.set({ ${bindings.online}: navigator.onLine });
}; };
window.addEventListener( 'online', ${handlerName} ); window.addEventListener( 'online', ${handlerName} );
window.addEventListener( 'offline', ${handlerName} ); window.addEventListener( 'offline', ${handlerName} );

@ -108,9 +108,7 @@ export default function visitIfBlock(
simple(generator, block, state, node, branches[0], dynamic, vars); simple(generator, block, state, node, branches[0], dynamic, vars);
} }
block.builders.create.addLine( block.builders.create.addLine(`${if_name}${name}.create();`);
`${if_name}${name}.create();`
);
block.builders.claim.addLine( block.builders.claim.addLine(
`${if_name}${name}.claim( ${state.parentNodes} );` `${if_name}${name}.claim( ${state.parentNodes} );`
@ -119,8 +117,8 @@ export default function visitIfBlock(
if (node.needsAnchor) { if (node.needsAnchor) {
block.addElement( block.addElement(
anchor, anchor,
`${generator.helper('createComment')}()`, `@createComment()`,
`${generator.helper('createComment')}()`, `@createComment()`,
state.parentNode, state.parentNode,
true true
); );
@ -139,12 +137,12 @@ function simple(
{ name, anchor, params, if_name } { name, anchor, params, if_name }
) { ) {
block.builders.init.addBlock(deindent` block.builders.init.addBlock(deindent`
var ${name} = (${branch.condition}) && ${branch.block}( ${params}, ${block.component} ); var ${name} = (${branch.condition}) && ${branch.block}( ${params}, #component );
`); `);
const isTopLevel = !state.parentNode; const isTopLevel = !state.parentNode;
const mountOrIntro = branch.hasIntroMethod ? 'intro' : 'mount'; const mountOrIntro = branch.hasIntroMethod ? 'intro' : 'mount';
const targetNode = state.parentNode || block.target; const targetNode = state.parentNode || '#target';
const anchorNode = state.parentNode ? 'null' : 'anchor'; const anchorNode = state.parentNode ? 'null' : 'anchor';
block.builders.mount.addLine( block.builders.mount.addLine(
@ -159,7 +157,7 @@ function simple(
if ( ${name} ) { if ( ${name} ) {
${name}.update( changed, ${params} ); ${name}.update( changed, ${params} );
} else { } else {
${name} = ${branch.block}( ${params}, ${block.component} ); ${name} = ${branch.block}( ${params}, #component );
if ( ${name} ) ${name}.create(); if ( ${name} ) ${name}.create();
} }
@ -169,7 +167,7 @@ function simple(
if ( ${name} ) { if ( ${name} ) {
${name}.update( changed, ${params} ); ${name}.update( changed, ${params} );
} else { } else {
${name} = ${branch.block}( ${params}, ${block.component} ); ${name} = ${branch.block}( ${params}, #component );
${name}.create(); ${name}.create();
${name}.mount( ${parentNode}, ${anchor} ); ${name}.mount( ${parentNode}, ${anchor} );
} }
@ -177,14 +175,14 @@ function simple(
: branch.hasIntroMethod : branch.hasIntroMethod
? deindent` ? deindent`
if ( !${name} ) { if ( !${name} ) {
${name} = ${branch.block}( ${params}, ${block.component} ); ${name} = ${branch.block}( ${params}, #component );
${name}.create(); ${name}.create();
} }
${name}.intro( ${parentNode}, ${anchor} ); ${name}.intro( ${parentNode}, ${anchor} );
` `
: deindent` : deindent`
if ( !${name} ) { if ( !${name} ) {
${name} = ${branch.block}( ${params}, ${block.component} ); ${name} = ${branch.block}( ${params}, #component );
${name}.create(); ${name}.create();
${name}.mount( ${parentNode}, ${anchor} ); ${name}.mount( ${parentNode}, ${anchor} );
} }
@ -242,13 +240,13 @@ function compound(
} }
var ${current_block} = ${get_block}( ${params} ); var ${current_block} = ${get_block}( ${params} );
var ${name} = ${current_block_and}${current_block}( ${params}, ${block.component} ); var ${name} = ${current_block_and}${current_block}( ${params}, #component );
`); `);
const isTopLevel = !state.parentNode; const isTopLevel = !state.parentNode;
const mountOrIntro = branches[0].hasIntroMethod ? 'intro' : 'mount'; const mountOrIntro = branches[0].hasIntroMethod ? 'intro' : 'mount';
const targetNode = state.parentNode || block.target; const targetNode = state.parentNode || '#target';
const anchorNode = state.parentNode ? 'null' : 'anchor'; const anchorNode = state.parentNode ? 'null' : 'anchor';
block.builders.mount.addLine( block.builders.mount.addLine(
`${if_name}${name}.${mountOrIntro}( ${targetNode}, ${anchorNode} );` `${if_name}${name}.${mountOrIntro}( ${targetNode}, ${anchorNode} );`
@ -257,17 +255,17 @@ function compound(
const parentNode = state.parentNode || `${anchor}.parentNode`; const parentNode = state.parentNode || `${anchor}.parentNode`;
const changeBlock = deindent` const changeBlock = deindent`
${hasElse ? ${hasElse
deindent` ? deindent`
${name}.unmount(); ${name}.unmount();
${name}.destroy(); ${name}.destroy();
` : `
deindent` : deindent`
if ( ${name} ) { if ( ${name} ) {
${name}.unmount(); ${name}.unmount();
${name}.destroy(); ${name}.destroy();
}`} }`}
${name} = ${current_block_and}${current_block}( ${params}, ${block.component} ); ${name} = ${current_block_and}${current_block}( ${params}, #component );
${if_name}${name}.create(); ${if_name}${name}.create();
${if_name}${name}.${mountOrIntro}( ${parentNode}, ${anchor} ); ${if_name}${name}.${mountOrIntro}( ${parentNode}, ${anchor} );
`; `;
@ -288,13 +286,9 @@ function compound(
`); `);
} }
block.builders.unmount.addLine( block.builders.unmount.addLine(`${if_name}${name}.unmount();`);
`${if_name}${name}.unmount();`
);
block.builders.destroy.addLine( block.builders.destroy.addLine(`${if_name}${name}.destroy();`);
`${if_name}${name}.destroy();`
);
} }
// 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
@ -342,19 +336,19 @@ function compoundWithOutros(
if (hasElse) { if (hasElse) {
block.builders.init.addBlock(deindent` block.builders.init.addBlock(deindent`
${current_block_index} = ${get_block}( ${params} ); ${current_block_index} = ${get_block}( ${params} );
${name} = ${if_blocks}[ ${current_block_index} ] = ${if_block_creators}[ ${current_block_index} ]( ${params}, ${block.component} ); ${name} = ${if_blocks}[ ${current_block_index} ] = ${if_block_creators}[ ${current_block_index} ]( ${params}, #component );
`); `);
} else { } else {
block.builders.init.addBlock(deindent` block.builders.init.addBlock(deindent`
if ( ~( ${current_block_index} = ${get_block}( ${params} ) ) ) { if ( ~( ${current_block_index} = ${get_block}( ${params} ) ) ) {
${name} = ${if_blocks}[ ${current_block_index} ] = ${if_block_creators}[ ${current_block_index} ]( ${params}, ${block.component} ); ${name} = ${if_blocks}[ ${current_block_index} ] = ${if_block_creators}[ ${current_block_index} ]( ${params}, #component );
} }
`); `);
} }
const isTopLevel = !state.parentNode; const isTopLevel = !state.parentNode;
const mountOrIntro = branches[0].hasIntroMethod ? 'intro' : 'mount'; const mountOrIntro = branches[0].hasIntroMethod ? 'intro' : 'mount';
const targetNode = state.parentNode || block.target; const targetNode = state.parentNode || '#target';
const anchorNode = state.parentNode ? 'null' : 'anchor'; const anchorNode = state.parentNode ? 'null' : 'anchor';
block.builders.mount.addLine( block.builders.mount.addLine(
@ -374,7 +368,7 @@ function compoundWithOutros(
const createNewBlock = deindent` const createNewBlock = deindent`
${name} = ${if_blocks}[ ${current_block_index} ]; ${name} = ${if_blocks}[ ${current_block_index} ];
if ( !${name} ) { if ( !${name} ) {
${name} = ${if_blocks}[ ${current_block_index} ] = ${if_block_creators}[ ${current_block_index} ]( ${params}, ${block.component} ); ${name} = ${if_blocks}[ ${current_block_index} ] = ${if_block_creators}[ ${current_block_index} ]( ${params}, #component );
${name}.create(); ${name}.create();
} }
${name}.${mountOrIntro}( ${parentNode}, ${anchor} ); ${name}.${mountOrIntro}( ${parentNode}, ${anchor} );

@ -18,8 +18,10 @@ export default function visitMustacheTag(
block.addVariable(value); block.addVariable(value);
block.addElement( block.addElement(
name, name,
`${generator.helper('createText')}( ${value} = ${snippet} )`, `@createText( ${value} = ${snippet} )`,
generator.hydratable ? `${generator.helper('claimText')}( ${state.parentNodes}, ${value} = ${snippet} )` : '', generator.hydratable
? `@claimText( ${state.parentNodes}, ${value} = ${snippet} )`
: '',
state.parentNode, state.parentNode,
true true
); );

@ -23,15 +23,15 @@ export default function visitRawMustacheTag(
// exists for `Element`s. // exists for `Element`s.
block.addElement( block.addElement(
before, before,
`${generator.helper('createElement')}( 'noscript' )`, `@createElement( 'noscript' )`,
`${generator.helper('createElement')}( 'noscript' )`, `@createElement( 'noscript' )`,
state.parentNode, state.parentNode,
true true
); );
block.addElement( block.addElement(
after, after,
`${generator.helper('createElement')}( 'noscript' )`, `@createElement( 'noscript' )`,
`${generator.helper('createElement')}( 'noscript' )`, `@createElement( 'noscript' )`,
state.parentNode, state.parentNode,
true true
); );
@ -39,9 +39,7 @@ export default function visitRawMustacheTag(
const isToplevel = !state.parentNode; const isToplevel = !state.parentNode;
const mountStatement = `${before}.insertAdjacentHTML( 'afterend', ${value} = ${snippet} );`; const mountStatement = `${before}.insertAdjacentHTML( 'afterend', ${value} = ${snippet} );`;
const detachStatement = `${generator.helper( const detachStatement = `@detachBetween( ${before}, ${after} );`;
'detachBetween'
)}( ${before}, ${after} );`;
block.builders.mount.addLine(mountStatement); block.builders.mount.addLine(mountStatement);

@ -2,6 +2,7 @@ import { DomGenerator } from '../index';
import Block from '../Block'; import Block from '../Block';
import { Node } from '../../../interfaces'; import { Node } from '../../../interfaces';
import { State } from '../interfaces'; import { State } from '../interfaces';
import stringify from '../../../utils/stringify';
export default function visitText( export default function visitText(
generator: DomGenerator, generator: DomGenerator,
@ -12,8 +13,10 @@ export default function visitText(
if (!node._state.shouldCreate) return; if (!node._state.shouldCreate) return;
block.addElement( block.addElement(
node._state.name, node._state.name,
`${generator.helper('createText')}( ${JSON.stringify(node.data)} )`, `@createText( ${stringify(node.data)} )`,
generator.hydratable ? `${generator.helper('claimText')}( ${state.parentNodes}, ${JSON.stringify(node.data)} )` : '', generator.hydratable
? `@claimText( ${state.parentNodes}, ${stringify(node.data)} )`
: '',
state.parentNode, state.parentNode,
node.usedAsAnchor node.usedAsAnchor
); );

@ -7,13 +7,13 @@ export default function visitYieldTag(
block: Block, block: Block,
state: State state: State
) { ) {
const parentNode = state.parentNode || block.target; const parentNode = state.parentNode || '#target';
block.builders.mount.addLine( block.builders.mount.addLine(
`if ( ${block.component}._yield ) ${block.component}._yield.mount( ${parentNode}, null );` `if ( #component._yield ) #component._yield.mount( ${parentNode}, null );`
); );
block.builders.unmount.addLine( block.builders.unmount.addLine(
`if ( ${block.component}._yield ) ${block.component}._yield.unmount();` `if ( #component._yield ) #component._yield.unmount();`
); );
} }

@ -22,27 +22,30 @@ export default function getSetter({
return deindent` return deindent`
var list = this.${context}.${block.listNames.get(name)}; var list = this.${context}.${block.listNames.get(name)};
var index = this.${context}.${block.indexNames.get(name)}; var index = this.${context}.${block.indexNames.get(name)};
${computed && `var state = ${block.component}.get();`} ${computed && `var state = #component.get();`}
list[index]${tail} = ${value}; list[index]${tail} = ${value};
${computed ? ${computed
`${block.component}._set({ ${dependencies.map((prop: string) => `${prop}: state.${prop}`).join(', ')} });` : ? `#component._set({ ${dependencies
`${block.component}._set({ ${dependencies.map((prop: string) => `${prop}: ${block.component}.get( '${prop}' )`).join(', ')} });` .map((prop: string) => `${prop}: state.${prop}`)
} .join(', ')} });`
: `#component._set({ ${dependencies
.map((prop: string) => `${prop}: #component.get( '${prop}' )`)
.join(', ')} });`}
`; `;
} }
if (attribute.value.type === 'MemberExpression') { if (attribute.value.type === 'MemberExpression') {
const alias = block.alias(name);
return deindent` return deindent`
var state = ${block.component}.get(); var state = #component.get();
${snippet} = ${value}; ${snippet} = ${value};
${block.component}._set({ ${dependencies.map((prop: string) => `${prop}: state.${prop}`).join(', ')} }); #component._set({ ${dependencies
.map((prop: string) => `${prop}: state.${prop}`)
.join(', ')} });
`; `;
} }
return `${block.component}._set({ ${name}: ${value} });`; return `#component._set({ ${name}: ${value} });`;
} }
function isComputed(node: Node) { function isComputed(node: Node) {

@ -86,23 +86,19 @@ export default function ssr(
${name}.filename = ${JSON.stringify(options.filename)}; ${name}.filename = ${JSON.stringify(options.filename)};
${name}.data = function () { ${name}.data = function () {
return ${templateProperties.data return ${templateProperties.data ? `@template.data()` : `{}`};
? `${generator.alias('template')}.data()`
: `{}`};
}; };
${name}.render = function ( state, options ) { ${name}.render = function ( state, options ) {
${templateProperties.data ${templateProperties.data
? `state = Object.assign( ${generator.alias( ? `state = Object.assign( @template.data(), state || {} );`
'template'
)}.data(), state || {} );`
: `state = state || {};`} : `state = state || {};`}
${computations.map( ${computations.map(
({ key, deps }) => ({ key, deps }) =>
`state.${key} = ${generator.alias( `state.${key} = @template.computed.${key}( ${deps
'template' .map(dep => `state.${dep}`)
)}.computed.${key}( ${deps.map(dep => `state.${dep}`).join(', ')} );` .join(', ')} );`
)} )}
${generator.bindings.length && ${generator.bindings.length &&
@ -117,7 +113,7 @@ export default function ssr(
} }
`} `}
return \`${generator.renderCode}\`; return \`${generator.renderCode}\`.trim();
}; };
${name}.renderCss = function () { ${name}.renderCss = function () {
@ -149,7 +145,7 @@ export default function ssr(
const { name } = prop.key; const { name } = prop.key;
const expression = const expression =
generator.importedComponents.get(name) || generator.importedComponents.get(name) ||
`${generator.alias('template')}.components.${name}`; `@template.components.${name}`;
return `addComponent( ${expression} );`; return `addComponent( ${expression} );`;
})} })}
`} `}
@ -172,7 +168,7 @@ export default function ssr(
function __escape ( html ) { function __escape ( html ) {
return String( html ).replace( /["'&<>]/g, match => escaped[ match ] ); return String( html ).replace( /["'&<>]/g, match => escaped[ match ] );
} }
`; `.replace(/(\\)?@(\w*)/g, (match: string, escaped: string, name: string) => escaped ? match.slice(1) : generator.alias(name));
return generator.generate(result, options, { name, format }); return generator.generate(result, options, { name, format });
} }

@ -11,7 +11,7 @@ export default function visitComponent(
block: Block, block: Block,
node: Node node: Node
) { ) {
function stringify(chunk: Node) { function stringifyAttribute(chunk: Node) {
if (chunk.type === 'Text') return chunk.data; if (chunk.type === 'Text') return chunk.data;
if (chunk.type === 'MustacheTag') { if (chunk.type === 'MustacheTag') {
const { snippet } = block.contextualise(chunk.expression); const { snippet } = block.contextualise(chunk.expression);
@ -47,7 +47,7 @@ export default function visitComponent(
value = snippet; value = snippet;
} }
} else { } else {
value = '`' + attribute.value.map(stringify).join('') + '`'; value = '`' + attribute.value.map(stringifyAttribute).join('') + '`';
} }
return `${attribute.name}: ${value}`; return `${attribute.name}: ${value}`;
@ -59,7 +59,9 @@ export default function visitComponent(
? getTailSnippet(binding.value) ? getTailSnippet(binding.value)
: ''; : '';
const keypath = block.contexts.has(name) ? `${name}${tail}` : `state.${name}${tail}`; const keypath = block.contexts.has(name)
? `${name}${tail}`
: `state.${name}${tail}`;
return `${binding.name}: ${keypath}`; return `${binding.name}: ${keypath}`;
}) })
) )
@ -68,7 +70,7 @@ export default function visitComponent(
const expression = node.name === ':Self' const expression = node.name === ':Self'
? generator.name ? generator.name
: generator.importedComponents.get(node.name) || : generator.importedComponents.get(node.name) ||
`${generator.alias('template')}.components.${node.name}`; `@template.components.${node.name}`;
bindings.forEach(binding => { bindings.forEach(binding => {
block.addBinding(binding, expression); block.addBinding(binding, expression);

@ -10,7 +10,9 @@ export default function visitEachBlock(
) { ) {
const { dependencies, snippet } = block.contextualise(node.expression); const { dependencies, snippet } = block.contextualise(node.expression);
const open = `\${ ${node.else ? `${snippet}.length ? ` : ''}${snippet}.map( ${node.index const open = `\${ ${node.else
? `${snippet}.length ? `
: ''}${snippet}.map( ${node.index
? `( ${node.context}, ${node.index} )` ? `( ${node.context}, ${node.index} )`
: node.context} => \``; : node.context} => \``;
generator.append(open); generator.append(open);

@ -7,5 +7,5 @@ export default function visitText(
block: Block, block: Block,
node: Node node: Node
) { ) {
generator.append(node.data.replace(/(\${|`|\\)/g, '\\$1')); generator.append(node.data.replace(/(\${|`|\\)/g, '\\$1').replace(/([^\\])?([@#])/g, '$1\\$2'));
} }

@ -85,9 +85,7 @@ export default function processCss(
} }
shouldTransform = false; shouldTransform = false;
} } else if (child.type === 'PseudoElementSelector') {
else if (child.type === 'PseudoElementSelector') {
code.prependRight(c, attr); code.prependRight(c, attr);
shouldTransform = false; shouldTransform = false;
} }

@ -76,39 +76,17 @@ export class Parser {
this.error('Unexpected end of input'); this.error('Unexpected end of input');
} }
// trim unnecessary whitespace if (this.html.children.length) {
while (this.html.children.length) { let start = this.html.children[0] && this.html.children[0].start;
const firstChild = this.html.children[0]; while (/\s/.test(template[start])) start += 1;
this.html.start = firstChild.start;
if (firstChild.type !== 'Text') break;
const length = firstChild.data.length;
firstChild.data = trimStart(firstChild.data);
if (firstChild.data === '') {
this.html.children.shift();
} else {
this.html.start += length - firstChild.data.length;
break;
}
}
while (this.html.children.length) {
const lastChild = this.html.children[this.html.children.length - 1];
this.html.end = lastChild.end;
if (lastChild.type !== 'Text') break;
const length = lastChild.data.length; let end = this.html.children[this.html.children.length - 1] && this.html.children[this.html.children.length - 1].end;
lastChild.data = trimEnd(lastChild.data); while (/\s/.test(template[end - 1])) end -= 1;
if (lastChild.data === '') { this.html.start = start;
this.html.children.pop(); this.html.end = end;
} else { } else {
this.html.end -= length - lastChild.data.length; this.html.start = this.html.end = null;
break;
}
} }
} }

@ -62,23 +62,6 @@ const disallowedContents = new Map([
['th', new Set(['td', 'th', 'tr'])], ['th', new Set(['td', 'th', 'tr'])],
]); ]);
function stripWhitespace(element) {
if (element.children.length) {
const firstChild = element.children[0];
const lastChild = element.children[element.children.length - 1];
if (firstChild.type === 'Text') {
firstChild.data = trimStart(firstChild.data);
if (!firstChild.data) element.children.shift();
}
if (lastChild.type === 'Text') {
lastChild.data = trimEnd(lastChild.data);
if (!lastChild.data) element.children.pop();
}
}
}
export default function tag(parser: Parser) { export default function tag(parser: Parser) {
const start = parser.index++; const start = parser.index++;
@ -147,9 +130,6 @@ export default function tag(parser: Parser) {
parent = parser.current(); parent = parser.current();
} }
// strip leading/trailing whitespace as necessary
stripWhitespace(parent);
parent.end = parser.index; parent.end = parser.index;
parser.stack.pop(); parser.stack.pop();
@ -158,8 +138,6 @@ export default function tag(parser: Parser) {
// can this be a child of the parent element, or does it implicitly // can this be a child of the parent element, or does it implicitly
// close it, like `<li>one<li>two`? // close it, like `<li>one<li>two`?
if (disallowedContents.get(parent.name).has(name)) { if (disallowedContents.get(parent.name).has(name)) {
stripWhitespace(parent);
parent.end = start; parent.end = start;
parser.stack.pop(); parser.stack.pop();
} }

@ -1,4 +1,5 @@
import { assign, noop } from './utils.js'; import { assign, noop } from './utils.js';
import { createElement } from './dom.js';
export function linear(t) { export function linear(t) {
return t; return t;
@ -37,8 +38,9 @@ export function wrapTransition(node, fn, params, intro, outgroup) {
var ease = obj.easing || linear; var ease = obj.easing || linear;
var cssText; var cssText;
// TODO share <style> tag between all transitions?
if (obj.css && !transitionManager.stylesheet) { if (obj.css && !transitionManager.stylesheet) {
var style = document.createElement('style'); var style = createElement('style');
document.head.appendChild(style); document.head.appendChild(style);
transitionManager.stylesheet = style.sheet; transitionManager.stylesheet = style.sheet;
} }

@ -15,4 +15,4 @@ export default function clone(node: Node) {
} }
return cloned; return cloned;
} }

@ -1,6 +1,9 @@
const start = /\n(\t+)/; const start = /\n(\t+)/;
export default function deindent(strings: TemplateStringsArray, ...values: any[]) { export default function deindent(
strings: TemplateStringsArray,
...values: any[]
) {
const indentation = start.exec(strings[0])[1]; const indentation = start.exec(strings[0])[1];
const pattern = new RegExp(`^${indentation}`, 'gm'); const pattern = new RegExp(`^${indentation}`, 'gm');

@ -3,4 +3,4 @@ import { Node } from '../interfaces';
export default function getObject(node: Node) { export default function getObject(node: Node) {
while (node.type === 'MemberExpression') node = node.object; while (node.type === 'MemberExpression') node = node.object;
return node; return node;
} }

@ -6,4 +6,4 @@ export default function getTailSnippet(node: Node) {
const start = node.end; const start = node.end;
return `[✂${start}-${end}✂]`; return `[✂${start}-${end}✂]`;
} }

@ -0,0 +1,3 @@
export default function stringify(data: string) {
return JSON.stringify(data.replace(/([^\\])?([@#])/g, '$1\\$2'));
}

@ -146,7 +146,10 @@ function checkTypeAttribute(validator: Validator, node: Node) {
} }
if (attribute.value.length > 1 || attribute.value[0].type !== 'Text') { if (attribute.value.length > 1 || attribute.value[0].type !== 'Text') {
validator.error(`'type' attribute cannot be dynamic if input uses two-way binding`, attribute.start); validator.error(
`'type' attribute cannot be dynamic if input uses two-way binding`,
attribute.start
);
} }
return attribute.value[0].data; return attribute.value[0].data;

@ -145,7 +145,7 @@ var template = (function () {
function add_css () { function add_css () {
var style = createElement( 'style' ); var style = createElement( 'style' );
style.id = "svelte-3590263702-style"; style.id = 'svelte-3590263702-style';
style.textContent = "\n\tp[svelte-3590263702], [svelte-3590263702] p {\n\t\tcolor: red;\n\t}\n"; style.textContent = "\n\tp[svelte-3590263702], [svelte-3590263702] p {\n\t\tcolor: red;\n\t}\n";
appendNode( style, document.head ); appendNode( style, document.head );
} }
@ -198,7 +198,7 @@ function SvelteComponent ( options ) {
this._yield = options._yield; this._yield = options._yield;
this._torndown = false; this._torndown = false;
if ( !document.getElementById( "svelte-3590263702-style" ) ) add_css(); if ( !document.getElementById( 'svelte-3590263702-style' ) ) add_css();
this._fragment = create_main_fragment( this._state, this ); this._fragment = create_main_fragment( this._state, this );

@ -10,7 +10,7 @@ var template = (function () {
function add_css () { function add_css () {
var style = createElement( 'style' ); var style = createElement( 'style' );
style.id = "svelte-3590263702-style"; style.id = 'svelte-3590263702-style';
style.textContent = "\n\tp[svelte-3590263702], [svelte-3590263702] p {\n\t\tcolor: red;\n\t}\n"; style.textContent = "\n\tp[svelte-3590263702], [svelte-3590263702] p {\n\t\tcolor: red;\n\t}\n";
appendNode( style, document.head ); appendNode( style, document.head );
} }
@ -63,7 +63,7 @@ function SvelteComponent ( options ) {
this._yield = options._yield; this._yield = options._yield;
this._torndown = false; this._torndown = false;
if ( !document.getElementById( "svelte-3590263702-style" ) ) add_css(); if ( !document.getElementById( 'svelte-3590263702-style' ) ) add_css();
this._fragment = create_main_fragment( this._state, this ); this._fragment = create_main_fragment( this._state, this );

@ -136,7 +136,7 @@ var proto = {
}; };
function create_main_fragment ( state, component ) { function create_main_fragment ( state, component ) {
var div, text, p, text_1, text_2, text_3, text_4, p_1, text_5, text_6, text_7, text_8, if_block_4_anchor; var div, text, p, text_1, text_2, text_3, text_4, p_1, text_5, text_6, text_8, if_block_4_anchor;
var if_block = (state.a) && create_if_block( state, component ); var if_block = (state.a) && create_if_block( state, component );
@ -164,7 +164,6 @@ function create_main_fragment ( state, component ) {
text_5 = createText( "so can this" ); text_5 = createText( "so can this" );
text_6 = createText( "\n\n\t" ); text_6 = createText( "\n\n\t" );
if ( if_block_3 ) if_block_3.create(); if ( if_block_3 ) if_block_3.create();
text_7 = createText( "\n\n\t" );
text_8 = createText( "\n\n" ); text_8 = createText( "\n\n" );
if ( if_block_4 ) if_block_4.create(); if ( if_block_4 ) if_block_4.create();
if_block_4_anchor = createComment(); if_block_4_anchor = createComment();
@ -185,7 +184,6 @@ function create_main_fragment ( state, component ) {
appendNode( text_5, p_1 ); appendNode( text_5, p_1 );
appendNode( text_6, div ); appendNode( text_6, div );
if ( if_block_3 ) if_block_3.mount( div, null ); if ( if_block_3 ) if_block_3.mount( div, null );
appendNode( text_7, div );
insertNode( text_8, target, anchor ); insertNode( text_8, target, anchor );
if ( if_block_4 ) if_block_4.mount( target, anchor ); if ( if_block_4 ) if_block_4.mount( target, anchor );
insertNode( if_block_4_anchor, target, anchor ); insertNode( if_block_4_anchor, target, anchor );
@ -232,7 +230,7 @@ function create_main_fragment ( state, component ) {
if ( !if_block_3 ) { if ( !if_block_3 ) {
if_block_3 = create_if_block_3( state, component ); if_block_3 = create_if_block_3( state, component );
if_block_3.create(); if_block_3.create();
if_block_3.mount( div, text_7 ); if_block_3.mount( div, null );
} }
} else if ( if_block_3 ) { } else if ( if_block_3 ) {
if_block_3.unmount(); if_block_3.unmount();

@ -1,7 +1,7 @@
import { appendNode, assign, createComment, createElement, createText, detachNode, dispatchObservers, insertNode, noop, proto } from "svelte/shared.js"; import { appendNode, assign, createComment, createElement, createText, detachNode, dispatchObservers, insertNode, noop, proto } from "svelte/shared.js";
function create_main_fragment ( state, component ) { function create_main_fragment ( state, component ) {
var div, text, p, text_1, text_2, text_3, text_4, p_1, text_5, text_6, text_7, text_8, if_block_4_anchor; var div, text, p, text_1, text_2, text_3, text_4, p_1, text_5, text_6, text_8, if_block_4_anchor;
var if_block = (state.a) && create_if_block( state, component ); var if_block = (state.a) && create_if_block( state, component );
@ -29,7 +29,6 @@ function create_main_fragment ( state, component ) {
text_5 = createText( "so can this" ); text_5 = createText( "so can this" );
text_6 = createText( "\n\n\t" ); text_6 = createText( "\n\n\t" );
if ( if_block_3 ) if_block_3.create(); if ( if_block_3 ) if_block_3.create();
text_7 = createText( "\n\n\t" );
text_8 = createText( "\n\n" ); text_8 = createText( "\n\n" );
if ( if_block_4 ) if_block_4.create(); if ( if_block_4 ) if_block_4.create();
if_block_4_anchor = createComment(); if_block_4_anchor = createComment();
@ -50,7 +49,6 @@ function create_main_fragment ( state, component ) {
appendNode( text_5, p_1 ); appendNode( text_5, p_1 );
appendNode( text_6, div ); appendNode( text_6, div );
if ( if_block_3 ) if_block_3.mount( div, null ); if ( if_block_3 ) if_block_3.mount( div, null );
appendNode( text_7, div );
insertNode( text_8, target, anchor ); insertNode( text_8, target, anchor );
if ( if_block_4 ) if_block_4.mount( target, anchor ); if ( if_block_4 ) if_block_4.mount( target, anchor );
insertNode( if_block_4_anchor, target, anchor ); insertNode( if_block_4_anchor, target, anchor );
@ -97,7 +95,7 @@ function create_main_fragment ( state, component ) {
if ( !if_block_3 ) { if ( !if_block_3 ) {
if_block_3 = create_if_block_3( state, component ); if_block_3 = create_if_block_3( state, component );
if_block_3.create(); if_block_3.create();
if_block_3.mount( div, text_7 ); if_block_3.mount( div, null );
} }
} else if ( if_block_3 ) { } else if ( if_block_3 ) {
if_block_3.unmount(); if_block_3.unmount();

@ -1,4 +1,5 @@
{ {
"hash": 3179574701,
"html": { "html": {
"start": 0, "start": 0,
"end": 45, "end": 45,
@ -21,9 +22,9 @@
"end": 32, "end": 32,
"type": "MustacheTag", "type": "MustacheTag",
"expression": { "expression": {
"type": "Identifier",
"start": 22, "start": 22,
"end": 30, "end": 30,
"type": "Identifier",
"name": "readonly" "name": "readonly"
} }
} }
@ -36,4 +37,4 @@
}, },
"css": null, "css": null,
"js": null "js": null
} }

@ -1,5 +1,5 @@
{ {
"hash": 3305933215, "hash": 2788845841,
"html": { "html": {
"start": 0, "start": 0,
"end": 29, "end": 29,

@ -1,4 +1,5 @@
{ {
"hash": 804348386,
"html": { "html": {
"start": 0, "start": 0,
"end": 46, "end": 46,
@ -27,9 +28,9 @@
"end": 28, "end": 28,
"type": "MustacheTag", "type": "MustacheTag",
"expression": { "expression": {
"type": "Identifier",
"start": 21, "start": 21,
"end": 26, "end": 26,
"type": "Identifier",
"name": "color" "name": "color"
} }
}, },
@ -48,9 +49,9 @@
"end": 40, "end": 40,
"type": "MustacheTag", "type": "MustacheTag",
"expression": { "expression": {
"type": "Identifier",
"start": 33, "start": 33,
"end": 38, "end": 38,
"type": "Identifier",
"name": "color" "name": "color"
} }
} }
@ -60,4 +61,4 @@
}, },
"css": null, "css": null,
"js": null "js": null
} }

@ -1,4 +1,5 @@
{ {
"hash": 1563956934,
"html": { "html": {
"start": 0, "start": 0,
"end": 41, "end": 41,
@ -31,4 +32,4 @@
}, },
"css": null, "css": null,
"js": null "js": null
} }

@ -1,4 +1,5 @@
{ {
"hash": 507039402,
"html": { "html": {
"start": 0, "start": 0,
"end": 28, "end": 28,
@ -45,4 +46,4 @@
}, },
"css": null, "css": null,
"js": null "js": null
} }

@ -1,5 +1,5 @@
{ {
"hash": 4120363214, "hash": 1705925892,
"html": { "html": {
"start": 0, "start": 0,
"end": 10, "end": 10,
@ -18,9 +18,9 @@
"name": "id", "name": "id",
"value": [ "value": [
{ {
"type": "AttributeShorthand",
"start": 6, "start": 6,
"end": 8, "end": 8,
"type": "AttributeShorthand",
"expression": { "expression": {
"type": "Identifier", "type": "Identifier",
"start": 6, "start": 6,

@ -1,4 +1,5 @@
{ {
"hash": 606864228,
"html": { "html": {
"start": 0, "start": 0,
"end": 30, "end": 30,
@ -24,4 +25,4 @@
}, },
"css": null, "css": null,
"js": null "js": null
} }

@ -1,4 +1,5 @@
{ {
"hash": 1493227373,
"html": { "html": {
"start": 0, "start": 0,
"end": 23, "end": 23,
@ -31,4 +32,4 @@
}, },
"css": null, "css": null,
"js": null "js": null
} }

@ -1,4 +1,5 @@
{ {
"hash": 3488539025,
"html": { "html": {
"start": 0, "start": 0,
"end": 21, "end": 21,
@ -31,4 +32,4 @@
}, },
"css": null, "css": null,
"js": null "js": null
} }

@ -1,4 +1,5 @@
{ {
"hash": 3088875001,
"html": { "html": {
"start": 0, "start": 0,
"end": 18, "end": 18,
@ -29,4 +30,4 @@
}, },
"css": null, "css": null,
"js": null "js": null
} }

@ -1,4 +1,5 @@
{ {
"hash": 1937205193,
"html": { "html": {
"start": 0, "start": 0,
"end": 25, "end": 25,
@ -16,9 +17,9 @@
"type": "Binding", "type": "Binding",
"name": "value", "name": "value",
"value": { "value": {
"type": "Identifier",
"start": 19, "start": 19,
"end": 23, "end": 23,
"type": "Identifier",
"name": "name" "name": "name"
} }
} }
@ -29,4 +30,4 @@
}, },
"css": null, "css": null,
"js": null "js": null
} }

@ -1,4 +1,5 @@
{ {
"hash": 3294612990,
"html": { "html": {
"start": 0, "start": 0,
"end": 18, "end": 18,
@ -14,4 +15,4 @@
}, },
"css": null, "css": null,
"js": null "js": null
} }

@ -1,4 +1,5 @@
{ {
"hash": 2365862121,
"html": { "html": {
"start": 0, "start": 0,
"end": 24, "end": 24,
@ -23,4 +24,4 @@
}, },
"css": null, "css": null,
"js": null "js": null
} }

@ -1,4 +1,5 @@
{ {
"hash": 156753432,
"html": { "html": {
"start": 0, "start": 0,
"end": 17, "end": 17,
@ -14,4 +15,4 @@
}, },
"css": null, "css": null,
"js": null "js": null
} }

@ -1,4 +1,5 @@
{ {
"hash": 1147407419,
"html": { "html": {
"start": 0, "start": 0,
"end": 14, "end": 14,
@ -18,6 +19,12 @@
"data": "foo" "data": "foo"
} }
] ]
},
{
"start": 14,
"end": 16,
"type": "Text",
"data": "\n\n"
} }
] ]
}, },
@ -25,65 +32,65 @@
"start": 16, "start": 16,
"end": 56, "end": 56,
"attributes": [], "attributes": [],
"content": {
"start": 23,
"end": 48,
"styles": "\n\tdiv {\n\t\tcolor: red;\n\t}\n"
},
"children": [ "children": [
{ {
"type": "Rule", "type": "Rule",
"start": 25,
"end": 47,
"selector": { "selector": {
"type": "SelectorList", "type": "SelectorList",
"start": 25,
"end": 28,
"children": [ "children": [
{ {
"type": "Selector", "type": "Selector",
"start": 25,
"end": 28,
"children": [ "children": [
{ {
"type": "TypeSelector", "type": "TypeSelector",
"name": "div",
"start": 25, "start": 25,
"end": 28, "end": 28
"name": "div"
} }
] ],
"start": 25,
"end": 28
} }
] ],
"start": 25,
"end": 28
}, },
"block": { "block": {
"type": "Block", "type": "Block",
"start": 29,
"end": 47,
"children": [ "children": [
{ {
"type": "Declaration", "type": "Declaration",
"start": 33,
"end": 43,
"important": false, "important": false,
"property": "color", "property": "color",
"value": { "value": {
"type": "Value", "type": "Value",
"start": 39,
"end": 43,
"children": [ "children": [
{ {
"type": "Identifier", "type": "Identifier",
"name": "red",
"start": 40, "start": 40,
"end": 43, "end": 43
"name": "red"
} }
] ],
} "start": 39,
"end": 43
},
"start": 33,
"end": 43
} }
] ],
} "start": 29,
"end": 47
},
"start": 25,
"end": 47
} }
] ],
"content": {
"start": 23,
"end": 48,
"styles": "\n\tdiv {\n\t\tcolor: red;\n\t}\n"
}
}, },
"js": null "js": null
} }

@ -1,4 +1,5 @@
{ {
"hash": 3238289871,
"html": { "html": {
"start": 0, "start": 0,
"end": 84, "end": 84,
@ -9,12 +10,11 @@
"end": 84, "end": 84,
"type": "EachBlock", "type": "EachBlock",
"expression": { "expression": {
"type": "Identifier",
"start": 8, "start": 8,
"end": 15, "end": 15,
"type": "Identifier",
"name": "animals" "name": "animals"
}, },
"context": "animal",
"children": [ "children": [
{ {
"start": 29, "start": 29,
@ -28,40 +28,41 @@
"end": 42, "end": 42,
"type": "MustacheTag", "type": "MustacheTag",
"expression": { "expression": {
"type": "Identifier",
"start": 34, "start": 34,
"end": 40, "end": 40,
"type": "Identifier",
"name": "animal" "name": "animal"
} }
} }
] ]
} }
], ],
"context": "animal",
"else": { "else": {
"start": 55,
"end": 75,
"type": "ElseBlock",
"children": [ "children": [
{ {
"start": 57,
"end": 74,
"type": "Element",
"name": "p",
"attributes": [], "attributes": [],
"children": [ "children": [
{ {
"data": "no animals",
"end": 70,
"start": 60, "start": 60,
"type": "Text" "end": 70,
"type": "Text",
"data": "no animals"
} }
], ]
"end": 74,
"name": "p",
"start": 57,
"type": "Element"
} }
], ]
"end": 75,
"start": 55,
"type": "ElseBlock"
} }
} }
] ]
}, },
"css": null, "css": null,
"js": null "js": null
} }

@ -1,4 +1,5 @@
{ {
"hash": 2841674990,
"html": { "html": {
"start": 0, "start": 0,
"end": 66, "end": 66,
@ -9,13 +10,11 @@
"end": 66, "end": 66,
"type": "EachBlock", "type": "EachBlock",
"expression": { "expression": {
"type": "Identifier",
"start": 8, "start": 8,
"end": 15, "end": 15,
"type": "Identifier",
"name": "animals" "name": "animals"
}, },
"context": "animal",
"index": "i",
"children": [ "children": [
{ {
"start": 32, "start": 32,
@ -29,9 +28,9 @@
"end": 40, "end": 40,
"type": "MustacheTag", "type": "MustacheTag",
"expression": { "expression": {
"type": "Identifier",
"start": 37, "start": 37,
"end": 38, "end": 38,
"type": "Identifier",
"name": "i" "name": "i"
} }
}, },
@ -46,18 +45,20 @@
"end": 52, "end": 52,
"type": "MustacheTag", "type": "MustacheTag",
"expression": { "expression": {
"type": "Identifier",
"start": 44, "start": 44,
"end": 50, "end": 50,
"type": "Identifier",
"name": "animal" "name": "animal"
} }
} }
] ]
} }
] ],
"context": "animal",
"index": "i"
} }
] ]
}, },
"css": null, "css": null,
"js": null "js": null
} }

@ -1,4 +1,5 @@
{ {
"hash": 2025411181,
"html": { "html": {
"start": 0, "start": 0,
"end": 54, "end": 54,
@ -9,13 +10,11 @@
"end": 54, "end": 54,
"type": "EachBlock", "type": "EachBlock",
"expression": { "expression": {
"type": "Identifier",
"start": 8, "start": 8,
"end": 13, "end": 13,
"type": "Identifier",
"name": "todos" "name": "todos"
}, },
"context": "todo",
"key": "id",
"children": [ "children": [
{ {
"start": 29, "start": 29,
@ -29,18 +28,20 @@
"end": 40, "end": 40,
"type": "MustacheTag", "type": "MustacheTag",
"expression": { "expression": {
"type": "Identifier",
"start": 34, "start": 34,
"end": 38, "end": 38,
"type": "Identifier",
"name": "todo" "name": "todo"
} }
} }
] ]
} }
] ],
"context": "todo",
"key": "id"
} }
] ]
}, },
"css": null, "css": null,
"js": null "js": null
} }

@ -1,4 +1,5 @@
{ {
"hash": 220340986,
"html": { "html": {
"start": 0, "start": 0,
"end": 56, "end": 56,
@ -9,12 +10,11 @@
"end": 56, "end": 56,
"type": "EachBlock", "type": "EachBlock",
"expression": { "expression": {
"type": "Identifier",
"start": 8, "start": 8,
"end": 15, "end": 15,
"type": "Identifier",
"name": "animals" "name": "animals"
}, },
"context": "animal",
"children": [ "children": [
{ {
"start": 29, "start": 29,
@ -28,18 +28,19 @@
"end": 42, "end": 42,
"type": "MustacheTag", "type": "MustacheTag",
"expression": { "expression": {
"type": "Identifier",
"start": 34, "start": 34,
"end": 40, "end": 40,
"type": "Identifier",
"name": "animal" "name": "animal"
} }
} }
] ]
} }
] ],
"context": "animal"
} }
] ]
}, },
"css": null, "css": null,
"js": null "js": null
} }

@ -1,4 +1,5 @@
{ {
"hash": 1265376132,
"html": { "html": {
"start": 0, "start": 0,
"end": 24, "end": 24,
@ -22,9 +23,9 @@
"end": 18, "end": 18,
"type": "MustacheTag", "type": "MustacheTag",
"expression": { "expression": {
"type": "Identifier",
"start": 12, "start": 12,
"end": 16, "end": 16,
"type": "Identifier",
"name": "name" "name": "name"
} }
}, },
@ -40,4 +41,4 @@
}, },
"css": null, "css": null,
"js": null "js": null
} }

@ -1,4 +1,5 @@
{ {
"hash": 611274658,
"html": { "html": {
"start": 0, "start": 0,
"end": 17, "end": 17,
@ -23,4 +24,4 @@
}, },
"css": null, "css": null,
"js": null "js": null
} }

@ -1,23 +1,28 @@
{ {
"html": { "hash": 825536165,
"start": 0, "html": {
"end": 15, "start": 0,
"type": "Fragment", "end": 15,
"children": [{ "type": "Fragment",
"attributes": [{ "children": [
"end": 14, {
"name": "html", "start": 0,
"start": 10, "end": 15,
"type": "Attribute", "type": "Element",
"value": true "name": "!doctype",
}], "attributes": [
"children": [], {
"end": 15, "start": 10,
"name": "!doctype", "end": 14,
"start": 0, "type": "Attribute",
"type": "Element" "name": "html",
}] "value": true
}, }
"css": null, ],
"js": null "children": []
} }
]
},
"css": null,
"js": null
}

@ -1,4 +1,5 @@
{ {
"hash": 4260626221,
"html": { "html": {
"start": 0, "start": 0,
"end": 101, "end": 101,
@ -16,48 +17,48 @@
"type": "EventHandler", "type": "EventHandler",
"name": "click", "name": "click",
"expression": { "expression": {
"type": "CallExpression",
"start": 18, "start": 18,
"end": 44, "end": 44,
"type": "CallExpression",
"callee": { "callee": {
"type": "Identifier",
"start": 18, "start": 18,
"end": 21, "end": 21,
"type": "Identifier",
"name": "set" "name": "set"
}, },
"arguments": [ "arguments": [
{ {
"type": "ObjectExpression",
"start": 22, "start": 22,
"end": 43, "end": 43,
"type": "ObjectExpression",
"properties": [ "properties": [
{ {
"type": "Property",
"start": 24, "start": 24,
"end": 41, "end": 41,
"type": "Property",
"kind": "init",
"computed": false,
"method": false, "method": false,
"shorthand": false, "shorthand": false,
"computed": false,
"key": { "key": {
"type": "Identifier",
"start": 24, "start": 24,
"end": 31, "end": 31,
"type": "Identifier",
"name": "visible" "name": "visible"
}, },
"value": { "value": {
"type": "UnaryExpression",
"start": 33, "start": 33,
"end": 41, "end": 41,
"type": "UnaryExpression",
"operator": "!", "operator": "!",
"prefix": true, "prefix": true,
"argument": { "argument": {
"type": "Identifier",
"start": 34, "start": 34,
"end": 41, "end": 41,
"type": "Identifier",
"name": "visible" "name": "visible"
} }
} },
"kind": "init"
} }
] ]
} }
@ -67,10 +68,10 @@
], ],
"children": [ "children": [
{ {
"data": "toggle",
"start": 46, "start": 46,
"end": 52, "end": 52,
"type": "Text" "type": "Text",
"data": "toggle"
} }
] ]
}, },
@ -85,10 +86,10 @@
"end": 101, "end": 101,
"type": "IfBlock", "type": "IfBlock",
"expression": { "expression": {
"end": 76, "type": "Identifier",
"name": "visible",
"start": 69, "start": 69,
"type": "Identifier" "end": 76,
"name": "visible"
}, },
"children": [ "children": [
{ {
@ -99,10 +100,10 @@
"attributes": [], "attributes": [],
"children": [ "children": [
{ {
"data": "hello!",
"end": 89,
"start": 83, "start": 83,
"type": "Text" "end": 89,
"type": "Text",
"data": "hello!"
} }
] ]
} }
@ -112,4 +113,4 @@
}, },
"css": null, "css": null,
"js": null "js": null
} }

@ -1,4 +1,5 @@
{ {
"hash": 3134964533,
"html": { "html": {
"start": 0, "start": 0,
"end": 56, "end": 56,
@ -9,9 +10,9 @@
"end": 56, "end": 56,
"type": "IfBlock", "type": "IfBlock",
"expression": { "expression": {
"type": "Identifier",
"start": 6, "start": 6,
"end": 9, "end": 9,
"type": "Identifier",
"name": "foo" "name": "foo"
}, },
"children": [ "children": [
@ -58,4 +59,4 @@
}, },
"css": null, "css": null,
"js": null "js": null
} }

@ -1,4 +1,5 @@
{ {
"hash": 985817334,
"html": { "html": {
"start": 0, "start": 0,
"end": 93, "end": 93,
@ -9,20 +10,20 @@
"end": 93, "end": 93,
"type": "IfBlock", "type": "IfBlock",
"expression": { "expression": {
"type": "BinaryExpression",
"start": 6, "start": 6,
"end": 12, "end": 12,
"type": "BinaryExpression",
"operator": ">",
"left": { "left": {
"type": "Identifier",
"start": 6, "start": 6,
"end": 7, "end": 7,
"type": "Identifier",
"name": "x" "name": "x"
}, },
"operator": ">",
"right": { "right": {
"type": "Literal",
"start": 10, "start": 10,
"end": 12, "end": 12,
"type": "Literal",
"value": 10, "value": 10,
"raw": "10" "raw": "10"
} }
@ -55,20 +56,20 @@
"type": "IfBlock", "type": "IfBlock",
"elseif": true, "elseif": true,
"expression": { "expression": {
"type": "BinaryExpression",
"start": 53, "start": 53,
"end": 58, "end": 58,
"type": "BinaryExpression",
"operator": "<",
"left": { "left": {
"type": "Identifier",
"start": 53, "start": 53,
"end": 54, "end": 54,
"type": "Identifier",
"name": "x" "name": "x"
}, },
"operator": "<",
"right": { "right": {
"type": "Literal",
"start": 57, "start": 57,
"end": 58, "end": 58,
"type": "Literal",
"value": 5, "value": 5,
"raw": "5" "raw": "5"
} }
@ -98,4 +99,4 @@
}, },
"css": null, "css": null,
"js": null "js": null
} }

@ -1,4 +1,5 @@
{ {
"hash": 2374871934,
"html": { "html": {
"start": 0, "start": 0,
"end": 21, "end": 21,
@ -9,9 +10,9 @@
"end": 21, "end": 21,
"type": "IfBlock", "type": "IfBlock",
"expression": { "expression": {
"type": "Identifier",
"start": 6, "start": 6,
"end": 9, "end": 9,
"type": "Identifier",
"name": "foo" "name": "foo"
}, },
"children": [ "children": [
@ -27,4 +28,4 @@
}, },
"css": null, "css": null,
"js": null "js": null
} }

@ -1,5 +1,5 @@
{ {
"hash": 3806276940, "hash": 126082492,
"html": { "html": {
"start": 0, "start": 0,
"end": 31, "end": 31,
@ -12,6 +12,12 @@
"name": "ul", "name": "ul",
"attributes": [], "attributes": [],
"children": [ "children": [
{
"start": 4,
"end": 6,
"type": "Text",
"data": "\n\t"
},
{ {
"start": 6, "start": 6,
"end": 13, "end": 13,
@ -23,7 +29,7 @@
"start": 10, "start": 10,
"end": 13, "end": 13,
"type": "Text", "type": "Text",
"data": "a" "data": "a\n\t"
} }
] ]
}, },
@ -38,7 +44,7 @@
"start": 17, "start": 17,
"end": 20, "end": 20,
"type": "Text", "type": "Text",
"data": "b" "data": "b\n\t"
} }
] ]
}, },

@ -1,5 +1,5 @@
{ {
"hash": 2678229240, "hash": 4049070444,
"html": { "html": {
"start": 0, "start": 0,
"end": 19, "end": 19,

@ -1,4 +1,5 @@
{ {
"hash": 183399343,
"html": { "html": {
"start": 0, "start": 0,
"end": 30, "end": 30,
@ -11,14 +12,20 @@
"name": "p", "name": "p",
"attributes": [], "attributes": [],
"children": [ "children": [
{
"start": 3,
"end": 4,
"type": "Text",
"data": " "
},
{ {
"start": 4, "start": 4,
"end": 14, "end": 14,
"type": "RawMustacheTag", "type": "RawMustacheTag",
"expression": { "expression": {
"type": "Identifier",
"start": 7, "start": 7,
"end": 11, "end": 11,
"type": "Identifier",
"name": "raw1" "name": "raw1"
} }
}, },
@ -33,11 +40,17 @@
"end": 25, "end": 25,
"type": "RawMustacheTag", "type": "RawMustacheTag",
"expression": { "expression": {
"type": "Identifier",
"start": 18, "start": 18,
"end": 22, "end": 22,
"type": "Identifier",
"name": "raw2" "name": "raw2"
} }
},
{
"start": 25,
"end": 26,
"type": "Text",
"data": " "
} }
] ]
} }
@ -45,4 +58,4 @@
}, },
"css": null, "css": null,
"js": null "js": null
} }

@ -1,4 +1,5 @@
{ {
"hash": 850398275,
"html": { "html": {
"start": 0, "start": 0,
"end": 25, "end": 25,
@ -23,4 +24,4 @@
}, },
"css": null, "css": null,
"js": null "js": null
} }

@ -1,4 +1,5 @@
{ {
"hash": 3451341610,
"html": { "html": {
"start": 0, "start": 0,
"end": 11, "end": 11,
@ -11,9 +12,15 @@
"name": "div", "name": "div",
"attributes": [], "attributes": [],
"children": [] "children": []
},
{
"start": 11,
"end": 13,
"type": "Text",
"data": "\n\n"
} }
] ]
}, },
"css": null, "css": null,
"js": null "js": null
} }

@ -1,4 +1,5 @@
{ {
"hash": 1378757574,
"html": { "html": {
"start": 0, "start": 0,
"end": 24, "end": 24,
@ -22,9 +23,9 @@
"end": 18, "end": 18,
"type": "MustacheTag", "type": "MustacheTag",
"expression": { "expression": {
"type": "Identifier",
"start": 12, "start": 12,
"end": 16, "end": 16,
"type": "Identifier",
"name": "name" "name": "name"
} }
}, },
@ -35,6 +36,12 @@
"data": "!" "data": "!"
} }
] ]
},
{
"start": 24,
"end": 26,
"type": "Text",
"data": "\n\n"
} }
] ]
}, },
@ -49,73 +56,73 @@
"end": 134, "end": 134,
"body": [ "body": [
{ {
"type": "ExportDefaultDeclaration",
"start": 36,
"end": 95,
"declaration": { "declaration": {
"type": "ObjectExpression",
"start": 51, "start": 51,
"end": 94, "end": 94,
"type": "ObjectExpression",
"properties": [ "properties": [
{ {
"type": "Property",
"start": 55, "start": 55,
"end": 91, "end": 91,
"type": "Property", "method": false,
"shorthand": false,
"computed": false, "computed": false,
"key": { "key": {
"type": "Identifier",
"start": 55, "start": 55,
"end": 59, "end": 59,
"type": "Identifier",
"name": "data" "name": "data"
}, },
"kind": "init",
"method": false,
"shorthand": false,
"value": { "value": {
"type": "ArrowFunctionExpression",
"start": 61, "start": 61,
"end": 91, "end": 91,
"type": "ArrowFunctionExpression", "id": null,
"generator": false,
"expression": true,
"async": false, "async": false,
"params": [],
"body": { "body": {
"type": "ObjectExpression",
"start": 68, "start": 68,
"end": 90, "end": 90,
"type": "ObjectExpression",
"properties": [ "properties": [
{ {
"type": "Property",
"start": 73, "start": 73,
"end": 86, "end": 86,
"type": "Property", "method": false,
"shorthand": false,
"computed": false, "computed": false,
"key": { "key": {
"type": "Identifier",
"start": 73, "start": 73,
"end": 77, "end": 77,
"type": "Identifier",
"name": "name" "name": "name"
}, },
"kind": "init",
"method": false,
"shorthand": false,
"value": { "value": {
"type": "Literal",
"start": 79, "start": 79,
"end": 86, "end": 86,
"type": "Literal", "value": "world",
"raw": "'world'", "raw": "'world'"
"value": "world" },
} "kind": "init"
} }
] ]
}, }
"expression": true, },
"generator": false, "kind": "init"
"id": null,
"params": []
}
} }
] ]
}, }
"end": 95,
"start": 36,
"type": "ExportDefaultDeclaration"
} }
], ],
"sourceType": "module" "sourceType": "module"
} }
} }
} }

@ -1,4 +1,5 @@
{ {
"hash": 619854804,
"html": { "html": {
"start": 0, "start": 0,
"end": 24, "end": 24,
@ -22,9 +23,9 @@
"end": 18, "end": 18,
"type": "MustacheTag", "type": "MustacheTag",
"expression": { "expression": {
"type": "Identifier",
"start": 12, "start": 12,
"end": 16, "end": 16,
"type": "Identifier",
"name": "name" "name": "name"
} }
}, },
@ -35,6 +36,12 @@
"data": "!" "data": "!"
} }
] ]
},
{
"start": 24,
"end": 26,
"type": "Text",
"data": "\n\n"
} }
] ]
}, },
@ -49,73 +56,73 @@
"end": 123, "end": 123,
"body": [ "body": [
{ {
"type": "ExportDefaultDeclaration",
"start": 36,
"end": 95,
"declaration": { "declaration": {
"type": "ObjectExpression",
"start": 51, "start": 51,
"end": 94, "end": 94,
"type": "ObjectExpression",
"properties": [ "properties": [
{ {
"type": "Property",
"start": 55, "start": 55,
"end": 91, "end": 91,
"type": "Property", "method": false,
"shorthand": false,
"computed": false, "computed": false,
"key": { "key": {
"type": "Identifier",
"start": 55, "start": 55,
"end": 59, "end": 59,
"type": "Identifier",
"name": "data" "name": "data"
}, },
"kind": "init",
"method": false,
"shorthand": false,
"value": { "value": {
"type": "ArrowFunctionExpression",
"start": 61, "start": 61,
"end": 91, "end": 91,
"type": "ArrowFunctionExpression", "id": null,
"generator": false,
"expression": true,
"async": false, "async": false,
"params": [],
"body": { "body": {
"type": "ObjectExpression",
"start": 68, "start": 68,
"end": 90, "end": 90,
"type": "ObjectExpression",
"properties": [ "properties": [
{ {
"type": "Property",
"start": 73, "start": 73,
"end": 86, "end": 86,
"type": "Property", "method": false,
"shorthand": false,
"computed": false, "computed": false,
"key": { "key": {
"type": "Identifier",
"start": 73, "start": 73,
"end": 77, "end": 77,
"type": "Identifier",
"name": "name" "name": "name"
}, },
"kind": "init",
"method": false,
"shorthand": false,
"value": { "value": {
"type": "Literal",
"start": 79, "start": 79,
"end": 86, "end": 86,
"type": "Literal", "value": "world",
"raw": "'world'", "raw": "'world'"
"value": "world" },
} "kind": "init"
} }
] ]
}, }
"expression": true, },
"generator": false, "kind": "init"
"id": null,
"params": []
}
} }
] ]
}, }
"end": 95,
"start": 36,
"type": "ExportDefaultDeclaration"
} }
], ],
"sourceType": "module" "sourceType": "module"
} }
} }
} }

@ -1,4 +1,5 @@
{ {
"hash": 388108696,
"html": { "html": {
"start": 0, "start": 0,
"end": 24, "end": 24,
@ -22,9 +23,9 @@
"end": 18, "end": 18,
"type": "MustacheTag", "type": "MustacheTag",
"expression": { "expression": {
"type": "Identifier",
"start": 12, "start": 12,
"end": 16, "end": 16,
"type": "Identifier",
"name": "name" "name": "name"
} }
}, },
@ -35,6 +36,12 @@
"data": "!" "data": "!"
} }
] ]
},
{
"start": 24,
"end": 26,
"type": "Text",
"data": "\n\n"
} }
] ]
}, },
@ -49,73 +56,73 @@
"end": 96, "end": 96,
"body": [ "body": [
{ {
"type": "ExportDefaultDeclaration",
"start": 36,
"end": 95,
"declaration": { "declaration": {
"type": "ObjectExpression",
"start": 51, "start": 51,
"end": 94, "end": 94,
"type": "ObjectExpression",
"properties": [ "properties": [
{ {
"type": "Property",
"start": 55, "start": 55,
"end": 91, "end": 91,
"type": "Property", "method": false,
"shorthand": false,
"computed": false, "computed": false,
"key": { "key": {
"type": "Identifier",
"start": 55, "start": 55,
"end": 59, "end": 59,
"type": "Identifier",
"name": "data" "name": "data"
}, },
"kind": "init",
"method": false,
"shorthand": false,
"value": { "value": {
"type": "ArrowFunctionExpression",
"start": 61, "start": 61,
"end": 91, "end": 91,
"type": "ArrowFunctionExpression", "id": null,
"generator": false,
"expression": true,
"async": false, "async": false,
"params": [],
"body": { "body": {
"type": "ObjectExpression",
"start": 68, "start": 68,
"end": 90, "end": 90,
"type": "ObjectExpression",
"properties": [ "properties": [
{ {
"type": "Property",
"start": 73, "start": 73,
"end": 86, "end": 86,
"type": "Property", "method": false,
"shorthand": false,
"computed": false, "computed": false,
"key": { "key": {
"type": "Identifier",
"start": 73, "start": 73,
"end": 77, "end": 77,
"type": "Identifier",
"name": "name" "name": "name"
}, },
"kind": "init",
"method": false,
"shorthand": false,
"value": { "value": {
"type": "Literal",
"start": 79, "start": 79,
"end": 86, "end": 86,
"type": "Literal", "value": "world",
"raw": "'world'", "raw": "'world'"
"value": "world" },
} "kind": "init"
} }
] ]
}, }
"expression": true, },
"generator": false, "kind": "init"
"id": null,
"params": []
}
} }
] ]
}, }
"end": 95,
"start": 36,
"type": "ExportDefaultDeclaration"
} }
], ],
"sourceType": "module" "sourceType": "module"
} }
} }
} }

@ -1,4 +1,5 @@
{ {
"hash": 4200201687,
"html": { "html": {
"start": 0, "start": 0,
"end": 6, "end": 6,
@ -16,4 +17,4 @@
}, },
"css": null, "css": null,
"js": null "js": null
} }

@ -1,5 +1,5 @@
{ {
"hash": 1792372370, "hash": 216762188,
"html": { "html": {
"start": 0, "start": 0,
"end": 57, "end": 57,

@ -1,4 +1,5 @@
{ {
"hash": 1185019088,
"html": { "html": {
"start": 0, "start": 0,
"end": 30, "end": 30,
@ -11,14 +12,20 @@
"name": "p", "name": "p",
"attributes": [], "attributes": [],
"children": [ "children": [
{
"start": 3,
"end": 4,
"type": "Text",
"data": " "
},
{ {
"start": 4, "start": 4,
"end": 9, "end": 9,
"type": "MustacheTag", "type": "MustacheTag",
"expression": { "expression": {
"type": "Identifier",
"start": 6, "start": 6,
"end": 7, "end": 7,
"type": "Identifier",
"name": "a" "name": "a"
} }
}, },
@ -33,9 +40,9 @@
"end": 15, "end": 15,
"type": "MustacheTag", "type": "MustacheTag",
"expression": { "expression": {
"type": "Identifier",
"start": 12, "start": 12,
"end": 13, "end": 13,
"type": "Identifier",
"name": "b" "name": "b"
} }
}, },
@ -50,9 +57,9 @@
"end": 23, "end": 23,
"type": "MustacheTag", "type": "MustacheTag",
"expression": { "expression": {
"type": "Identifier",
"start": 20, "start": 20,
"end": 21, "end": 21,
"type": "Identifier",
"name": "c" "name": "c"
} }
}, },
@ -60,7 +67,7 @@
"start": 23, "start": 23,
"end": 26, "end": 26,
"type": "Text", "type": "Text",
"data": " :" "data": " : "
} }
] ]
} }
@ -68,4 +75,4 @@
}, },
"css": null, "css": null,
"js": null "js": null
} }

@ -1,5 +1,5 @@
{ {
"hash": 3618147195, "hash": 2992234421,
"html": { "html": {
"start": 0, "start": 0,
"end": 63, "end": 63,

@ -1,5 +1,5 @@
{ {
"hash": 1535528483, "hash": 503236647,
"html": { "html": {
"start": 0, "start": 0,
"end": 27, "end": 27,

@ -1,5 +1,5 @@
{ {
"hash": 3160753914, "hash": 3731674194,
"html": { "html": {
"start": 0, "start": 0,
"end": 43, "end": 43,
@ -19,18 +19,17 @@
"intro": true, "intro": true,
"outro": false, "outro": false,
"expression": { "expression": {
"start": 15,
"end": 27,
"type": "ObjectExpression", "type": "ObjectExpression",
"start": 15,
"end": 27,
"properties": [ "properties": [
{ {
"type": "Property",
"start": 16, "start": 16,
"end": 26, "end": 26,
"type": "Property",
"method": false, "method": false,
"computed": false,
"shorthand": false, "shorthand": false,
"kind": "init", "computed": false,
"key": { "key": {
"type": "Identifier", "type": "Identifier",
"start": 16, "start": 16,
@ -38,12 +37,13 @@
"name": "opacity" "name": "opacity"
}, },
"value": { "value": {
"type": "Literal",
"start": 25, "start": 25,
"end": 26, "end": 26,
"type": "Literal",
"value": 0, "value": 0,
"raw": "0" "raw": "0"
} },
"kind": "init"
} }
] ]
} }

@ -1,9 +1,16 @@
{ {
"hash": 424837432,
"html": { "html": {
"start": 6, "start": 6,
"end": 36, "end": 36,
"type": "Fragment", "type": "Fragment",
"children": [ "children": [
{
"start": 0,
"end": 6,
"type": "Text",
"data": "\n\n\t\t\t\t"
},
{ {
"start": 6, "start": 6,
"end": 36, "end": 36,
@ -23,4 +30,4 @@
}, },
"css": null, "css": null,
"js": null "js": null
} }

@ -0,0 +1 @@
<h1>Hello <strong>{{name}}! </strong><span>How are you?</span></h1>

@ -0,0 +1,68 @@
{
"hash": 2961389466,
"html": {
"start": 0,
"end": 67,
"type": "Fragment",
"children": [
{
"start": 0,
"end": 67,
"type": "Element",
"name": "h1",
"attributes": [],
"children": [
{
"start": 4,
"end": 10,
"type": "Text",
"data": "Hello "
},
{
"start": 10,
"end": 37,
"type": "Element",
"name": "strong",
"attributes": [],
"children": [
{
"start": 18,
"end": 26,
"type": "MustacheTag",
"expression": {
"type": "Identifier",
"start": 20,
"end": 24,
"name": "name"
}
},
{
"start": 26,
"end": 28,
"type": "Text",
"data": "! "
}
]
},
{
"start": 37,
"end": 62,
"type": "Element",
"name": "span",
"attributes": [],
"children": [
{
"start": 43,
"end": 55,
"type": "Text",
"data": "How are you?"
}
]
}
]
}
]
},
"css": null,
"js": null
}

@ -1,14 +1,17 @@
{ {
"html": { "hash": 3659433152,
"start": 0, "html": {
"end": 9, "start": 0,
"type": "Fragment", "end": 9,
"children": [ "type": "Fragment",
{ "children": [
"start": 0, {
"end": 9, "start": 0,
"type": "YieldTag" "end": 9,
} "type": "YieldTag"
] }
} ]
} },
"css": null,
"js": null
}

@ -0,0 +1,13 @@
// this file will replace all the expected.js and expected-bundle.js files with
// their _actual equivalents. Only use it when you're sure that you haven't
// broken anything!
const fs = require("fs");
const glob = require("glob");
glob.sync("samples/*/_actual.json", { cwd: __dirname }).forEach(file => {
const actual = fs.readFileSync(`${__dirname}/${file}`, "utf-8");
fs.writeFileSync(
`${__dirname}/${file.replace("_actual", "output")}`,
actual
);
});

@ -0,0 +1,3 @@
export default {
html: `#foo`
};

@ -0,0 +1,3 @@
export default {
html: `@foo`
};

@ -0,0 +1,12 @@
export default {
data: {
name: 'world'
},
test ( assert, component, target ) {
assert.equal(
target.textContent,
`Hello world! How are you?`
);
}
};

@ -0,0 +1 @@
<h1>Hello <strong>{{name}}! </strong><span>How are you?</span></h1>

@ -34,7 +34,7 @@ describe("ssr", () => {
// add .solo to a sample directory name to only run that test, or // add .solo to a sample directory name to only run that test, or
// .show to always show the output. or both // .show to always show the output. or both
const solo = /\.solo/.test(dir); const solo = /\.solo/.test(dir);
let show = /\.show/.test(dir); const show = /\.show/.test(dir);
if (solo && process.env.CI) { if (solo && process.env.CI) {
throw new Error("Forgot to remove `solo: true` from test"); throw new Error("Forgot to remove `solo: true` from test");
@ -42,35 +42,31 @@ describe("ssr", () => {
(solo ? it.only : it)(dir, () => { (solo ? it.only : it)(dir, () => {
dir = path.resolve("test/server-side-rendering/samples", dir); dir = path.resolve("test/server-side-rendering/samples", dir);
const component = require(`${dir}/main.html`); try {
const component = require(`${dir}/main.html`);
const expectedHtml = tryToReadFile(`${dir}/_expected.html`); const expectedHtml = tryToReadFile(`${dir}/_expected.html`);
const expectedCss = tryToReadFile(`${dir}/_expected.css`) || ""; const expectedCss = tryToReadFile(`${dir}/_expected.css`) || "";
const data = tryToLoadJson(`${dir}/data.json`); const data = tryToLoadJson(`${dir}/data.json`);
let html;
let css;
let error;
try { const html = component.render(data);
html = component.render(data); const css = component.renderCss().css;
css = component.renderCss().css;
} catch (e) {
show = true;
error = e;
}
if (show) showOutput(dir, { generate: "ssr" }); fs.writeFileSync(`${dir}/_actual.html`, html);
if (error) throw error; if (css) fs.writeFileSync(`${dir}/_actual.css`, css);
fs.writeFileSync(`${dir}/_actual.html`, html); assert.htmlEqual(html, expectedHtml);
if (css) fs.writeFileSync(`${dir}/_actual.css`, css); assert.equal(
css.replace(/^\s+/gm, ""),
expectedCss.replace(/^\s+/gm, "")
);
assert.htmlEqual(html, expectedHtml); if (show) showOutput(dir, { generate: 'ssr' });
assert.equal( } catch (err) {
css.replace(/^\s+/gm, ""), showOutput(dir, { generate: 'ssr' });
expectedCss.replace(/^\s+/gm, "") throw err;
); }
}); });
}); });

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

@ -1 +1,3 @@
<div><p>foo: ''</p></div> <div>
<p>foo: ''</p>
</div>

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

@ -1 +1,3 @@
<div><p>Hello</p></div> <div>
<p>Hello</p>
</div>

@ -1 +1,3 @@
<div><p>i am a widget</p></div> <div>
<p>i am a widget</p>
</div>
Loading…
Cancel
Save