diff --git a/src/generators/Generator.js b/src/generators/Generator.js index 469b271f85..a14668494a 100644 --- a/src/generators/Generator.js +++ b/src/generators/Generator.js @@ -35,6 +35,12 @@ export default class Generator { 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 = {}; + + this.aliases = {}; + this._callbacks = {}; } @@ -47,6 +53,20 @@ export default class Generator { }); } + alias ( name ) { + if ( !( name in this.aliases ) ) { + let alias = name; + let i = 1; + while ( alias in this.importedNames ) { + alias = `${name}$${i++}`; + } + + this.aliases[ name ] = alias; + } + + return this.aliases[ name ]; + } + contextualise ( expression, isEventHandler ) { this.addSourcemapLocations( expression ); @@ -58,6 +78,8 @@ export default class Generator { let scope = annotateWithScopes( expression ); + const self = this; + walk( expression, { enter ( node, parent, key ) { if ( node._scope ) { @@ -70,7 +92,7 @@ export default class Generator { if ( scope.has( name ) ) return; if ( parent && parent.type === 'CallExpression' && node === parent.callee && helpers[ name ] ) { - code.prependRight( node.start, `template.helpers.` ); + code.prependRight( node.start, `${self.alias( 'template' )}.helpers.` ); } else if ( name === 'event' && isEventHandler ) { @@ -249,6 +271,9 @@ export default class Generator { imports.push( node ); this.code.remove( a, b ); + node.specifiers.forEach( specifier => { + this.importedNames[ specifier.local.name ] = true; + }); } } @@ -260,21 +285,33 @@ export default class Generator { // export is last property, we can just return it this.code.overwrite( defaultExport.start, defaultExport.declaration.start, `return ` ); } else { - // TODO ensure `template` isn't already declared - this.code.overwrite( defaultExport.start, defaultExport.declaration.start, `var template = ` ); + // we need to avoid a conflict with anything on the top-level scope of the component + // however, determining which things these are is tricky, so we instead just avoid conflicts with any identifiers anywhere + const identifiers = new Set(); + walk( js, { + enter ( node ) { + if ( node.type === 'Identifier' ) { + identifiers.add( node.name ); + } + } + }); + let template = 'template'; + for ( let i = 1; identifiers.has( template ); template = `template$${i++}` ); + + this.code.overwrite( defaultExport.start, defaultExport.declaration.start, `var ${template} = ` ); let i = defaultExport.start; while ( /\s/.test( source[ i - 1 ] ) ) i--; const indentation = source.slice( i, defaultExport.start ); - this.code.appendLeft( finalNode.end, `\n\n${indentation}return template;` ); + this.code.appendLeft( finalNode.end, `\n\n${indentation}return ${template};` ); } defaultExport.declaration.properties.forEach( prop => { templateProperties[ prop.key.name ] = prop; }); - this.code.prependRight( js.content.start, 'var template = (function () {' ); + this.code.prependRight( js.content.start, `var ${this.alias( 'template' )} = (function () {` ); } else { this.code.prependRight( js.content.start, '(function () {' ); } diff --git a/src/generators/dom/index.js b/src/generators/dom/index.js index c0541b8959..4d386affd3 100644 --- a/src/generators/dom/index.js +++ b/src/generators/dom/index.js @@ -14,11 +14,6 @@ class DomGenerator extends Generator { this.renderers = []; this.uses = {}; - // allow compiler to deconflict user's `import { get } from 'whatever'` and - // Svelte's builtin `import { get, ... } from 'svelte/shared.js'`; - this.importedNames = {}; - this.aliases = {}; - this.importedComponents = {}; } @@ -143,20 +138,6 @@ class DomGenerator extends Generator { return this.alias( name ); } - - alias ( name ) { - if ( !( name in this.aliases ) ) { - let alias = name; - let i = 1; - while ( alias in this.importedNames ) { - alias = `${name}$${i++}`; - } - - this.aliases[ name ] = alias; - } - - return this.aliases[ name ]; - } } export default function dom ( parsed, source, options, names ) { @@ -180,12 +161,6 @@ export default function dom ( parsed, source, options, names ) { templateProperties.ondestroy = templateProperties.onteardown; } - generator.imports.forEach( node => { - node.specifiers.forEach( specifier => { - generator.importedNames[ specifier.local.name ] = true; - }); - }); - let namespace = null; if ( templateProperties.namespace ) { const ns = templateProperties.namespace.value.value; @@ -261,7 +236,7 @@ export default function dom ( parsed, source, options, names ) { computations.forEach( ({ key, deps }) => { builder.addBlock( deindent` if ( isInitial || ${deps.map( dep => `( '${dep}' in newState && typeof state.${dep} === 'object' || state.${dep} !== oldState.${dep} )` ).join( ' || ' )} ) { - state.${key} = newState.${key} = template.computed.${key}( ${deps.map( dep => `state.${dep}` ).join( ', ' )} ); + state.${key} = newState.${key} = ${generator.alias( 'template' )}.computed.${key}( ${deps.map( dep => `state.${dep}` ).join( ', ' )} ); } ` ); }); @@ -338,9 +313,9 @@ export default function dom ( parsed, source, options, names ) { if ( templateProperties.oncreate ) { builders.init.addBlock( deindent` if ( options._root ) { - options._root._renderHooks.push({ fn: template.oncreate, context: this }); + options._root._renderHooks.push({ fn: ${generator.alias( 'template' )}.oncreate, context: this }); } else { - template.oncreate.call( this ); + ${generator.alias( 'template' )}.oncreate.call( this ); } ` ); } @@ -351,7 +326,7 @@ export default function dom ( parsed, source, options, names ) { if ( generator.usesRefs ) constructorBlock.addLine( `this.refs = {};` ); constructorBlock.addLine( - `this._state = ${templateProperties.data ? `Object.assign( template.data(), options.data )` : `options.data || {}`};` + `this._state = ${templateProperties.data ? `Object.assign( ${generator.alias( 'template' )}.data(), options.data )` : `options.data || {}`};` ); if ( templateProperties.computed ) { @@ -399,11 +374,11 @@ export default function dom ( parsed, source, options, names ) { const sharedPath = options.shared === true ? 'svelte/shared.js' : options.shared; if ( sharedPath ) { - const base = templateProperties.methods ? `{}, template.methods` : `{}`; + const base = templateProperties.methods ? `{}, ${generator.alias( 'template' )}.methods` : `{}`; builders.main.addBlock( `${name}.prototype = Object.assign( ${base}, ${generator.helper( 'proto' )} );` ); } else { if ( templateProperties.methods ) { - builders.main.addBlock( `${name}.prototype = template.methods;` ); + builders.main.addBlock( `${name}.prototype = ${generator.alias( 'template' )}.methods;` ); } [ 'get', 'fire', 'observe', 'on', 'set', '_flush' ].forEach( methodName => { @@ -418,7 +393,7 @@ export default function dom ( parsed, source, options, names ) { }; ${name}.prototype.teardown = ${name}.prototype.destroy = function destroy ( detach ) { - this.fire( 'destroy' );${templateProperties.ondestroy ? `\ntemplate.ondestroy.call( this );` : ``} + this.fire( 'destroy' );${templateProperties.ondestroy ? `\n${generator.alias( 'template' )}.ondestroy.call( this );` : ``} this._fragment.teardown( detach !== false ); this._fragment = null; diff --git a/src/generators/dom/visitors/Component.js b/src/generators/dom/visitors/Component.js index 7165a8a7c2..f6fae1392e 100644 --- a/src/generators/dom/visitors/Component.js +++ b/src/generators/dom/visitors/Component.js @@ -106,7 +106,7 @@ export default { componentInitProperties.push(`data: ${name}_initialData`); } - const expression = node.name === ':Self' ? generator.name : generator.importedComponents[ node.name ] || `template.components.${node.name}`; + const expression = node.name === ':Self' ? generator.name : generator.importedComponents[ node.name ] || `${generator.alias( 'template' )}.components.${node.name}`; local.init.addBlockAtStart( deindent` ${statements.join( '\n\n' )} diff --git a/src/generators/dom/visitors/attributes/addElementAttributes.js b/src/generators/dom/visitors/attributes/addElementAttributes.js index f355354ab2..191e612a0e 100644 --- a/src/generators/dom/visitors/attributes/addElementAttributes.js +++ b/src/generators/dom/visitors/attributes/addElementAttributes.js @@ -182,7 +182,7 @@ export default function addElementAttributes ( generator, node, local ) { if ( name in generator.events ) { local.init.addBlock( deindent` - var ${handlerName} = template.events.${name}.call( component, ${local.name}, function ( event ) { + var ${handlerName} = ${generator.alias( 'template' )}.events.${name}.call( component, ${local.name}, function ( event ) { ${handlerBody} }); ` ); diff --git a/src/generators/server-side-rendering/index.js b/src/generators/server-side-rendering/index.js index efe0d13ea7..0a4fd2afa0 100644 --- a/src/generators/server-side-rendering/index.js +++ b/src/generators/server-side-rendering/index.js @@ -60,12 +60,12 @@ export default function ssr ( parsed, source, options, names ) { parsed.html.children.forEach( node => generator.visit( node ) ); builders.render.addLine( - templateProperties.data ? `root = Object.assign( template.data(), root || {} );` : `root = root || {};` + templateProperties.data ? `root = Object.assign( ${generator.alias( 'template' )}.data(), root || {} );` : `root = root || {};` ); computations.forEach( ({ key, deps }) => { builders.render.addLine( - `root.${key} = template.computed.${key}( ${deps.map( dep => `root.${dep}` ).join( ', ' )} );` + `root.${key} = ${generator.alias( 'template' )}.computed.${key}( ${deps.map( dep => `root.${dep}` ).join( ', ' )} );` ); }); @@ -118,7 +118,7 @@ export default function ssr ( parsed, source, options, names ) { ` ); templateProperties.components.value.properties.forEach( prop => { - builders.renderCss.addLine( `addComponent( template.components.${prop.key.name} );` ); + builders.renderCss.addLine( `addComponent( ${generator.alias( 'template' )}.components.${prop.key.name} );` ); }); } @@ -140,7 +140,7 @@ export default function ssr ( parsed, source, options, names ) { ${name}.filename = ${JSON.stringify( options.filename )}; ${name}.data = function () { - return ${templateProperties.data ? `template.data()` : `{}`}; + return ${templateProperties.data ? `${generator.alias( 'template' )}.data()` : `{}`}; }; ${name}.render = function ( root, options ) { diff --git a/src/generators/server-side-rendering/visitors/Component.js b/src/generators/server-side-rendering/visitors/Component.js index 895792a7d0..0a13ade27c 100644 --- a/src/generators/server-side-rendering/visitors/Component.js +++ b/src/generators/server-side-rendering/visitors/Component.js @@ -50,7 +50,7 @@ export default { })) .join( ', ' ); - const expression = node.name === ':Self' ? generator.name : `template.components.${node.name}`; + const expression = node.name === ':Self' ? generator.name : `${generator.alias( 'template' )}.components.${node.name}`; bindings.forEach( binding => { generator.addBinding( binding, expression ); diff --git a/test/generator/samples/deconflict-template-1/_config.js b/test/generator/samples/deconflict-template-1/_config.js new file mode 100644 index 0000000000..c7ca7bec26 --- /dev/null +++ b/test/generator/samples/deconflict-template-1/_config.js @@ -0,0 +1,3 @@ +export default { + html: `template` +}; diff --git a/test/generator/samples/deconflict-template-1/main.html b/test/generator/samples/deconflict-template-1/main.html new file mode 100644 index 0000000000..f716419dc4 --- /dev/null +++ b/test/generator/samples/deconflict-template-1/main.html @@ -0,0 +1,13 @@ +{{value}} + + diff --git a/test/generator/samples/deconflict-template-1/module.js b/test/generator/samples/deconflict-template-1/module.js new file mode 100644 index 0000000000..aa68a2e4ce --- /dev/null +++ b/test/generator/samples/deconflict-template-1/module.js @@ -0,0 +1 @@ +export default 'template'; diff --git a/test/generator/samples/deconflict-template-2/_config.js b/test/generator/samples/deconflict-template-2/_config.js new file mode 100644 index 0000000000..c7ca7bec26 --- /dev/null +++ b/test/generator/samples/deconflict-template-2/_config.js @@ -0,0 +1,3 @@ +export default { + html: `template` +}; diff --git a/test/generator/samples/deconflict-template-2/main.html b/test/generator/samples/deconflict-template-2/main.html new file mode 100644 index 0000000000..34994a335b --- /dev/null +++ b/test/generator/samples/deconflict-template-2/main.html @@ -0,0 +1,15 @@ +{{value}} + +