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 { 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++}`;
if ( this._aliases.has( name ) ) {
return this._aliases.get( name );
}
this.aliases.set( name, alias );
}
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 );
});
}
}

@ -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 ) ) );
});
}

@ -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
});
},

@ -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,
});
},

@ -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} );
}
` );

@ -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`

@ -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 );`
);
}
};

@ -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;
` );
}

@ -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;
}
` );
}

@ -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;
` );
}

@ -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,
// <input type='checkbox' bind:group='foo'>
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`;

@ -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 ) {

@ -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();

@ -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;
}
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 = {} ) {

@ -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' ] );

@ -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;
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 );

@ -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
};
}

@ -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;

@ -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