Merge pull request #401 from sveltejs/gh-400

Aliasing/deconflicting overhaul
pull/412/head
Rich Harris 8 years ago committed by GitHub
commit 059fb5f07d

@ -1,19 +1,18 @@
import MagicString, { Bundle } from 'magic-string'; import MagicString, { Bundle } from 'magic-string';
import { walk } from 'estree-walker'; import { walk } from 'estree-walker';
import isReference from '../utils/isReference.js'; import isReference from '../utils/isReference.js';
import counter from './shared/utils/counter.js';
import flattenReference from '../utils/flattenReference.js'; import flattenReference from '../utils/flattenReference.js';
import globalWhitelist from '../utils/globalWhitelist.js'; import globalWhitelist from '../utils/globalWhitelist.js';
import reservedNames from '../utils/reservedNames.js';
import getIntro from './shared/utils/getIntro.js'; import getIntro from './shared/utils/getIntro.js';
import getOutro from './shared/utils/getOutro.js'; import getOutro from './shared/utils/getOutro.js';
import annotateWithScopes from './annotateWithScopes.js'; import annotateWithScopes from './annotateWithScopes.js';
export default class Generator { export default class Generator {
constructor ( parsed, source, name, names, visitors, options ) { constructor ( parsed, source, name, visitors, options ) {
this.parsed = parsed; this.parsed = parsed;
this.source = source; this.source = source;
this.name = name; this.name = name;
this.names = names;
this.visitors = visitors; this.visitors = visitors;
this.options = options; this.options = options;
@ -31,15 +30,14 @@ export default class Generator {
this.elementDepth = 0; this.elementDepth = 0;
this.code = new MagicString( source ); this.code = new MagicString( source );
this.getUniqueName = counter( names );
this.cssId = parsed.css ? `svelte-${parsed.hash}` : ''; this.cssId = parsed.css ? `svelte-${parsed.hash}` : '';
this.usesRefs = false; this.usesRefs = false;
// allow compiler to deconflict user's `import { get } from 'whatever'` and // allow compiler to deconflict user's `import { get } from 'whatever'` and
// Svelte's builtin `import { get, ... } from 'svelte/shared.js'`; // Svelte's builtin `import { get, ... } from 'svelte/shared.js'`;
this.importedNames = new Set(); this.importedNames = new Set();
this._aliases = new Map();
this.aliases = new Map(); this._usedNames = new Set();
this._callbacks = new Map(); this._callbacks = new Map();
} }
@ -54,17 +52,12 @@ export default class Generator {
} }
alias ( name ) { alias ( name ) {
if ( !( this.aliases.has( name ) ) ) { if ( this._aliases.has( name ) ) {
let alias = name; return this._aliases.get( name );
let i = 1;
while ( alias in this.importedNames ) {
alias = `${name}$${i++}`;
} }
const alias = this.getUniqueName( name );
this.aliases.set( name, alias ); this._aliases.set( name, alias );
} return alias;
return this.aliases.get( name );
} }
contextualise ( expression, isEventHandler ) { contextualise ( expression, isEventHandler ) {
@ -124,7 +117,7 @@ export default class Generator {
} }
} }
if ( globalWhitelist[ name ] ) { if ( globalWhitelist.has( name ) ) {
code.prependRight( node.start, `( '${name}' in root ? root.` ); code.prependRight( node.start, `( '${name}' in root ? root.` );
code.appendLeft( node.object ? node.object.end : node.end, ` : ${name} )` ); code.appendLeft( node.object ? node.object.end : node.end, ` : ${name} )` );
} else { } else {
@ -244,8 +237,21 @@ export default class Generator {
}; };
} }
getUniqueNameMaker () { getUniqueName ( name ) {
return counter( this.names ); let alias = name;
for ( let i = 1; reservedNames.has( alias ) || this.importedNames.has( alias ) || this._usedNames.has( alias ); alias = `${name}$${i++}` );
this._usedNames.add( alias );
return alias;
}
getUniqueNameMaker ( params ) {
const localUsedNames = new Set( params );
return name => {
let alias = name;
for ( let i = 1; reservedNames.has( alias ) || this.importedNames.has( alias ) || this._usedNames.has( alias ) || localUsedNames.has( alias ); alias = `${name}$${i++}` );
localUsedNames.add( alias );
return alias;
};
} }
parseJs () { parseJs () {
@ -272,7 +278,7 @@ export default class Generator {
imports.push( node ); imports.push( node );
this.code.remove( a, b ); this.code.remove( a, b );
node.specifiers.forEach( specifier => { node.specifiers.forEach( specifier => {
this.importedNames[ specifier.local.name ] = true; this.importedNames.add( specifier.local.name );
}); });
} }
} }

@ -9,8 +9,8 @@ import Generator from '../Generator.js';
import * as shared from '../../shared/index.js'; import * as shared from '../../shared/index.js';
class DomGenerator extends Generator { class DomGenerator extends Generator {
constructor ( parsed, source, name, names, visitors, options ) { constructor ( parsed, source, name, visitors, options ) {
super( parsed, source, name, names, visitors, options ); super( parsed, source, name, visitors, options );
this.renderers = []; this.renderers = [];
this.uses = new Set(); this.uses = new Set();
@ -58,7 +58,11 @@ class DomGenerator extends Generator {
const properties = new CodeBuilder(); const properties = new CodeBuilder();
if ( fragment.key ) properties.addBlock( `key: key,` ); let localKey;
if ( fragment.key ) {
localKey = fragment.getUniqueName( 'key' );
properties.addBlock( `key: ${localKey},` );
}
if ( fragment.builders.mount.isEmpty() ) { if ( fragment.builders.mount.isEmpty() ) {
properties.addBlock( `mount: ${this.helper( 'noop' )},` ); properties.addBlock( `mount: ${this.helper( 'noop' )},` );
@ -93,7 +97,7 @@ class DomGenerator extends Generator {
} }
this.renderers.push( deindent` this.renderers.push( deindent`
function ${fragment.name} ( ${fragment.params.join( ', ' )}, component${fragment.key ? `, key` : ''} ) { function ${fragment.name} ( ${fragment.params.join( ', ' )}, ${fragment.component}${fragment.key ? `, ${localKey}` : ''} ) {
${fragment.builders.init} ${fragment.builders.init}
return { return {
@ -122,7 +126,7 @@ class DomGenerator extends Generator {
target: 'target', target: 'target',
localElementDepth: 0, localElementDepth: 0,
builders: getBuilders(), builders: getBuilders(),
getUniqueName: this.getUniqueNameMaker() getUniqueName: this.getUniqueNameMaker( this.current.params )
}); });
// walk the children here // walk the children here
@ -145,11 +149,11 @@ class DomGenerator extends Generator {
} }
} }
export default function dom ( parsed, source, options, names ) { export default function dom ( parsed, source, options ) {
const format = options.format || 'es'; const format = options.format || 'es';
const name = options.name || 'SvelteComponent'; const name = options.name || 'SvelteComponent';
const generator = new DomGenerator( parsed, source, name, names, visitors, options ); const generator = new DomGenerator( parsed, source, name, visitors, options );
const { computations, defaultExport, templateProperties } = generator.parseJs(); const { computations, defaultExport, templateProperties } = generator.parseJs();
@ -196,6 +200,9 @@ export default function dom ( parsed, source, options, names ) {
} }
} }
const getUniqueName = generator.getUniqueNameMaker( [ 'root' ] );
const component = getUniqueName( 'component' );
generator.push({ generator.push({
name: generator.alias( 'renderMainFragment' ), name: generator.alias( 'renderMainFragment' ),
namespace, namespace,
@ -203,6 +210,8 @@ export default function dom ( parsed, source, options, names ) {
localElementDepth: 0, localElementDepth: 0,
key: null, key: null,
component,
contexts: new Map(), contexts: new Map(),
indexes: new Map(), indexes: new Map(),
@ -211,7 +220,7 @@ export default function dom ( parsed, source, options, names ) {
listNames: new Map(), listNames: new Map(),
builders: getBuilders(), builders: getBuilders(),
getUniqueName: generator.getUniqueNameMaker() getUniqueName,
}); });
parsed.html.children.forEach( node => generator.visit( node ) ); parsed.html.children.forEach( node => generator.visit( node ) );
@ -418,16 +427,16 @@ export default function dom ( parsed, source, options, names ) {
} }
const names = Array.from( generator.uses ).map( name => { const names = Array.from( generator.uses ).map( name => {
return name !== generator.aliases.get( name ) ? `${name} as ${generator.aliases.get( name )}` : name; return name !== generator.alias( name ) ? `${name} as ${generator.alias( name )}` : name;
}); });
builders.main.addLineAtStart( builders.main.addLineAtStart(
`import { ${names.join( ', ' )} } from ${JSON.stringify( sharedPath )}` `import { ${names.join( ', ' )} } from ${JSON.stringify( sharedPath )};`
); );
} else { } else {
generator.uses.forEach( key => { generator.uses.forEach( key => {
const fn = shared[ key ]; // eslint-disable-line import/namespace const fn = shared[ key ]; // eslint-disable-line import/namespace
builders.main.addBlock( fn.toString().replace( /^function [^(]*/, 'function ' + generator.aliases.get( key ) ) ); builders.main.addBlock( fn.toString().replace( /^function [^(]*/, 'function ' + generator.alias( key ) ) );
}); });
} }

@ -9,11 +9,12 @@ function capDown ( name ) {
export default { export default {
enter ( generator, node ) { enter ( generator, node ) {
const hasChildren = node.children.length > 0; const hasChildren = node.children.length > 0;
const name = generator.current.getUniqueName( capDown( node.name === ':Self' ? generator.name : node.name ) ); const { current } = generator;
const name = current.getUniqueName( capDown( node.name === ':Self' ? generator.name : node.name ) );
const local = { const local = {
name, name,
namespace: generator.current.namespace, namespace: current.namespace,
isComponent: true, isComponent: true,
allUsedContexts: [], allUsedContexts: [],
@ -22,7 +23,7 @@ export default {
update: new CodeBuilder() update: new CodeBuilder()
}; };
const isToplevel = generator.current.localElementDepth === 0; const isToplevel = current.localElementDepth === 0;
generator.hasComponents = true; generator.hasComponents = true;
@ -32,8 +33,8 @@ export default {
const initialProps = local.allUsedContexts.map( contextName => { const initialProps = local.allUsedContexts.map( contextName => {
if ( contextName === 'root' ) return `root: root`; if ( contextName === 'root' ) return `root: root`;
const listName = generator.current.listNames.get( contextName ); const listName = current.listNames.get( contextName );
const indexName = generator.current.indexNames.get( contextName ); const indexName = current.indexNames.get( contextName );
return `${listName}: ${listName},\n${indexName}: ${indexName}`; return `${listName}: ${listName},\n${indexName}: ${indexName}`;
}).join( ',\n' ); }).join( ',\n' );
@ -41,8 +42,8 @@ export default {
const updates = local.allUsedContexts.map( contextName => { const updates = local.allUsedContexts.map( contextName => {
if ( contextName === 'root' ) return `${name}._context.root = root;`; if ( contextName === 'root' ) return `${name}._context.root = root;`;
const listName = generator.current.listNames.get( contextName ); const listName = current.listNames.get( contextName );
const indexName = generator.current.indexNames.get( contextName ); const indexName = current.indexNames.get( contextName );
return `${name}._context.${listName} = ${listName};\n${name}._context.${indexName} = ${indexName};`; return `${name}._context.${listName} = ${listName};\n${name}._context.${indexName} = ${indexName};`;
}).join( '\n' ); }).join( '\n' );
@ -57,26 +58,28 @@ export default {
} }
const componentInitProperties = [ const componentInitProperties = [
`target: ${!isToplevel ? generator.current.target: 'null'}`, `target: ${!isToplevel ? current.target: 'null'}`,
'_root: component._root || component' `_root: ${current.component}._root || ${current.component}`
]; ];
// Component has children, put them in a separate {{yield}} block // Component has children, put them in a separate {{yield}} block
if ( hasChildren ) { if ( hasChildren ) {
const yieldName = generator.getUniqueName( `render${name}YieldFragment` ); const yieldName = generator.getUniqueName( `render${name}YieldFragment` );
const params = generator.current.params.join( ', ' ); const params = current.params.join( ', ' );
generator.generateBlock( node, yieldName ); generator.generateBlock( node, yieldName );
generator.current.builders.init.addLine( const yieldFragment = current.getUniqueName( `${name}_yieldFragment` );
`var ${name}_yieldFragment = ${yieldName}( ${params}, component );`
current.builders.init.addLine(
`var ${yieldFragment} = ${yieldName}( ${params}, ${current.component} );`
); );
generator.current.builders.update.addLine( current.builders.update.addLine(
`${name}_yieldFragment.update( changed, ${params} );` `${yieldFragment}.update( changed, ${params} );`
); );
componentInitProperties.push( `_yield: ${name}_yieldFragment`); componentInitProperties.push( `_yield: ${yieldFragment}`);
} }
const statements = []; const statements = [];
@ -85,6 +88,7 @@ export default {
const initialProps = local.staticAttributes const initialProps = local.staticAttributes
.concat( local.dynamicAttributes ) .concat( local.dynamicAttributes )
.map( attribute => `${attribute.name}: ${attribute.value}` ); .map( attribute => `${attribute.name}: ${attribute.value}` );
const initialData = current.getUniqueName( `${name}_initialData` );
if ( initialProps.length ) { if ( initialProps.length ) {
statements.push( deindent` statements.push( deindent`
@ -93,17 +97,17 @@ export default {
}; };
` ); ` );
} else { } else {
statements.push( `var ${name}_initialData = {};` ); statements.push( `var ${initialData} = {};` );
} }
if ( local.bindings.length ) { if ( local.bindings.length ) {
const bindings = local.bindings.map( binding => { const bindings = local.bindings.map( binding => {
return `if ( ${binding.prop} in ${binding.obj} ) ${name}_initialData.${binding.name} = ${binding.value};`; return `if ( ${binding.prop} in ${binding.obj} ) ${initialData}.${binding.name} = ${binding.value};`;
}); });
statements.push( bindings.join( '\n' ) ); statements.push( bindings.join( '\n' ) );
} }
componentInitProperties.push(`data: ${name}_initialData`); componentInitProperties.push(`data: ${initialData}`);
} }
const expression = node.name === ':Self' ? generator.name : generator.importedComponents.get( node.name ) || `${generator.alias( 'template' )}.components.${node.name}`; const expression = node.name === ':Self' ? generator.name : generator.importedComponents.get( node.name ) || `${generator.alias( 'template' )}.components.${node.name}`;
@ -116,7 +120,7 @@ export default {
` ); ` );
if ( isToplevel ) { if ( isToplevel ) {
generator.current.builders.mount.addLine( `${name}._fragment.mount( target, anchor );` ); current.builders.mount.addLine( `${name}._fragment.mount( target, anchor );` );
} }
if ( local.dynamicAttributes.length ) { if ( local.dynamicAttributes.length ) {
@ -141,16 +145,16 @@ export default {
` ); ` );
} }
generator.current.builders.teardown.addLine( `${name}.destroy( ${isToplevel ? 'detach' : 'false'} );` ); current.builders.teardown.addLine( `${name}.destroy( ${isToplevel ? 'detach' : 'false'} );` );
generator.current.builders.init.addBlock( local.init ); current.builders.init.addBlock( local.init );
if ( !local.update.isEmpty() ) generator.current.builders.update.addBlock( local.update ); if ( !local.update.isEmpty() ) current.builders.update.addBlock( local.update );
generator.push({ generator.push({
namespace: local.namespace, namespace: local.namespace,
target: name, target: name,
parent: generator.current, parent: current,
localElementDepth: generator.current.localElementDepth + 1, localElementDepth: current.localElementDepth + 1,
key: null key: null
}); });
}, },

@ -2,19 +2,16 @@ import CodeBuilder from '../../../utils/CodeBuilder.js';
import deindent from '../../../utils/deindent.js'; import deindent from '../../../utils/deindent.js';
import getBuilders from '../utils/getBuilders.js'; import getBuilders from '../utils/getBuilders.js';
const reserved = new Set( [ 'component', 'root' ] );
export default { export default {
enter ( generator, node ) { enter ( generator, node ) {
const name = generator.getUniqueName( `eachBlock` ); const name = generator.getUniqueName( `eachBlock` );
const renderer = generator.getUniqueName( `renderEachBlock` ); const renderer = generator.getUniqueName( `renderEachBlock` );
const elseName = `${name}_else`; const elseName = generator.getUniqueName( `${name}_else` );
const iterations = `${name}_iterations`; const renderElse = generator.getUniqueName( `${renderer}_else` );
const renderElse = `${renderer}_else`;
const i = generator.current.getUniqueName( `i` ); const i = generator.current.getUniqueName( `i` );
const { params } = generator.current; const params = generator.current.params.join( ', ' );
const listName = `${name}_value`; const listName = generator.current.getUniqueName( `${name}_value` );
const isToplevel = generator.current.localElementDepth === 0; const isToplevel = generator.current.localElementDepth === 0;
@ -22,49 +19,55 @@ export default {
const { dependencies, snippet } = generator.contextualise( node.expression ); const { dependencies, snippet } = generator.contextualise( node.expression );
const anchor = `${name}_anchor`; const anchor = generator.current.getUniqueName( `${name}_anchor` );
generator.createAnchor( anchor ); generator.createAnchor( anchor );
generator.current.builders.init.addLine( `var ${name}_value = ${snippet};` ); const localVars = {};
generator.current.builders.init.addLine( `var ${iterations} = [];` );
if ( node.key ) generator.current.builders.init.addLine( `var ${name}_lookup = Object.create( null );` ); localVars.iteration = generator.current.getUniqueName( `${name}_iteration` );
localVars.iterations = generator.current.getUniqueName( `${name}_iterations` );
localVars._iterations = generator.current.getUniqueName( `_${name}_iterations` );
localVars.lookup = generator.current.getUniqueName( `${name}_lookup` );
localVars._lookup = generator.current.getUniqueName( `_${name}_lookup` );
generator.current.builders.init.addLine( `var ${listName} = ${snippet};` );
generator.current.builders.init.addLine( `var ${localVars.iterations} = [];` );
if ( node.key ) generator.current.builders.init.addLine( `var ${localVars.lookup} = Object.create( null );` );
if ( node.else ) generator.current.builders.init.addLine( `var ${elseName} = null;` ); if ( node.else ) generator.current.builders.init.addLine( `var ${elseName} = null;` );
const initialRender = new CodeBuilder(); const initialRender = new CodeBuilder();
const localVars = {};
if ( node.key ) { if ( node.key ) {
localVars.fragment = generator.current.getUniqueName( 'fragment' ); localVars.fragment = generator.current.getUniqueName( 'fragment' );
localVars.value = generator.current.getUniqueName( 'value' ); localVars.value = generator.current.getUniqueName( 'value' );
localVars.key = generator.current.getUniqueName( 'key' ); localVars.key = generator.current.getUniqueName( 'key' );
initialRender.addBlock( deindent` initialRender.addBlock( deindent`
var ${localVars.key} = ${name}_value[${i}].${node.key}; var ${localVars.key} = ${listName}[${i}].${node.key};
${name}_iterations[${i}] = ${name}_lookup[ ${localVars.key} ] = ${renderer}( ${params}, ${listName}, ${listName}[${i}], ${i}, component${node.key ? `, ${localVars.key}` : `` } ); ${localVars.iterations}[${i}] = ${localVars.lookup}[ ${localVars.key} ] = ${renderer}( ${params}, ${listName}, ${listName}[${i}], ${i}, ${generator.current.component}${node.key ? `, ${localVars.key}` : `` } );
` ); ` );
} else { } else {
initialRender.addLine( initialRender.addLine(
`${name}_iterations[${i}] = ${renderer}( ${params}, ${listName}, ${listName}[${i}], ${i}, component );` `${localVars.iterations}[${i}] = ${renderer}( ${params}, ${listName}, ${listName}[${i}], ${i}, ${generator.current.component} );`
); );
} }
if ( !isToplevel ) { if ( !isToplevel ) {
initialRender.addLine( initialRender.addLine(
`${name}_iterations[${i}].mount( ${anchor}.parentNode, ${anchor} );` `${localVars.iterations}[${i}].mount( ${anchor}.parentNode, ${anchor} );`
); );
} }
generator.current.builders.init.addBlock( deindent` generator.current.builders.init.addBlock( deindent`
for ( var ${i} = 0; ${i} < ${name}_value.length; ${i} += 1 ) { for ( var ${i} = 0; ${i} < ${listName}.length; ${i} += 1 ) {
${initialRender} ${initialRender}
} }
` ); ` );
if ( node.else ) { if ( node.else ) {
generator.current.builders.init.addBlock( deindent` generator.current.builders.init.addBlock( deindent`
if ( !${name}_value.length ) { if ( !${listName}.length ) {
${elseName} = ${renderElse}( ${params}, component ); ${elseName} = ${renderElse}( ${params}, ${generator.current.component} );
${!isToplevel ? `${elseName}.mount( ${anchor}.parentNode, ${anchor} );` : ''} ${!isToplevel ? `${elseName}.mount( ${anchor}.parentNode, ${anchor} );` : ''}
} }
` ); ` );
@ -72,8 +75,8 @@ export default {
if ( isToplevel ) { if ( isToplevel ) {
generator.current.builders.mount.addBlock( deindent` generator.current.builders.mount.addBlock( deindent`
for ( var ${i} = 0; ${i} < ${iterations}.length; ${i} += 1 ) { for ( var ${i} = 0; ${i} < ${localVars.iterations}.length; ${i} += 1 ) {
${iterations}[${i}].mount( ${anchor}.parentNode, ${anchor} ); ${localVars.iterations}[${i}].mount( ${anchor}.parentNode, ${anchor} );
} }
` ); ` );
if ( node.else ) { if ( node.else ) {
@ -87,65 +90,65 @@ export default {
if ( node.key ) { if ( node.key ) {
generator.current.builders.update.addBlock( deindent` generator.current.builders.update.addBlock( deindent`
var ${name}_value = ${snippet}; var ${listName} = ${snippet};
var _${name}_iterations = []; var ${localVars._iterations} = [];
var _${name}_lookup = Object.create( null ); var ${localVars._lookup} = Object.create( null );
var ${localVars.fragment} = document.createDocumentFragment(); var ${localVars.fragment} = document.createDocumentFragment();
// create new iterations as necessary // create new iterations as necessary
for ( var ${i} = 0; ${i} < ${name}_value.length; ${i} += 1 ) { for ( var ${i} = 0; ${i} < ${listName}.length; ${i} += 1 ) {
var ${localVars.value} = ${name}_value[${i}]; var ${localVars.value} = ${listName}[${i}];
var ${localVars.key} = ${localVars.value}.${node.key}; var ${localVars.key} = ${localVars.value}.${node.key};
if ( ${name}_lookup[ ${localVars.key} ] ) { if ( ${localVars.lookup}[ ${localVars.key} ] ) {
_${name}_iterations[${i}] = _${name}_lookup[ ${localVars.key} ] = ${name}_lookup[ ${localVars.key} ]; ${localVars._iterations}[${i}] = ${localVars._lookup}[ ${localVars.key} ] = ${localVars.lookup}[ ${localVars.key} ];
_${name}_lookup[ ${localVars.key} ].update( changed, ${params}, ${listName}, ${listName}[${i}], ${i} ); ${localVars._lookup}[ ${localVars.key} ].update( changed, ${params}, ${listName}, ${listName}[${i}], ${i} );
} else { } else {
_${name}_iterations[${i}] = _${name}_lookup[ ${localVars.key} ] = ${renderer}( ${params}, ${listName}, ${listName}[${i}], ${i}, component${node.key ? `, ${localVars.key}` : `` } ); ${localVars._iterations}[${i}] = ${localVars._lookup}[ ${localVars.key} ] = ${renderer}( ${params}, ${listName}, ${listName}[${i}], ${i}, ${generator.current.component}${node.key ? `, ${localVars.key}` : `` } );
} }
_${name}_iterations[${i}].mount( ${localVars.fragment}, null ); ${localVars._iterations}[${i}].mount( ${localVars.fragment}, null );
} }
// remove old iterations // remove old iterations
for ( var ${i} = 0; ${i} < ${name}_iterations.length; ${i} += 1 ) { for ( var ${i} = 0; ${i} < ${localVars.iterations}.length; ${i} += 1 ) {
var ${name}_iteration = ${name}_iterations[${i}]; var ${localVars.iteration} = ${localVars.iterations}[${i}];
if ( !_${name}_lookup[ ${name}_iteration.${localVars.key} ] ) { if ( !${localVars._lookup}[ ${localVars.iteration}.key ] ) {
${name}_iteration.teardown( true ); ${localVars.iteration}.teardown( true );
} }
} }
${name}_anchor.parentNode.insertBefore( ${localVars.fragment}, ${name}_anchor ); ${anchor}.parentNode.insertBefore( ${localVars.fragment}, ${anchor} );
${name}_iterations = _${name}_iterations; ${localVars.iterations} = ${localVars._iterations};
${name}_lookup = _${name}_lookup; ${localVars.lookup} = ${localVars._lookup};
` ); ` );
} else { } else {
generator.current.builders.update.addBlock( deindent` generator.current.builders.update.addBlock( deindent`
var ${name}_value = ${snippet}; var ${listName} = ${snippet};
for ( var ${i} = 0; ${i} < ${name}_value.length; ${i} += 1 ) { for ( var ${i} = 0; ${i} < ${listName}.length; ${i} += 1 ) {
if ( !${iterations}[${i}] ) { if ( !${localVars.iterations}[${i}] ) {
${iterations}[${i}] = ${renderer}( ${params}, ${listName}, ${listName}[${i}], ${i}, component ); ${localVars.iterations}[${i}] = ${renderer}( ${params}, ${listName}, ${listName}[${i}], ${i}, ${generator.current.component} );
${iterations}[${i}].mount( ${anchor}.parentNode, ${anchor} ); ${localVars.iterations}[${i}].mount( ${anchor}.parentNode, ${anchor} );
} else { } else {
${iterations}[${i}].update( changed, ${params}, ${listName}, ${listName}[${i}], ${i} ); ${localVars.iterations}[${i}].update( changed, ${params}, ${listName}, ${listName}[${i}], ${i} );
} }
} }
teardownEach( ${iterations}, true, ${name}_value.length ); teardownEach( ${localVars.iterations}, true, ${listName}.length );
${iterations}.length = ${listName}.length; ${localVars.iterations}.length = ${listName}.length;
` ); ` );
} }
if ( node.else ) { if ( node.else ) {
generator.current.builders.update.addBlock( deindent` generator.current.builders.update.addBlock( deindent`
if ( !${name}_value.length && ${elseName} ) { if ( !${listName}.length && ${elseName} ) {
${elseName}.update( changed, ${params} ); ${elseName}.update( changed, ${params} );
} else if ( !${name}_value.length ) { } else if ( !${listName}.length ) {
${elseName} = ${renderElse}( ${params}, component ); ${elseName} = ${renderElse}( ${params}, ${generator.current.component} );
${elseName}.mount( ${anchor}.parentNode, ${anchor} ); ${elseName}.mount( ${anchor}.parentNode, ${anchor} );
} else if ( ${elseName} ) { } else if ( ${elseName} ) {
${elseName}.teardown( true ); ${elseName}.teardown( true );
@ -154,7 +157,7 @@ export default {
} }
generator.current.builders.teardown.addBlock( generator.current.builders.teardown.addBlock(
`${generator.helper( 'teardownEach' )}( ${iterations}, ${isToplevel ? 'detach' : 'false'} );` ); `${generator.helper( 'teardownEach' )}( ${localVars.iterations}, ${isToplevel ? 'detach' : 'false'} );` );
if ( node.else ) { if ( node.else ) {
generator.current.builders.teardown.addBlock( deindent` generator.current.builders.teardown.addBlock( deindent`
@ -169,20 +172,13 @@ export default {
} }
const indexNames = new Map( generator.current.indexNames ); const indexNames = new Map( generator.current.indexNames );
const indexName = node.index || `${node.context}__index`; const indexName = node.index || generator.current.getUniqueName( `${node.context}__index` );
indexNames.set( node.context, indexName ); indexNames.set( node.context, indexName );
const listNames = new Map( generator.current.listNames ); const listNames = new Map( generator.current.listNames );
listNames.set( node.context, listName ); listNames.set( node.context, listName );
// ensure that contexts like `root` or `component` don't blow up the whole show const context = generator.getUniqueName( node.context );
let context = node.context;
let c = 1;
while ( reserved.has( context ) || ~generator.current.params.indexOf( context ) ) {
context = `${node.context}$${c++}`;
}
const contexts = new Map( generator.current.contexts ); const contexts = new Map( generator.current.contexts );
contexts.set( node.context, context ); contexts.set( node.context, context );
@ -194,6 +190,8 @@ export default {
const blockParams = generator.current.params.concat( listName, context, indexName ); const blockParams = generator.current.params.concat( listName, context, indexName );
const getUniqueName = generator.getUniqueNameMaker( blockParams );
generator.push({ generator.push({
name: renderer, name: renderer,
target: 'target', target: 'target',
@ -202,6 +200,8 @@ export default {
key: node.key, key: node.key,
localElementDepth: 0, localElementDepth: 0,
component: getUniqueName( 'component' ),
contextDependencies, contextDependencies,
contexts, contexts,
indexes, indexes,
@ -211,7 +211,7 @@ export default {
params: blockParams, params: blockParams,
builders: getBuilders(), builders: getBuilders(),
getUniqueName: generator.getUniqueNameMaker() getUniqueName,
}); });
}, },

@ -2,7 +2,7 @@ import deindent from '../../../utils/deindent.js';
function getConditionsAndBlocks ( generator, node, _name, i = 0 ) { function getConditionsAndBlocks ( generator, node, _name, i = 0 ) {
generator.addSourcemapLocations( node.expression ); generator.addSourcemapLocations( node.expression );
const name = `${_name}_${i}`; const name = generator.getUniqueName( `${_name}_${i}` );
const conditionsAndBlocks = [{ const conditionsAndBlocks = [{
condition: generator.contextualise( node.expression ).snippet, condition: generator.contextualise( node.expression ).snippet,
@ -17,7 +17,7 @@ function getConditionsAndBlocks ( generator, node, _name, i = 0 ) {
...getConditionsAndBlocks( generator, node.else.children[0], _name, i + 1 ) ...getConditionsAndBlocks( generator, node.else.children[0], _name, i + 1 )
); );
} else { } else {
const name = `${_name}_${i + 1}`; const name = generator.getUniqueName( `${_name}_${i + 1}` );
conditionsAndBlocks.push({ conditionsAndBlocks.push({
condition: null, condition: null,
block: node.else ? name : null, block: node.else ? name : null,
@ -34,8 +34,9 @@ export default {
enter ( generator, node ) { enter ( generator, node ) {
const params = generator.current.params.join( ', ' ); const params = generator.current.params.join( ', ' );
const name = generator.getUniqueName( `ifBlock` ); const name = generator.getUniqueName( `ifBlock` );
const getBlock = generator.getUniqueName( `getBlock` ); const getBlock = generator.current.getUniqueName( `getBlock` );
const currentBlock = generator.getUniqueName( `currentBlock` ); const currentBlock = generator.current.getUniqueName( `currentBlock` );
const _currentBlock = generator.current.getUniqueName( `_currentBlock` );
const isToplevel = generator.current.localElementDepth === 0; const isToplevel = generator.current.localElementDepth === 0;
const conditionsAndBlocks = getConditionsAndBlocks( generator, node, generator.getUniqueName( `renderIfBlock` ) ); const conditionsAndBlocks = getConditionsAndBlocks( generator, node, generator.getUniqueName( `renderIfBlock` ) );
@ -51,7 +52,7 @@ export default {
} }
var ${currentBlock} = ${getBlock}( ${params} ); var ${currentBlock} = ${getBlock}( ${params} );
var ${name} = ${currentBlock} && ${currentBlock}( ${params}, component ); var ${name} = ${currentBlock} && ${currentBlock}( ${params}, ${generator.current.component} );
` ); ` );
const mountStatement = `if ( ${name} ) ${name}.mount( ${anchor}.parentNode, ${anchor} );`; const mountStatement = `if ( ${name} ) ${name}.mount( ${anchor}.parentNode, ${anchor} );`;
@ -62,13 +63,13 @@ export default {
} }
generator.current.builders.update.addBlock( deindent` generator.current.builders.update.addBlock( deindent`
var _${currentBlock} = ${currentBlock}; var ${_currentBlock} = ${currentBlock};
${currentBlock} = ${getBlock}( ${params} ); ${currentBlock} = ${getBlock}( ${params} );
if ( _${currentBlock} === ${currentBlock} && ${name}) { if ( ${_currentBlock} === ${currentBlock} && ${name}) {
${name}.update( changed, ${params} ); ${name}.update( changed, ${params} );
} else { } else {
if ( ${name} ) ${name}.teardown( true ); if ( ${name} ) ${name}.teardown( true );
${name} = ${currentBlock} && ${currentBlock}( ${params}, component ); ${name} = ${currentBlock} && ${currentBlock}( ${params}, ${generator.current.component} );
if ( ${name} ) ${name}.mount( ${anchor}.parentNode, ${anchor} ); if ( ${name} ) ${name}.mount( ${anchor}.parentNode, ${anchor} );
} }
` ); ` );

@ -6,7 +6,7 @@ export default {
const { snippet } = generator.contextualise( node.expression ); const { snippet } = generator.contextualise( node.expression );
generator.current.builders.init.addLine( `var last_${name} = ${snippet}` ); generator.current.builders.init.addLine( `var last_${name} = ${snippet};` );
generator.addElement( name, `${generator.helper( 'createText' )}( last_${name} )`, true ); generator.addElement( name, `${generator.helper( 'createText' )}( last_${name} )`, true );
generator.current.builders.update.addBlock( deindent` generator.current.builders.update.addBlock( deindent`

@ -4,11 +4,11 @@ export default {
generator.createAnchor( anchor ); generator.createAnchor( anchor );
generator.current.builders.mount.addLine( generator.current.builders.mount.addLine(
`component._yield && component._yield.mount( ${generator.current.target}, ${anchor} );` `${generator.current.component}._yield && ${generator.current.component}._yield.mount( ${generator.current.target}, ${anchor} );`
); );
generator.current.builders.teardown.addLine( generator.current.builders.teardown.addLine(
`component._yield && component._yield.teardown( detach );` `${generator.current.component}._yield && ${generator.current.component}._yield.teardown( detach );`
); );
} }
}; };

@ -78,7 +78,7 @@ export default function addComponentAttributes ( generator, node, local ) {
else if ( attribute.type === 'EventHandler' ) { else if ( attribute.type === 'EventHandler' ) {
// TODO verify that it's a valid callee (i.e. built-in or declared method) // TODO verify that it's a valid callee (i.e. built-in or declared method)
generator.addSourcemapLocations( attribute.expression ); generator.addSourcemapLocations( attribute.expression );
generator.code.prependRight( attribute.expression.start, 'component.' ); generator.code.prependRight( attribute.expression.start, `${generator.current.component}.` );
const usedContexts = []; const usedContexts = [];
attribute.expression.arguments.forEach( arg => { attribute.expression.arguments.forEach( arg => {
@ -117,11 +117,11 @@ export default function addComponentAttributes ( generator, node, local ) {
generator.usesRefs = true; generator.usesRefs = true;
local.init.addLine( local.init.addLine(
`component.refs.${attribute.name} = ${local.name};` `${generator.current.component}.refs.${attribute.name} = ${local.name};`
); );
generator.current.builders.teardown.addLine( deindent` generator.current.builders.teardown.addLine( deindent`
if ( component.refs.${attribute.name} === ${local.name} ) component.refs.${attribute.name} = null; if ( ${generator.current.component}.refs.${attribute.name} === ${local.name} ) ${generator.current.component}.refs.${attribute.name} = null;
` ); ` );
} }

@ -39,25 +39,27 @@ export default function createBinding ( generator, node, attribute, current, loc
generator.hasComplexBindings = true; generator.hasComplexBindings = true;
const updating = generator.current.getUniqueName( `${local.name}_updating` );
local.init.addBlock( deindent` local.init.addBlock( deindent`
var ${local.name}_updating = false; var ${updating} = false;
component._bindings.push( function () { ${generator.current.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 ( ${local.name}_updating ) return; if ( ${updating} ) return;
${local.name}_updating = true; ${updating} = true;
${setter} ${setter}
${local.name}_updating = false; ${updating} = false;
}); });
}); });
` ); ` );
local.update.addBlock( deindent` local.update.addBlock( deindent`
if ( !${local.name}_updating && ${dependencies.map( dependency => `'${dependency}' in changed` ).join( '||' )} ) { if ( !${updating} && ${dependencies.map( dependency => `'${dependency}' in changed` ).join( '||' )} ) {
${local.name}_updating = true; ${updating} = true;
${local.name}._set({ ${attribute.name}: ${snippet} }); ${local.name}._set({ ${attribute.name}: ${snippet} });
${local.name}_updating = false; ${updating} = false;
} }
` ); ` );
} }

@ -154,7 +154,7 @@ export default function addElementAttributes ( generator, node, local ) {
const flattened = flattenReference( attribute.expression.callee ); const flattened = flattenReference( attribute.expression.callee );
if ( flattened.name !== 'event' && flattened.name !== 'this' ) { if ( flattened.name !== 'event' && flattened.name !== 'this' ) {
// allow event.stopPropagation(), this.select() etc // allow event.stopPropagation(), this.select() etc
generator.code.prependRight( attribute.expression.start, 'component.' ); generator.code.prependRight( attribute.expression.start, `${generator.current.component}.` );
} }
const usedContexts = []; const usedContexts = [];
@ -182,7 +182,7 @@ export default function addElementAttributes ( generator, node, local ) {
if ( generator.events.has( name ) ) { if ( generator.events.has( name ) ) {
local.init.addBlock( deindent` local.init.addBlock( deindent`
var ${handlerName} = ${generator.alias( 'template' )}.events.${name}.call( component, ${local.name}, function ( event ) { var ${handlerName} = ${generator.alias( 'template' )}.events.${name}.call( ${generator.current.component}, ${local.name}, function ( event ) {
${handlerBody} ${handlerBody}
}); });
` ); ` );
@ -213,11 +213,11 @@ export default function addElementAttributes ( generator, node, local ) {
generator.usesRefs = true; generator.usesRefs = true;
local.init.addLine( local.init.addLine(
`component.refs.${name} = ${local.name};` `${generator.current.component}.refs.${name} = ${local.name};`
); );
generator.current.builders.teardown.addLine( deindent` generator.current.builders.teardown.addLine( deindent`
if ( component.refs.${name} === ${local.name} ) component.refs.${name} = null; if ( ${generator.current.component}.refs.${name} === ${local.name} ) ${generator.current.component}.refs.${name} = null;
` ); ` );
} }

@ -30,9 +30,9 @@ export default function createBinding ( generator, node, attribute, current, loc
setter = `var selectedOption = ${local.name}.selectedOptions[0] || ${local.name}.options[0];\n${setter}`; setter = `var selectedOption = ${local.name}.selectedOptions[0] || ${local.name}.options[0];\n${setter}`;
} }
const value = generator.current.getUniqueName( 'value' ); const value = current.getUniqueName( 'value' );
const i = generator.current.getUniqueName( 'i' ); const i = current.getUniqueName( 'i' );
const option = generator.current.getUniqueName( 'option' ); const option = current.getUniqueName( 'option' );
const ifStatement = isMultipleSelect ? const ifStatement = isMultipleSelect ?
deindent` deindent`
@ -67,11 +67,11 @@ export default function createBinding ( generator, node, attribute, current, loc
`${local.name}.__value === ${snippet}`; `${local.name}.__value === ${snippet}`;
local.init.addLine( local.init.addLine(
`component._bindingGroups[${bindingGroup}].push( ${local.name} );` `${current.component}._bindingGroups[${bindingGroup}].push( ${local.name} );`
); );
local.teardown.addBlock( local.teardown.addBlock(
`component._bindingGroups[${bindingGroup}].splice( component._bindingGroups[${bindingGroup}].indexOf( ${local.name} ), 1 );` `${current.component}._bindingGroups[${bindingGroup}].splice( ${current.component}._bindingGroups[${bindingGroup}].indexOf( ${local.name} ), 1 );`
); );
updateElement = `${local.name}.checked = ${condition};`; updateElement = `${local.name}.checked = ${condition};`;
@ -82,13 +82,15 @@ export default function createBinding ( generator, node, attribute, current, loc
updateElement = `${local.name}.${attribute.name} = ${snippet};`; updateElement = `${local.name}.${attribute.name} = ${snippet};`;
} }
const updating = generator.current.getUniqueName( `${local.name}_updating` );
local.init.addBlock( deindent` local.init.addBlock( deindent`
var ${local.name}_updating = false; var ${updating} = false;
function ${handler} () { function ${handler} () {
${local.name}_updating = true; ${updating} = true;
${setter} ${setter}
${local.name}_updating = false; ${updating} = false;
} }
${generator.helper( 'addEventListener' )}( ${local.name}, '${eventName}', ${handler} ); ${generator.helper( 'addEventListener' )}( ${local.name}, '${eventName}', ${handler} );
@ -97,12 +99,12 @@ export default function createBinding ( generator, node, attribute, current, loc
node.initialUpdate = updateElement; node.initialUpdate = updateElement;
local.update.addLine( deindent` local.update.addLine( deindent`
if ( !${local.name}_updating ) { if ( !${updating} ) {
${updateElement} ${updateElement}
} }
` ); ` );
generator.current.builders.teardown.addLine( deindent` current.builders.teardown.addLine( deindent`
${generator.helper( 'removeEventListener' )}( ${local.name}, '${eventName}', ${handler} ); ${generator.helper( 'removeEventListener' )}( ${local.name}, '${eventName}', ${handler} );
` ); ` );
} }
@ -136,7 +138,7 @@ function getBindingValue ( generator, local, node, attribute, isMultipleSelect,
// <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( 'getBindingGroupValue' )}( component._bindingGroups[${bindingGroup}] )`; return `${generator.helper( 'getBindingGroupValue' )}( ${generator.current.component}._bindingGroups[${bindingGroup}] )`;
} }
return `${local.name}.__value`; return `${local.name}.__value`;

@ -10,19 +10,19 @@ export default function getSetter ({ current, name, context, attribute, dependen
var index = this.${context}.${current.indexNames.get( name )}; var index = this.${context}.${current.indexNames.get( name )};
list[index]${tail} = ${value}; list[index]${tail} = ${value};
component._set({ ${prop}: component.get( '${prop}' ) }); ${current.component}._set({ ${prop}: ${current.component}.get( '${prop}' ) });
`; `;
} }
if ( attribute.value.type === 'MemberExpression' ) { if ( attribute.value.type === 'MemberExpression' ) {
return deindent` return deindent`
var ${name} = component.get( '${name}' ); var ${name} = ${current.component}.get( '${name}' );
${snippet} = ${value}; ${snippet} = ${value};
component._set({ ${name}: ${name} }); ${current.component}._set({ ${name}: ${name} });
`; `;
} }
return `component._set({ ${name}: ${value} });`; return `${current.component}._set({ ${name}: ${value} });`;
} }
function getTailSnippet ( node ) { function getTailSnippet ( node ) {

@ -6,8 +6,8 @@ import visitors from './visitors/index.js';
import Generator from '../Generator.js'; import Generator from '../Generator.js';
class SsrGenerator extends Generator { class SsrGenerator extends Generator {
constructor ( parsed, source, name, names, visitors, options ) { constructor ( parsed, source, name, visitors, options ) {
super( parsed, source, name, names, visitors, options ); super( parsed, source, name, visitors, options );
this.bindings = []; this.bindings = [];
this.renderCode = ''; this.renderCode = '';
} }
@ -35,11 +35,11 @@ class SsrGenerator extends Generator {
} }
} }
export default function ssr ( parsed, source, options, names ) { export default function ssr ( parsed, source, options ) {
const format = options.format || 'cjs'; const format = options.format || 'cjs';
const name = options.name || 'SvelteComponent'; const name = options.name || 'SvelteComponent';
const generator = new SsrGenerator( parsed, source, name, names, visitors, options ); const generator = new SsrGenerator( parsed, source, name, visitors, options );
const { computations, templateProperties } = generator.parseJs(); const { computations, templateProperties } = generator.parseJs();

@ -1,17 +0,0 @@
export default function counter ( used ) {
const counts = new Map();
used.forEach( name => counts.set( name, 1 ) );
return function ( name ) {
if ( counts.has( name ) ) {
const count = counts.get( name );
const newName = `${name}${count}`;
counts.set( name, count + 1 );
return newName;
}
counts.set( name, 1 );
return name;
};
}

@ -37,13 +37,13 @@ export function compile ( source, _options ) {
return; return;
} }
const { names } = validate( parsed, source, options ); validate( parsed, source, options );
const compiler = options.generate === 'ssr' const compiler = options.generate === 'ssr'
? generateSSR ? generateSSR
: generate; : generate;
return compiler( parsed, source, options, names ); return compiler( parsed, source, options );
} }
export function create ( source, _options = {} ) { export function create ( source, _options = {} ) {

@ -1,26 +1 @@
export default { export default new Set( [ 'Array', 'Boolean', 'console', 'Date', 'decodeURI', 'decodeURIComponent', 'encodeURI', 'encodeURIComponent', 'Infinity', 'Intl', 'isFinite', 'isNaN', 'JSON', 'Map', 'Math', 'NaN', 'Number', 'Object', 'parseFloat', 'parseInt', 'RegExp', 'Set', 'String', 'undefined' ] );
Array: true,
Boolean: true,
console: true,
Date: true,
decodeURI: true,
decodeURIComponent: true,
encodeURI: true,
encodeURIComponent: true,
Infinity: true,
Intl: true,
isFinite: true,
isNaN: true,
JSON: true,
Map: true,
Math: true,
NaN: true,
Number: true,
Object: true,
parseFloat: true,
parseInt: true,
RegExp: true,
Set: true,
String: true,
undefined: true,
};

@ -0,0 +1 @@
export default new Set( [ 'arguments', 'await', 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'default', 'delete', 'do', 'else', 'enum', 'eval', 'export', 'extends', 'false', 'finally', 'for', 'function', 'if', 'implements', 'import', 'in', 'instanceof', 'interface', 'let', 'new', 'null', 'package', 'private', 'protected', 'public', 'return', 'static', 'super', 'switch', 'this', 'throw', 'true', 'try', 'typeof', 'var', 'void', 'while', 'with', 'yield' ] );

@ -6,11 +6,6 @@ export default function validateHtml ( validator, html ) {
let elementDepth = 0; let elementDepth = 0;
function visit ( node ) { function visit ( node ) {
if ( node.type === 'EachBlock' ) {
if ( !~validator.names.indexOf( node.context ) ) validator.names.push( node.context );
if ( node.index && !~validator.names.indexOf( node.index ) ) validator.names.push( node.index );
}
if ( node.type === 'Element' ) { if ( node.type === 'Element' ) {
if ( elementDepth === 0 && validator.namespace !== namespaces.svg && svg.test( node.name ) ) { if ( elementDepth === 0 && validator.namespace !== namespaces.svg && svg.test( node.name ) ) {
validator.warn( `<${node.name}> is an SVG element did you forget to add { namespace: 'svg' } ?`, node.start ); validator.warn( `<${node.name}> is an SVG element did you forget to add { namespace: 'svg' } ?`, node.start );

@ -36,8 +36,6 @@ export default function validate ( parsed, source, { onerror, onwarn, name, file
}); });
}, },
names: [],
namespace: null namespace: null
}; };
@ -53,8 +51,4 @@ export default function validate ( parsed, source, { onerror, onwarn, name, file
if ( parsed.html ) { if ( parsed.html ) {
validateHtml( validator, parsed.html ); validateHtml( validator, parsed.html );
} }
return {
names: validator.names
};
} }

@ -22,7 +22,7 @@ describe( 'validate', () => {
const errors = []; const errors = [];
const warnings = []; const warnings = [];
const { names } = svelte.validate( parsed, input, { svelte.validate( parsed, input, {
onerror ( error ) { onerror ( error ) {
errors.push({ errors.push({
message: error.message, message: error.message,
@ -42,13 +42,9 @@ describe( 'validate', () => {
const expectedErrors = tryToLoadJson( `test/validator/samples/${dir}/errors.json` ) || []; const expectedErrors = tryToLoadJson( `test/validator/samples/${dir}/errors.json` ) || [];
const expectedWarnings = tryToLoadJson( `test/validator/samples/${dir}/warnings.json` ) || []; const expectedWarnings = tryToLoadJson( `test/validator/samples/${dir}/warnings.json` ) || [];
const expectedNames = tryToLoadJson( `test/validator/samples/${dir}/names.json` );
assert.deepEqual( errors, expectedErrors ); assert.deepEqual( errors, expectedErrors );
assert.deepEqual( warnings, expectedWarnings ); assert.deepEqual( warnings, expectedWarnings );
if ( expectedNames ) {
assert.deepEqual( names, expectedNames );
}
} catch ( err ) { } catch ( err ) {
if ( err.name !== 'ParseError' ) throw err; if ( err.name !== 'ParseError' ) throw err;

@ -1,27 +0,0 @@
{{#each things as thing, index}}
<p>{{index}}: {{thing}}</p>
{{else}}
{{#each things as thingEachElse, indexEachElse}}
<p>{{indexEachElse}}: {{thingEachElse}}</p>
{{/each}}
{{/each}}
{{#if foo}}
{{elseif bar}}
{{#each things as thingIfElse, indexIfElse}}
<p>{{indexIfElse}}: {{thingIfElse}}</p>
{{/each}}
{{else}}
{{#each things as thingElse, indexElse}}
<p>{{indexElse}}: {{thingElse}}</p>
{{/each}}
{{/if}}

@ -1,10 +0,0 @@
[
"thing",
"index",
"thingEachElse",
"indexEachElse",
"thingIfElse",
"indexIfElse",
"thingElse",
"indexElse"
]
Loading…
Cancel
Save