diff --git a/src/generators/Generator.js b/src/generators/Generator.js index 8e3898a04b..f43ca29c3b 100644 --- a/src/generators/Generator.js +++ b/src/generators/Generator.js @@ -1,19 +1,18 @@ import MagicString, { Bundle } from 'magic-string'; import { walk } from 'estree-walker'; import isReference from '../utils/isReference.js'; -import counter from './shared/utils/counter.js'; import flattenReference from '../utils/flattenReference.js'; import globalWhitelist from '../utils/globalWhitelist.js'; +import reservedNames from '../utils/reservedNames.js'; import getIntro from './shared/utils/getIntro.js'; import getOutro from './shared/utils/getOutro.js'; import annotateWithScopes from './annotateWithScopes.js'; export default class Generator { - constructor ( parsed, source, name, names, visitors, options ) { + constructor ( parsed, source, name, visitors, options ) { this.parsed = parsed; this.source = source; this.name = name; - this.names = names; this.visitors = visitors; this.options = options; @@ -31,15 +30,14 @@ export default class Generator { this.elementDepth = 0; this.code = new MagicString( source ); - this.getUniqueName = counter( names ); this.cssId = parsed.css ? `svelte-${parsed.hash}` : ''; this.usesRefs = false; // allow compiler to deconflict user's `import { get } from 'whatever'` and // Svelte's builtin `import { get, ... } from 'svelte/shared.js'`; this.importedNames = new Set(); - - this.aliases = new Map(); + this._aliases = new Map(); + this._usedNames = new Set(); this._callbacks = new Map(); } @@ -54,17 +52,12 @@ export default class Generator { } alias ( name ) { - if ( !( this.aliases.has( name ) ) ) { - let alias = name; - let i = 1; - while ( alias in this.importedNames ) { - alias = `${name}$${i++}`; - } - - this.aliases.set( name, alias ); + if ( this._aliases.has( name ) ) { + return this._aliases.get( name ); } - - return this.aliases.get( name ); + const alias = this.getUniqueName( name ); + this._aliases.set( name, alias ); + return alias; } 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.appendLeft( node.object ? node.object.end : node.end, ` : ${name} )` ); } else { @@ -244,8 +237,21 @@ export default class Generator { }; } - getUniqueNameMaker () { - return counter( this.names ); + getUniqueName ( name ) { + 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 () { @@ -272,7 +278,7 @@ export default class Generator { imports.push( node ); this.code.remove( a, b ); node.specifiers.forEach( specifier => { - this.importedNames[ specifier.local.name ] = true; + this.importedNames.add( specifier.local.name ); }); } } diff --git a/src/generators/dom/index.js b/src/generators/dom/index.js index 38c5f81ec9..a51cb6feaf 100644 --- a/src/generators/dom/index.js +++ b/src/generators/dom/index.js @@ -9,8 +9,8 @@ import Generator from '../Generator.js'; import * as shared from '../../shared/index.js'; class DomGenerator extends Generator { - constructor ( parsed, source, name, names, visitors, options ) { - super( parsed, source, name, names, visitors, options ); + constructor ( parsed, source, name, visitors, options ) { + super( parsed, source, name, visitors, options ); this.renderers = []; this.uses = new Set(); @@ -58,7 +58,11 @@ class DomGenerator extends Generator { 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() ) { properties.addBlock( `mount: ${this.helper( 'noop' )},` ); @@ -93,7 +97,7 @@ class DomGenerator extends Generator { } 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} return { @@ -122,7 +126,7 @@ class DomGenerator extends Generator { target: 'target', localElementDepth: 0, builders: getBuilders(), - getUniqueName: this.getUniqueNameMaker() + getUniqueName: this.getUniqueNameMaker( this.current.params ) }); // 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 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(); @@ -196,6 +200,9 @@ export default function dom ( parsed, source, options, names ) { } } + const getUniqueName = generator.getUniqueNameMaker( [ 'root' ] ); + const component = getUniqueName( 'component' ); + generator.push({ name: generator.alias( 'renderMainFragment' ), namespace, @@ -203,6 +210,8 @@ export default function dom ( parsed, source, options, names ) { localElementDepth: 0, key: null, + component, + contexts: new Map(), indexes: new Map(), @@ -211,7 +220,7 @@ export default function dom ( parsed, source, options, names ) { listNames: new Map(), builders: getBuilders(), - getUniqueName: generator.getUniqueNameMaker() + getUniqueName, }); 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 => { - 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( - `import { ${names.join( ', ' )} } from ${JSON.stringify( sharedPath )}` + `import { ${names.join( ', ' )} } from ${JSON.stringify( sharedPath )};` ); } else { generator.uses.forEach( key => { 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 ) ) ); }); } diff --git a/src/generators/dom/visitors/Component.js b/src/generators/dom/visitors/Component.js index 01b0621e03..b584dbc145 100644 --- a/src/generators/dom/visitors/Component.js +++ b/src/generators/dom/visitors/Component.js @@ -9,11 +9,12 @@ function capDown ( name ) { export default { enter ( generator, node ) { 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 = { name, - namespace: generator.current.namespace, + namespace: current.namespace, isComponent: true, allUsedContexts: [], @@ -22,7 +23,7 @@ export default { update: new CodeBuilder() }; - const isToplevel = generator.current.localElementDepth === 0; + const isToplevel = current.localElementDepth === 0; generator.hasComponents = true; @@ -32,8 +33,8 @@ export default { const initialProps = local.allUsedContexts.map( contextName => { if ( contextName === 'root' ) return `root: root`; - const listName = generator.current.listNames.get( contextName ); - const indexName = generator.current.indexNames.get( contextName ); + const listName = current.listNames.get( contextName ); + const indexName = current.indexNames.get( contextName ); return `${listName}: ${listName},\n${indexName}: ${indexName}`; }).join( ',\n' ); @@ -41,8 +42,8 @@ export default { const updates = local.allUsedContexts.map( contextName => { if ( contextName === 'root' ) return `${name}._context.root = root;`; - const listName = generator.current.listNames.get( contextName ); - const indexName = generator.current.indexNames.get( contextName ); + const listName = current.listNames.get( contextName ); + const indexName = current.indexNames.get( contextName ); return `${name}._context.${listName} = ${listName};\n${name}._context.${indexName} = ${indexName};`; }).join( '\n' ); @@ -57,26 +58,28 @@ export default { } const componentInitProperties = [ - `target: ${!isToplevel ? generator.current.target: 'null'}`, - '_root: component._root || component' + `target: ${!isToplevel ? current.target: 'null'}`, + `_root: ${current.component}._root || ${current.component}` ]; // Component has children, put them in a separate {{yield}} block if ( hasChildren ) { const yieldName = generator.getUniqueName( `render${name}YieldFragment` ); - const params = generator.current.params.join( ', ' ); + const params = current.params.join( ', ' ); generator.generateBlock( node, yieldName ); - generator.current.builders.init.addLine( - `var ${name}_yieldFragment = ${yieldName}( ${params}, component );` + const yieldFragment = current.getUniqueName( `${name}_yieldFragment` ); + + current.builders.init.addLine( + `var ${yieldFragment} = ${yieldName}( ${params}, ${current.component} );` ); - generator.current.builders.update.addLine( - `${name}_yieldFragment.update( changed, ${params} );` + current.builders.update.addLine( + `${yieldFragment}.update( changed, ${params} );` ); - componentInitProperties.push( `_yield: ${name}_yieldFragment`); + componentInitProperties.push( `_yield: ${yieldFragment}`); } const statements = []; @@ -85,6 +88,7 @@ export default { const initialProps = local.staticAttributes .concat( local.dynamicAttributes ) .map( attribute => `${attribute.name}: ${attribute.value}` ); + const initialData = current.getUniqueName( `${name}_initialData` ); if ( initialProps.length ) { statements.push( deindent` @@ -93,17 +97,17 @@ export default { }; ` ); } else { - statements.push( `var ${name}_initialData = {};` ); + statements.push( `var ${initialData} = {};` ); } if ( local.bindings.length ) { 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' ) ); } - 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}`; @@ -116,7 +120,7 @@ export default { ` ); 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 ) { @@ -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 ); - if ( !local.update.isEmpty() ) generator.current.builders.update.addBlock( local.update ); + current.builders.init.addBlock( local.init ); + if ( !local.update.isEmpty() ) current.builders.update.addBlock( local.update ); generator.push({ namespace: local.namespace, target: name, - parent: generator.current, - localElementDepth: generator.current.localElementDepth + 1, + parent: current, + localElementDepth: current.localElementDepth + 1, key: null }); }, diff --git a/src/generators/dom/visitors/EachBlock.js b/src/generators/dom/visitors/EachBlock.js index a6819059e3..2dc6f222c8 100644 --- a/src/generators/dom/visitors/EachBlock.js +++ b/src/generators/dom/visitors/EachBlock.js @@ -2,19 +2,16 @@ import CodeBuilder from '../../../utils/CodeBuilder.js'; import deindent from '../../../utils/deindent.js'; import getBuilders from '../utils/getBuilders.js'; -const reserved = new Set( [ 'component', 'root' ] ); - export default { enter ( generator, node ) { const name = generator.getUniqueName( `eachBlock` ); const renderer = generator.getUniqueName( `renderEachBlock` ); - const elseName = `${name}_else`; - const iterations = `${name}_iterations`; - const renderElse = `${renderer}_else`; + const elseName = generator.getUniqueName( `${name}_else` ); + const renderElse = generator.getUniqueName( `${renderer}_else` ); 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; @@ -22,49 +19,55 @@ export default { const { dependencies, snippet } = generator.contextualise( node.expression ); - const anchor = `${name}_anchor`; + const anchor = generator.current.getUniqueName( `${name}_anchor` ); generator.createAnchor( anchor ); - generator.current.builders.init.addLine( `var ${name}_value = ${snippet};` ); - generator.current.builders.init.addLine( `var ${iterations} = [];` ); - if ( node.key ) generator.current.builders.init.addLine( `var ${name}_lookup = Object.create( null );` ); + const localVars = {}; + + 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;` ); const initialRender = new CodeBuilder(); - const localVars = {}; - if ( node.key ) { localVars.fragment = generator.current.getUniqueName( 'fragment' ); localVars.value = generator.current.getUniqueName( 'value' ); localVars.key = generator.current.getUniqueName( 'key' ); initialRender.addBlock( deindent` - var ${localVars.key} = ${name}_value[${i}].${node.key}; - ${name}_iterations[${i}] = ${name}_lookup[ ${localVars.key} ] = ${renderer}( ${params}, ${listName}, ${listName}[${i}], ${i}, component${node.key ? `, ${localVars.key}` : `` } ); + var ${localVars.key} = ${listName}[${i}].${node.key}; + ${localVars.iterations}[${i}] = ${localVars.lookup}[ ${localVars.key} ] = ${renderer}( ${params}, ${listName}, ${listName}[${i}], ${i}, ${generator.current.component}${node.key ? `, ${localVars.key}` : `` } ); ` ); } else { 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 ) { initialRender.addLine( - `${name}_iterations[${i}].mount( ${anchor}.parentNode, ${anchor} );` + `${localVars.iterations}[${i}].mount( ${anchor}.parentNode, ${anchor} );` ); } 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} } ` ); if ( node.else ) { generator.current.builders.init.addBlock( deindent` - if ( !${name}_value.length ) { - ${elseName} = ${renderElse}( ${params}, component ); + if ( !${listName}.length ) { + ${elseName} = ${renderElse}( ${params}, ${generator.current.component} ); ${!isToplevel ? `${elseName}.mount( ${anchor}.parentNode, ${anchor} );` : ''} } ` ); @@ -72,8 +75,8 @@ export default { if ( isToplevel ) { generator.current.builders.mount.addBlock( deindent` - for ( var ${i} = 0; ${i} < ${iterations}.length; ${i} += 1 ) { - ${iterations}[${i}].mount( ${anchor}.parentNode, ${anchor} ); + for ( var ${i} = 0; ${i} < ${localVars.iterations}.length; ${i} += 1 ) { + ${localVars.iterations}[${i}].mount( ${anchor}.parentNode, ${anchor} ); } ` ); if ( node.else ) { @@ -87,65 +90,65 @@ export default { if ( node.key ) { generator.current.builders.update.addBlock( deindent` - var ${name}_value = ${snippet}; - var _${name}_iterations = []; - var _${name}_lookup = Object.create( null ); + var ${listName} = ${snippet}; + var ${localVars._iterations} = []; + var ${localVars._lookup} = Object.create( null ); var ${localVars.fragment} = document.createDocumentFragment(); // create new iterations as necessary - for ( var ${i} = 0; ${i} < ${name}_value.length; ${i} += 1 ) { - var ${localVars.value} = ${name}_value[${i}]; + for ( var ${i} = 0; ${i} < ${listName}.length; ${i} += 1 ) { + var ${localVars.value} = ${listName}[${i}]; var ${localVars.key} = ${localVars.value}.${node.key}; - if ( ${name}_lookup[ ${localVars.key} ] ) { - _${name}_iterations[${i}] = _${name}_lookup[ ${localVars.key} ] = ${name}_lookup[ ${localVars.key} ]; - _${name}_lookup[ ${localVars.key} ].update( changed, ${params}, ${listName}, ${listName}[${i}], ${i} ); + if ( ${localVars.lookup}[ ${localVars.key} ] ) { + ${localVars._iterations}[${i}] = ${localVars._lookup}[ ${localVars.key} ] = ${localVars.lookup}[ ${localVars.key} ]; + ${localVars._lookup}[ ${localVars.key} ].update( changed, ${params}, ${listName}, ${listName}[${i}], ${i} ); } 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 - for ( var ${i} = 0; ${i} < ${name}_iterations.length; ${i} += 1 ) { - var ${name}_iteration = ${name}_iterations[${i}]; - if ( !_${name}_lookup[ ${name}_iteration.${localVars.key} ] ) { - ${name}_iteration.teardown( true ); + for ( var ${i} = 0; ${i} < ${localVars.iterations}.length; ${i} += 1 ) { + var ${localVars.iteration} = ${localVars.iterations}[${i}]; + if ( !${localVars._lookup}[ ${localVars.iteration}.key ] ) { + ${localVars.iteration}.teardown( true ); } } - ${name}_anchor.parentNode.insertBefore( ${localVars.fragment}, ${name}_anchor ); + ${anchor}.parentNode.insertBefore( ${localVars.fragment}, ${anchor} ); - ${name}_iterations = _${name}_iterations; - ${name}_lookup = _${name}_lookup; + ${localVars.iterations} = ${localVars._iterations}; + ${localVars.lookup} = ${localVars._lookup}; ` ); } else { generator.current.builders.update.addBlock( deindent` - var ${name}_value = ${snippet}; + var ${listName} = ${snippet}; - for ( var ${i} = 0; ${i} < ${name}_value.length; ${i} += 1 ) { - if ( !${iterations}[${i}] ) { - ${iterations}[${i}] = ${renderer}( ${params}, ${listName}, ${listName}[${i}], ${i}, component ); - ${iterations}[${i}].mount( ${anchor}.parentNode, ${anchor} ); + for ( var ${i} = 0; ${i} < ${listName}.length; ${i} += 1 ) { + if ( !${localVars.iterations}[${i}] ) { + ${localVars.iterations}[${i}] = ${renderer}( ${params}, ${listName}, ${listName}[${i}], ${i}, ${generator.current.component} ); + ${localVars.iterations}[${i}].mount( ${anchor}.parentNode, ${anchor} ); } 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 ) { generator.current.builders.update.addBlock( deindent` - if ( !${name}_value.length && ${elseName} ) { + if ( !${listName}.length && ${elseName} ) { ${elseName}.update( changed, ${params} ); - } else if ( !${name}_value.length ) { - ${elseName} = ${renderElse}( ${params}, component ); + } else if ( !${listName}.length ) { + ${elseName} = ${renderElse}( ${params}, ${generator.current.component} ); ${elseName}.mount( ${anchor}.parentNode, ${anchor} ); } else if ( ${elseName} ) { ${elseName}.teardown( true ); @@ -154,7 +157,7 @@ export default { } generator.current.builders.teardown.addBlock( - `${generator.helper( 'teardownEach' )}( ${iterations}, ${isToplevel ? 'detach' : 'false'} );` ); + `${generator.helper( 'teardownEach' )}( ${localVars.iterations}, ${isToplevel ? 'detach' : 'false'} );` ); if ( node.else ) { generator.current.builders.teardown.addBlock( deindent` @@ -169,20 +172,13 @@ export default { } 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 ); const listNames = new Map( generator.current.listNames ); listNames.set( node.context, listName ); - // ensure that contexts like `root` or `component` don't blow up the whole show - let context = node.context; - let c = 1; - - while ( reserved.has( context ) || ~generator.current.params.indexOf( context ) ) { - context = `${node.context}$${c++}`; - } - + const context = generator.getUniqueName( node.context ); const contexts = new Map( generator.current.contexts ); contexts.set( node.context, context ); @@ -194,6 +190,8 @@ export default { const blockParams = generator.current.params.concat( listName, context, indexName ); + const getUniqueName = generator.getUniqueNameMaker( blockParams ); + generator.push({ name: renderer, target: 'target', @@ -202,6 +200,8 @@ export default { key: node.key, localElementDepth: 0, + component: getUniqueName( 'component' ), + contextDependencies, contexts, indexes, @@ -211,7 +211,7 @@ export default { params: blockParams, builders: getBuilders(), - getUniqueName: generator.getUniqueNameMaker() + getUniqueName, }); }, diff --git a/src/generators/dom/visitors/IfBlock.js b/src/generators/dom/visitors/IfBlock.js index 6f89b55f97..81552588e5 100644 --- a/src/generators/dom/visitors/IfBlock.js +++ b/src/generators/dom/visitors/IfBlock.js @@ -2,7 +2,7 @@ import deindent from '../../../utils/deindent.js'; function getConditionsAndBlocks ( generator, node, _name, i = 0 ) { generator.addSourcemapLocations( node.expression ); - const name = `${_name}_${i}`; + const name = generator.getUniqueName( `${_name}_${i}` ); const conditionsAndBlocks = [{ 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 ) ); } else { - const name = `${_name}_${i + 1}`; + const name = generator.getUniqueName( `${_name}_${i + 1}` ); conditionsAndBlocks.push({ condition: null, block: node.else ? name : null, @@ -34,8 +34,9 @@ export default { enter ( generator, node ) { const params = generator.current.params.join( ', ' ); const name = generator.getUniqueName( `ifBlock` ); - const getBlock = generator.getUniqueName( `getBlock` ); - const currentBlock = generator.getUniqueName( `currentBlock` ); + const getBlock = generator.current.getUniqueName( `getBlock` ); + const currentBlock = generator.current.getUniqueName( `currentBlock` ); + const _currentBlock = generator.current.getUniqueName( `_currentBlock` ); const isToplevel = generator.current.localElementDepth === 0; const conditionsAndBlocks = getConditionsAndBlocks( generator, node, generator.getUniqueName( `renderIfBlock` ) ); @@ -51,7 +52,7 @@ export default { } 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} );`; @@ -62,13 +63,13 @@ export default { } generator.current.builders.update.addBlock( deindent` - var _${currentBlock} = ${currentBlock}; + var ${_currentBlock} = ${currentBlock}; ${currentBlock} = ${getBlock}( ${params} ); - if ( _${currentBlock} === ${currentBlock} && ${name}) { + if ( ${_currentBlock} === ${currentBlock} && ${name}) { ${name}.update( changed, ${params} ); } else { if ( ${name} ) ${name}.teardown( true ); - ${name} = ${currentBlock} && ${currentBlock}( ${params}, component ); + ${name} = ${currentBlock} && ${currentBlock}( ${params}, ${generator.current.component} ); if ( ${name} ) ${name}.mount( ${anchor}.parentNode, ${anchor} ); } ` ); diff --git a/src/generators/dom/visitors/MustacheTag.js b/src/generators/dom/visitors/MustacheTag.js index 48bce23747..35ec8ddee3 100644 --- a/src/generators/dom/visitors/MustacheTag.js +++ b/src/generators/dom/visitors/MustacheTag.js @@ -6,7 +6,7 @@ export default { 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.current.builders.update.addBlock( deindent` diff --git a/src/generators/dom/visitors/YieldTag.js b/src/generators/dom/visitors/YieldTag.js index 98996486c1..46dc3152c9 100644 --- a/src/generators/dom/visitors/YieldTag.js +++ b/src/generators/dom/visitors/YieldTag.js @@ -4,11 +4,11 @@ export default { generator.createAnchor( anchor ); 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( - `component._yield && component._yield.teardown( detach );` + `${generator.current.component}._yield && ${generator.current.component}._yield.teardown( detach );` ); } }; diff --git a/src/generators/dom/visitors/attributes/addComponentAttributes.js b/src/generators/dom/visitors/attributes/addComponentAttributes.js index 8e5c5c92a6..d2e0d48443 100644 --- a/src/generators/dom/visitors/attributes/addComponentAttributes.js +++ b/src/generators/dom/visitors/attributes/addComponentAttributes.js @@ -78,7 +78,7 @@ export default function addComponentAttributes ( generator, node, local ) { else if ( attribute.type === 'EventHandler' ) { // TODO verify that it's a valid callee (i.e. built-in or declared method) generator.addSourcemapLocations( attribute.expression ); - generator.code.prependRight( attribute.expression.start, 'component.' ); + generator.code.prependRight( attribute.expression.start, `${generator.current.component}.` ); const usedContexts = []; attribute.expression.arguments.forEach( arg => { @@ -117,11 +117,11 @@ export default function addComponentAttributes ( generator, node, local ) { generator.usesRefs = true; local.init.addLine( - `component.refs.${attribute.name} = ${local.name};` + `${generator.current.component}.refs.${attribute.name} = ${local.name};` ); 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; ` ); } diff --git a/src/generators/dom/visitors/attributes/addComponentBinding.js b/src/generators/dom/visitors/attributes/addComponentBinding.js index 68c862cb6e..df9efbc681 100644 --- a/src/generators/dom/visitors/attributes/addComponentBinding.js +++ b/src/generators/dom/visitors/attributes/addComponentBinding.js @@ -39,25 +39,27 @@ export default function createBinding ( generator, node, attribute, current, loc generator.hasComplexBindings = true; + const updating = generator.current.getUniqueName( `${local.name}_updating` ); + 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; ${local.name}.observe( '${attribute.name}', function ( value ) { - if ( ${local.name}_updating ) return; - ${local.name}_updating = true; + if ( ${updating} ) return; + ${updating} = true; ${setter} - ${local.name}_updating = false; + ${updating} = false; }); }); ` ); local.update.addBlock( deindent` - if ( !${local.name}_updating && ${dependencies.map( dependency => `'${dependency}' in changed` ).join( '||' )} ) { - ${local.name}_updating = true; + if ( !${updating} && ${dependencies.map( dependency => `'${dependency}' in changed` ).join( '||' )} ) { + ${updating} = true; ${local.name}._set({ ${attribute.name}: ${snippet} }); - ${local.name}_updating = false; + ${updating} = false; } ` ); } diff --git a/src/generators/dom/visitors/attributes/addElementAttributes.js b/src/generators/dom/visitors/attributes/addElementAttributes.js index c30c5d0cc2..de0699e4e4 100644 --- a/src/generators/dom/visitors/attributes/addElementAttributes.js +++ b/src/generators/dom/visitors/attributes/addElementAttributes.js @@ -154,7 +154,7 @@ export default function addElementAttributes ( generator, node, local ) { const flattened = flattenReference( attribute.expression.callee ); if ( flattened.name !== 'event' && flattened.name !== 'this' ) { // allow event.stopPropagation(), this.select() etc - generator.code.prependRight( attribute.expression.start, 'component.' ); + generator.code.prependRight( attribute.expression.start, `${generator.current.component}.` ); } const usedContexts = []; @@ -182,7 +182,7 @@ export default function addElementAttributes ( generator, node, local ) { if ( generator.events.has( name ) ) { 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} }); ` ); @@ -213,11 +213,11 @@ export default function addElementAttributes ( generator, node, local ) { generator.usesRefs = true; local.init.addLine( - `component.refs.${name} = ${local.name};` + `${generator.current.component}.refs.${name} = ${local.name};` ); 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; ` ); } diff --git a/src/generators/dom/visitors/attributes/addElementBinding.js b/src/generators/dom/visitors/attributes/addElementBinding.js index 769eeb91ff..ee222dc304 100644 --- a/src/generators/dom/visitors/attributes/addElementBinding.js +++ b/src/generators/dom/visitors/attributes/addElementBinding.js @@ -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}`; } - const value = generator.current.getUniqueName( 'value' ); - const i = generator.current.getUniqueName( 'i' ); - const option = generator.current.getUniqueName( 'option' ); + const value = current.getUniqueName( 'value' ); + const i = current.getUniqueName( 'i' ); + const option = current.getUniqueName( 'option' ); const ifStatement = isMultipleSelect ? deindent` @@ -67,11 +67,11 @@ export default function createBinding ( generator, node, attribute, current, loc `${local.name}.__value === ${snippet}`; local.init.addLine( - `component._bindingGroups[${bindingGroup}].push( ${local.name} );` + `${current.component}._bindingGroups[${bindingGroup}].push( ${local.name} );` ); 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};`; @@ -82,13 +82,15 @@ export default function createBinding ( generator, node, attribute, current, loc updateElement = `${local.name}.${attribute.name} = ${snippet};`; } + const updating = generator.current.getUniqueName( `${local.name}_updating` ); + local.init.addBlock( deindent` - var ${local.name}_updating = false; + var ${updating} = false; function ${handler} () { - ${local.name}_updating = true; + ${updating} = true; ${setter} - ${local.name}_updating = false; + ${updating} = false; } ${generator.helper( 'addEventListener' )}( ${local.name}, '${eventName}', ${handler} ); @@ -97,12 +99,12 @@ export default function createBinding ( generator, node, attribute, current, loc node.initialUpdate = updateElement; local.update.addLine( deindent` - if ( !${local.name}_updating ) { + if ( !${updating} ) { ${updateElement} } ` ); - generator.current.builders.teardown.addLine( deindent` + current.builders.teardown.addLine( deindent` ${generator.helper( 'removeEventListener' )}( ${local.name}, '${eventName}', ${handler} ); ` ); } @@ -136,7 +138,7 @@ function getBindingValue ( generator, local, node, attribute, isMultipleSelect, // if ( attribute.name === 'group' ) { if ( type === 'checkbox' ) { - return `${generator.helper( 'getBindingGroupValue' )}( component._bindingGroups[${bindingGroup}] )`; + return `${generator.helper( 'getBindingGroupValue' )}( ${generator.current.component}._bindingGroups[${bindingGroup}] )`; } return `${local.name}.__value`; diff --git a/src/generators/dom/visitors/attributes/binding/getSetter.js b/src/generators/dom/visitors/attributes/binding/getSetter.js index a81fc9277a..a63c543042 100644 --- a/src/generators/dom/visitors/attributes/binding/getSetter.js +++ b/src/generators/dom/visitors/attributes/binding/getSetter.js @@ -10,19 +10,19 @@ export default function getSetter ({ current, name, context, attribute, dependen var index = this.${context}.${current.indexNames.get( name )}; list[index]${tail} = ${value}; - component._set({ ${prop}: component.get( '${prop}' ) }); + ${current.component}._set({ ${prop}: ${current.component}.get( '${prop}' ) }); `; } if ( attribute.value.type === 'MemberExpression' ) { return deindent` - var ${name} = component.get( '${name}' ); + var ${name} = ${current.component}.get( '${name}' ); ${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 ) { diff --git a/src/generators/server-side-rendering/index.js b/src/generators/server-side-rendering/index.js index a2bb1bebb3..8cce0610c7 100644 --- a/src/generators/server-side-rendering/index.js +++ b/src/generators/server-side-rendering/index.js @@ -6,8 +6,8 @@ import visitors from './visitors/index.js'; import Generator from '../Generator.js'; class SsrGenerator extends Generator { - constructor ( parsed, source, name, names, visitors, options ) { - super( parsed, source, name, names, visitors, options ); + constructor ( parsed, source, name, visitors, options ) { + super( parsed, source, name, visitors, options ); this.bindings = []; 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 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(); diff --git a/src/generators/shared/utils/counter.js b/src/generators/shared/utils/counter.js deleted file mode 100644 index da00544ca5..0000000000 --- a/src/generators/shared/utils/counter.js +++ /dev/null @@ -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; - }; -} diff --git a/src/index.js b/src/index.js index 4e25696241..7de2a9b821 100644 --- a/src/index.js +++ b/src/index.js @@ -37,13 +37,13 @@ export function compile ( source, _options ) { return; } - const { names } = validate( parsed, source, options ); + validate( parsed, source, options ); const compiler = options.generate === 'ssr' ? generateSSR : generate; - return compiler( parsed, source, options, names ); + return compiler( parsed, source, options ); } export function create ( source, _options = {} ) { diff --git a/src/utils/globalWhitelist.js b/src/utils/globalWhitelist.js index 467cc4cc63..8472308174 100644 --- a/src/utils/globalWhitelist.js +++ b/src/utils/globalWhitelist.js @@ -1,26 +1 @@ -export default { - 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, -}; +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' ] ); diff --git a/src/utils/reservedNames.js b/src/utils/reservedNames.js new file mode 100644 index 0000000000..f4522d15b8 --- /dev/null +++ b/src/utils/reservedNames.js @@ -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' ] ); diff --git a/src/validate/html/index.js b/src/validate/html/index.js index 09a9816952..2e831ee9c1 100644 --- a/src/validate/html/index.js +++ b/src/validate/html/index.js @@ -6,11 +6,6 @@ export default function validateHtml ( validator, html ) { let elementDepth = 0; 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 ( 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 ); diff --git a/src/validate/index.js b/src/validate/index.js index d16c83708a..d4bd6e7cb2 100644 --- a/src/validate/index.js +++ b/src/validate/index.js @@ -36,8 +36,6 @@ export default function validate ( parsed, source, { onerror, onwarn, name, file }); }, - names: [], - namespace: null }; @@ -53,8 +51,4 @@ export default function validate ( parsed, source, { onerror, onwarn, name, file if ( parsed.html ) { validateHtml( validator, parsed.html ); } - - return { - names: validator.names - }; } diff --git a/test/validator/index.js b/test/validator/index.js index bfff108937..b3de9046a9 100644 --- a/test/validator/index.js +++ b/test/validator/index.js @@ -22,7 +22,7 @@ describe( 'validate', () => { const errors = []; const warnings = []; - const { names } = svelte.validate( parsed, input, { + svelte.validate( parsed, input, { onerror ( error ) { errors.push({ message: error.message, @@ -42,13 +42,9 @@ describe( 'validate', () => { const expectedErrors = tryToLoadJson( `test/validator/samples/${dir}/errors.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( warnings, expectedWarnings ); - if ( expectedNames ) { - assert.deepEqual( names, expectedNames ); - } } catch ( err ) { if ( err.name !== 'ParseError' ) throw err; diff --git a/test/validator/samples/names/input.html b/test/validator/samples/names/input.html deleted file mode 100644 index ba836ddb90..0000000000 --- a/test/validator/samples/names/input.html +++ /dev/null @@ -1,27 +0,0 @@ -{{#each things as thing, index}} - -

{{index}}: {{thing}}

- -{{else}} - - {{#each things as thingEachElse, indexEachElse}} -

{{indexEachElse}}: {{thingEachElse}}

- {{/each}} - -{{/each}} - -{{#if foo}} - -{{elseif bar}} - - {{#each things as thingIfElse, indexIfElse}} -

{{indexIfElse}}: {{thingIfElse}}

- {{/each}} - -{{else}} - - {{#each things as thingElse, indexElse}} -

{{indexElse}}: {{thingElse}}

- {{/each}} - - {{/if}} diff --git a/test/validator/samples/names/names.json b/test/validator/samples/names/names.json deleted file mode 100644 index e044812999..0000000000 --- a/test/validator/samples/names/names.json +++ /dev/null @@ -1,10 +0,0 @@ -[ - "thing", - "index", - "thingEachElse", - "indexEachElse", - "thingIfElse", - "indexIfElse", - "thingElse", - "indexElse" -]