diff --git a/src/generators/Generator.js b/src/generators/Generator.js index bdf8de7278..d6056930e2 100644 --- a/src/generators/Generator.js +++ b/src/generators/Generator.js @@ -242,7 +242,7 @@ export default class Generator { getUniqueName ( name ) { let alias = name; - for ( let i = 1; reservedNames.has( alias ) || this.importedNames.has( alias ) || this._usedNames.has( alias ); alias = `${name}$${i++}` ); + for ( let i = 1; reservedNames.has( alias ) || this.importedNames.has( alias ) || this._usedNames.has( alias ); alias = `${name}_${i++}` ); this._usedNames.add( alias ); return alias; } @@ -251,7 +251,7 @@ export default class Generator { 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++}` ); + 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; }; @@ -296,7 +296,7 @@ export default class Generator { } else { const { declarations } = annotateWithScopes( js ); let template = 'template'; - for ( let i = 1; declarations.has( template ); template = `template$${i++}` ); + for ( let i = 1; declarations.has( template ); template = `template_${i++}` ); this.code.overwrite( defaultExport.start, defaultExport.declaration.start, `var ${template} = ` ); diff --git a/src/generators/dom/index.js b/src/generators/dom/index.js index 233616e40f..32b9ec793d 100644 --- a/src/generators/dom/index.js +++ b/src/generators/dom/index.js @@ -78,7 +78,7 @@ class DomGenerator extends Generator { } else { properties.addBlock( deindent` update: function ( changed, ${fragment.params.join( ', ' )} ) { - var __tmp; + ${fragment.tmp ? `var ${fragment.tmp};` : ''} ${fragment.builders.update} }, @@ -119,8 +119,9 @@ class DomGenerator extends Generator { } } - generateBlock ( node, name ) { + generateBlock ( node, name, type ) { this.push({ + type, name, target: 'target', localElementDepth: 0, @@ -203,7 +204,8 @@ export default function dom ( parsed, source, options ) { const component = getUniqueName( 'component' ); generator.push({ - name: generator.alias( 'renderMainFragment' ), + type: 'block', + name: generator.alias( 'render_main_fragment' ), namespace, target: 'target', localElementDepth: 0, @@ -241,7 +243,7 @@ export default function dom ( parsed, source, options ) { } builders._set.addLine( 'var oldState = this._state;' ); - builders._set.addLine( 'this._state = Object.assign( {}, oldState, newState );' ); + builders._set.addLine( `this._state = ${generator.helper( 'assign' )}( {}, oldState, newState );` ); if ( computations.length ) { const builder = new CodeBuilder(); @@ -277,13 +279,13 @@ export default function dom ( parsed, source, options ) { if ( generator.css && options.css !== false ) { builders.main.addBlock( deindent` - var ${generator.alias( 'addedCss' )} = false; - function ${generator.alias( 'addCss' )} () { + var ${generator.alias( 'added_css' )} = false; + function ${generator.alias( 'add_css' )} () { var style = ${generator.helper( 'createElement' )}( 'style' ); style.textContent = ${JSON.stringify( generator.css )}; ${generator.helper( 'appendNode' )}( style, document.head ); - ${generator.alias( 'addedCss' )} = true; + ${generator.alias( 'added_css' )} = true; } ` ); } @@ -294,7 +296,7 @@ export default function dom ( parsed, source, options ) { builders.init.addLine( `this._torndown = false;` ); if ( parsed.css && options.css !== false ) { - builders.init.addLine( `if ( !${generator.alias( 'addedCss' )} ) ${generator.alias( 'addCss' )}();` ); + builders.init.addLine( `if ( !${generator.alias( 'added_css' )} ) ${generator.alias( 'add_css' )}();` ); } if ( generator.hasComponents ) { @@ -304,7 +306,7 @@ export default function dom ( parsed, source, options ) { if ( generator.hasComplexBindings ) { builders.init.addBlock( deindent` this._bindings = []; - this._fragment = ${generator.alias( 'renderMainFragment' )}( this._state, this ); + this._fragment = ${generator.alias( 'render_main_fragment' )}( this._state, this ); if ( options.target ) this._fragment.mount( options.target, null ); while ( this._bindings.length ) this._bindings.pop()(); ` ); @@ -312,7 +314,7 @@ export default function dom ( parsed, source, options ) { builders._set.addLine( `while ( this._bindings.length ) this._bindings.pop()();` ); } else { builders.init.addBlock( deindent` - this._fragment = ${generator.alias( 'renderMainFragment' )}( this._state, this ); + this._fragment = ${generator.alias( 'render_main_fragment' )}( this._state, this ); if ( options.target ) this._fragment.mount( options.target, null ); ` ); } @@ -340,7 +342,7 @@ export default function dom ( parsed, source, options ) { if ( generator.usesRefs ) constructorBlock.addLine( `this.refs = {};` ); constructorBlock.addLine( - `this._state = ${templateProperties.data ? `Object.assign( ${generator.alias( 'template' )}.data(), options.data )` : `options.data || {}`};` + `this._state = ${templateProperties.data ? `${generator.helper( 'assign' )}( ${generator.alias( 'template' )}.data(), options.data )` : `options.data || {}`};` ); if ( !generator.builders.metaBindings.isEmpty() ) { @@ -393,7 +395,7 @@ export default function dom ( parsed, source, options ) { if ( sharedPath ) { const base = templateProperties.methods ? `{}, ${generator.alias( 'template' )}.methods` : `{}`; - builders.main.addBlock( `${name}.prototype = Object.assign( ${base}, ${generator.helper( 'proto' )} );` ); + builders.main.addBlock( `${name}.prototype = ${generator.helper( 'assign' )}( ${base}, ${generator.helper( 'proto' )} );` ); } else { if ( templateProperties.methods ) { builders.main.addBlock( `${name}.prototype = ${generator.alias( 'template' )}.methods;` ); diff --git a/src/generators/dom/utils/findBlock.js b/src/generators/dom/utils/findBlock.js new file mode 100644 index 0000000000..59f71e3552 --- /dev/null +++ b/src/generators/dom/utils/findBlock.js @@ -0,0 +1,4 @@ +export default function findBlock ( fragment ) { + while ( fragment.type !== 'block' ) fragment = fragment.parent; + return fragment; +} \ No newline at end of file diff --git a/src/generators/dom/visitors/Component.js b/src/generators/dom/visitors/Component.js index d7849e9438..69e2e3c2ac 100644 --- a/src/generators/dom/visitors/Component.js +++ b/src/generators/dom/visitors/Component.js @@ -76,12 +76,12 @@ export default { // Component has children, put them in a separate {{yield}} block if ( hasChildren ) { - const yieldName = generator.getUniqueName( `render${name}YieldFragment` ); + const yieldName = generator.getUniqueName( `render_${name}_yield_fragment` ); const params = current.params.join( ', ' ); - generator.generateBlock( node, yieldName ); + generator.generateBlock( node, yieldName, 'block' ); - const yieldFragment = current.getUniqueName( `${name}_yieldFragment` ); + const yieldFragment = current.getUniqueName( `${name}_yield_fragment` ); current.builders.init.addLine( `var ${yieldFragment} = ${yieldName}( ${params}, ${current.component} );` @@ -104,9 +104,9 @@ export default { const initialPropString = stringifyProps( initialProps ); if ( local.bindings.length ) { - const initialData = current.getUniqueName( `${name}_initialData` ); + const initialData = current.getUniqueName( `${name}_initial_data` ); - statements.push( `var ${name}_initialData = ${initialPropString};` ); + statements.push( `var ${name}_initial_data = ${initialPropString};` ); local.bindings.forEach( binding => { statements.push( `if ( ${binding.prop} in ${binding.obj} ) ${initialData}.${binding.name} = ${binding.value};` ); @@ -159,6 +159,7 @@ export default { if ( !local.update.isEmpty() ) current.builders.update.addBlock( local.update ); generator.push({ + type: 'component', namespace: local.namespace, target: name, parent: current, diff --git a/src/generators/dom/visitors/EachBlock.js b/src/generators/dom/visitors/EachBlock.js index 2dc6f222c8..df9936197f 100644 --- a/src/generators/dom/visitors/EachBlock.js +++ b/src/generators/dom/visitors/EachBlock.js @@ -4,8 +4,8 @@ import getBuilders from '../utils/getBuilders.js'; export default { enter ( generator, node ) { - const name = generator.getUniqueName( `eachBlock` ); - const renderer = generator.getUniqueName( `renderEachBlock` ); + const name = generator.getUniqueName( `each_block` ); + const renderer = generator.getUniqueName( `render_each_block` ); const elseName = generator.getUniqueName( `${name}_else` ); const renderElse = generator.getUniqueName( `${renderer}_else` ); const i = generator.current.getUniqueName( `i` ); @@ -168,11 +168,11 @@ export default { } if ( node.else ) { - generator.generateBlock( node.else, renderElse ); + generator.generateBlock( node.else, renderElse, 'block' ); } const indexNames = new Map( generator.current.indexNames ); - const indexName = node.index || generator.current.getUniqueName( `${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 ); @@ -193,6 +193,7 @@ export default { const getUniqueName = generator.getUniqueNameMaker( blockParams ); generator.push({ + type: 'block', name: renderer, target: 'target', expression: node.expression, diff --git a/src/generators/dom/visitors/Element.js b/src/generators/dom/visitors/Element.js index 478bc39e2e..566a535bab 100644 --- a/src/generators/dom/visitors/Element.js +++ b/src/generators/dom/visitors/Element.js @@ -101,6 +101,7 @@ export default { generator.createMountStatement( name ); generator.push({ + type: 'element', namespace: local.namespace, target: name, parent: generator.current, diff --git a/src/generators/dom/visitors/IfBlock.js b/src/generators/dom/visitors/IfBlock.js index 81552588e5..0de7e936b2 100644 --- a/src/generators/dom/visitors/IfBlock.js +++ b/src/generators/dom/visitors/IfBlock.js @@ -9,7 +9,7 @@ function getConditionsAndBlocks ( generator, node, _name, i = 0 ) { block: name }]; - generator.generateBlock( node, name ); + generator.generateBlock( node, name, 'block' ); if ( node.else && node.else.children.length === 1 && node.else.children[0].type === 'IfBlock' ) { @@ -24,7 +24,7 @@ function getConditionsAndBlocks ( generator, node, _name, i = 0 ) { }); if ( node.else ) { - generator.generateBlock( node.else, name ); + generator.generateBlock( node.else, name, 'block' ); } } return conditionsAndBlocks; @@ -33,13 +33,13 @@ function getConditionsAndBlocks ( generator, node, _name, i = 0 ) { export default { enter ( generator, node ) { const params = generator.current.params.join( ', ' ); - const name = generator.getUniqueName( `ifBlock` ); - const getBlock = generator.current.getUniqueName( `getBlock` ); - const currentBlock = generator.current.getUniqueName( `currentBlock` ); - const _currentBlock = generator.current.getUniqueName( `_currentBlock` ); + const name = generator.getUniqueName( `if_block` ); + const getBlock = generator.current.getUniqueName( `get_block` ); + const currentBlock = generator.current.getUniqueName( `current_block` ); + const _currentBlock = generator.current.getUniqueName( `_current_block` ); const isToplevel = generator.current.localElementDepth === 0; - const conditionsAndBlocks = getConditionsAndBlocks( generator, node, generator.getUniqueName( `renderIfBlock` ) ); + const conditionsAndBlocks = getConditionsAndBlocks( generator, node, generator.getUniqueName( `render_if_block` ) ); const anchor = `${name}_anchor`; generator.createAnchor( anchor ); diff --git a/src/generators/dom/visitors/MustacheTag.js b/src/generators/dom/visitors/MustacheTag.js index 35ec8ddee3..6e0ba72e63 100644 --- a/src/generators/dom/visitors/MustacheTag.js +++ b/src/generators/dom/visitors/MustacheTag.js @@ -1,4 +1,5 @@ import deindent from '../../../utils/deindent.js'; +import findBlock from '../utils/findBlock.js'; export default { enter ( generator, node ) { @@ -9,9 +10,12 @@ export default { generator.current.builders.init.addLine( `var last_${name} = ${snippet};` ); generator.addElement( name, `${generator.helper( 'createText' )}( last_${name} )`, true ); + const fragment = findBlock( generator.current ); + if ( !fragment.tmp ) fragment.tmp = fragment.getUniqueName( 'tmp' ); + generator.current.builders.update.addBlock( deindent` - if ( ( __tmp = ${snippet} ) !== last_${name} ) { - ${name}.data = last_${name} = __tmp; + if ( ( ${fragment.tmp} = ${snippet} ) !== last_${name} ) { + ${name}.data = last_${name} = ${fragment.tmp}; } ` ); } diff --git a/src/generators/dom/visitors/RawMustacheTag.js b/src/generators/dom/visitors/RawMustacheTag.js index 5f2d54c0e0..2a5cf46381 100644 --- a/src/generators/dom/visitors/RawMustacheTag.js +++ b/src/generators/dom/visitors/RawMustacheTag.js @@ -1,4 +1,5 @@ import deindent from '../../../utils/deindent.js'; +import findBlock from '../utils/findBlock.js'; export default { enter ( generator, node ) { @@ -26,9 +27,12 @@ export default { generator.current.builders.init.addLine( mountStatement ); } + const fragment = findBlock( generator.current ); + if ( !fragment.tmp ) fragment.tmp = fragment.getUniqueName( 'tmp' ); + generator.current.builders.update.addBlock( deindent` - if ( ( __tmp = ${snippet} ) !== last_${name} ) { - last_${name} = __tmp; + if ( ( ${fragment.tmp} = ${snippet} ) !== last_${name} ) { + last_${name} = ${fragment.tmp}; ${detachStatement} ${mountStatement} } diff --git a/src/generators/dom/visitors/attributes/addElementAttributes.js b/src/generators/dom/visitors/attributes/addElementAttributes.js index 1f1132aa73..2d96cc73bf 100644 --- a/src/generators/dom/visitors/attributes/addElementAttributes.js +++ b/src/generators/dom/visitors/attributes/addElementAttributes.js @@ -3,6 +3,7 @@ import addElementBinding from './addElementBinding'; import deindent from '../../../../utils/deindent.js'; import flattenReference from '../../../../utils/flattenReference.js'; import getStaticAttributeValue from './binding/getStaticAttributeValue.js'; +import findBlock from '../../utils/findBlock.js'; export default function addElementAttributes ( generator, node, local ) { node.attributes.forEach( attribute => { @@ -105,9 +106,12 @@ export default function addElementAttributes ( generator, node, local ) { } local.init.addLine( updater ); + const fragment = findBlock( generator.current ); + if ( !fragment.tmp ) fragment.tmp = fragment.getUniqueName( 'tmp' ); + local.update.addBlock( deindent` - if ( ( __tmp = ${snippet} ) !== ${last} ) { - ${last} = __tmp; + if ( ( ${fragment.tmp} = ${snippet} ) !== ${last} ) { + ${last} = ${fragment.tmp}; ${updater} } ` ); @@ -177,7 +181,7 @@ export default function addElementAttributes ( generator, node, local ) { return `var ${listName} = this.__svelte.${listName}, ${indexName} = this.__svelte.${indexName}, ${name} = ${listName}[${indexName}]`; }); - const handlerName = generator.current.getUniqueName( `${name}Handler` ); + const handlerName = generator.current.getUniqueName( `${name}_handler` ); const handlerBody = ( declarations.length ? declarations.join( '\n' ) + '\n\n' : '' ) + `[✂${attribute.expression.start}-${attribute.expression.end}✂];`; if ( generator.events.has( name ) ) { diff --git a/src/generators/dom/visitors/attributes/addElementBinding.js b/src/generators/dom/visitors/attributes/addElementBinding.js index e8a049570e..eea65eda93 100644 --- a/src/generators/dom/visitors/attributes/addElementBinding.js +++ b/src/generators/dom/visitors/attributes/addElementBinding.js @@ -13,7 +13,7 @@ export default function createBinding ( generator, node, attribute, current, loc if ( !~local.allUsedContexts.indexOf( context ) ) local.allUsedContexts.push( context ); }); - const handler = current.getUniqueName( `${local.name}ChangeHandler` ); + const handler = current.getUniqueName( `${local.name}_change_handler` ); const isMultipleSelect = node.name === 'select' && node.attributes.find( attr => attr.name.toLowerCase() === 'multiple' ); // TODO use getStaticAttributeValue const type = getStaticAttributeValue( node, 'type' ); diff --git a/src/shared/index.js b/src/shared/index.js index 529435821b..1ce6b42ceb 100644 --- a/src/shared/index.js +++ b/src/shared/index.js @@ -3,6 +3,15 @@ export * from './methods.js'; export function noop () {} +export function assign ( target ) { + for ( var i = 1; i < arguments.length; i += 1 ) { + var source = arguments[i]; + for ( var k in source ) target[k] = source[k]; + } + + return target; +} + export function differs ( a, b ) { return ( a !== b ) || ( a && ( typeof a === 'object' ) || ( typeof a === 'function' ) ); } diff --git a/test/generator/index.js b/test/generator/index.js index 04599c6f2b..8ee4280828 100644 --- a/test/generator/index.js +++ b/test/generator/index.js @@ -30,6 +30,8 @@ require.extensions[ '.html' ] = function ( module, filename ) { return module._compile( code, filename ); }; +const Object_assign = Object.assign; + describe( 'generate', () => { before( setupHtmlEqual ); @@ -67,7 +69,7 @@ describe( 'generate', () => { // check that no ES2015+ syntax slipped in if ( !config.allowES2015 ) { try { - const startIndex = code.indexOf( 'function renderMainFragment' ); // may change! + const startIndex = code.indexOf( 'function render_main_fragment' ); // may change! const es5 = spaces( startIndex ) + code.slice( startIndex ).replace( /export default .+/, '' ); acorn.parse( es5, { ecmaVersion: 5 }); } catch ( err ) { @@ -93,6 +95,10 @@ describe( 'generate', () => { return env() .then( window => { + Object.assign = () => { + throw new Error( 'cannot use Object.assign in generated code, as it is not supported everywhere' ); + }; + global.window = window; // Put the constructor on window for testing @@ -145,6 +151,9 @@ describe( 'generate', () => { if ( !config.show ) console.log( addLineNumbers( code ) ); // eslint-disable-line no-console throw err; } + }) + .then( () => { + Object.assign = Object_assign; }); }); }