Merge pull request #306 from sveltejs/gh-222-a

Deconflict shared helpers with user imports
pull/309/head
Rich Harris 8 years ago committed by GitHub
commit e486ed0e1d

@ -213,7 +213,6 @@ export default class Generator {
while ( /[ \t]/.test( source[ a - 1 ] ) ) a -= 1; while ( /[ \t]/.test( source[ a - 1 ] ) ) a -= 1;
while ( source[b] === '\n' ) b += 1; while ( source[b] === '\n' ) b += 1;
//imports.push( source.slice( a, b ).replace( /^\s/, '' ) );
imports.push( node ); imports.push( node );
this.code.remove( a, b ); this.code.remove( a, b );
} }

@ -12,6 +12,11 @@ class DomGenerator extends Generator {
super( parsed, source, names, visitors ); super( parsed, source, names, visitors );
this.renderers = []; this.renderers = [];
this.uses = {}; 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 = {};
} }
addElement ( name, renderStatement, needsIdentifier = false ) { addElement ( name, renderStatement, needsIdentifier = false ) {
@ -23,13 +28,11 @@ class DomGenerator extends Generator {
this.createMountStatement( name ); this.createMountStatement( name );
} else { } else {
this.uses.appendNode = true; this.current.builders.init.addLine( `${this.helper( 'appendNode' )}( ${renderStatement}, ${this.current.target} );` );
this.current.builders.init.addLine( `appendNode( ${renderStatement}, ${this.current.target} );` );
} }
if ( isToplevel ) { if ( isToplevel ) {
this.uses.detachNode = true; this.current.builders.detach.addLine( `${this.helper( 'detachNode' )}( ${name} );` );
this.current.builders.detach.addLine( `detachNode( ${name} );` );
} }
} }
@ -55,8 +58,7 @@ class DomGenerator extends Generator {
if ( fragment.key ) properties.addBlock( `key: key,` ); if ( fragment.key ) properties.addBlock( `key: key,` );
if ( fragment.builders.mount.isEmpty() ) { if ( fragment.builders.mount.isEmpty() ) {
this.uses.noop = true; properties.addBlock( `mount: ${this.helper( 'noop' )},` );
properties.addBlock( `mount: noop,` );
} else { } else {
properties.addBlock( deindent` properties.addBlock( deindent`
mount: function ( target, anchor ) { mount: function ( target, anchor ) {
@ -66,8 +68,7 @@ class DomGenerator extends Generator {
} }
if ( fragment.builders.update.isEmpty() ) { if ( fragment.builders.update.isEmpty() ) {
this.uses.noop = true; properties.addBlock( `update: ${this.helper( 'noop' )},` );
properties.addBlock( `update: noop,` );
} else { } else {
properties.addBlock( deindent` properties.addBlock( deindent`
update: function ( changed, ${fragment.params} ) { update: function ( changed, ${fragment.params} ) {
@ -79,8 +80,7 @@ class DomGenerator extends Generator {
} }
if ( fragment.builders.teardown.isEmpty() ) { if ( fragment.builders.teardown.isEmpty() ) {
this.uses.noop = true; properties.addBlock( `teardown: ${this.helper( 'noop' )},` );
properties.addBlock( `teardown: noop,` );
} else { } else {
properties.addBlock( deindent` properties.addBlock( deindent`
teardown: function ( detach ) { teardown: function ( detach ) {
@ -101,18 +101,15 @@ class DomGenerator extends Generator {
} }
createAnchor ( name ) { createAnchor ( name ) {
this.uses.createComment = true; const renderStatement = `${this.helper( 'createComment' )}()`;
const renderStatement = `createComment()`;
this.addElement( name, renderStatement, true ); this.addElement( name, renderStatement, true );
} }
createMountStatement ( name ) { createMountStatement ( name ) {
if ( this.current.target === 'target' ) { if ( this.current.target === 'target' ) {
this.uses.insertNode = true; this.current.builders.mount.addLine( `${this.helper( 'insertNode' )}( ${name}, target, anchor );` );
this.current.builders.mount.addLine( `insertNode( ${name}, target, anchor );` );
} else { } else {
this.uses.appendNode = true; this.current.builders.init.addLine( `${this.helper( 'appendNode' )}( ${name}, ${this.current.target} );` );
this.current.builders.init.addLine( `appendNode( ${name}, ${this.current.target} );` );
} }
} }
@ -133,6 +130,22 @@ class DomGenerator extends Generator {
// unset the children, to avoid them being visited again // unset the children, to avoid them being visited again
node.children = []; node.children = [];
} }
helper ( name ) {
this.uses[ name ] = true;
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 ) { export default function dom ( parsed, source, options, names ) {
@ -143,6 +156,12 @@ export default function dom ( parsed, source, options, names ) {
const { computations, templateProperties } = generator.parseJs(); const { computations, templateProperties } = generator.parseJs();
generator.imports.forEach( node => {
node.specifiers.forEach( specifier => {
generator.importedNames[ specifier.local.name ] = true;
});
});
let namespace = null; let namespace = null;
if ( templateProperties.namespace ) { if ( templateProperties.namespace ) {
const ns = templateProperties.namespace.value; const ns = templateProperties.namespace.value;
@ -214,15 +233,12 @@ export default function dom ( parsed, source, options, names ) {
} }
if ( parsed.css && options.css !== false ) { if ( parsed.css && options.css !== false ) {
generator.uses.appendNode = true;
generator.uses.createElement = true;
builders.main.addBlock( deindent` builders.main.addBlock( deindent`
let addedCss = false; let addedCss = false;
function addCss () { function addCss () {
var style = createElement( 'style' ); var style = ${generator.helper( 'createElement' )}( 'style' );
style.textContent = ${JSON.stringify( processCss( parsed, generator.code ) )}; style.textContent = ${JSON.stringify( processCss( parsed, generator.code ) )};
appendNode( style, document.head ); ${generator.helper( 'appendNode' )}( style, document.head );
addedCss = true; addedCss = true;
} }
@ -305,12 +321,12 @@ export default function dom ( parsed, source, options, names ) {
builders.main.addBlock( sharedPath ? builders.main.addBlock( sharedPath ?
deindent` deindent`
${name}.prototype.get = get; ${name}.prototype.get = ${generator.helper( 'get' )};
${name}.prototype.fire = fire; ${name}.prototype.fire = ${generator.helper( 'fire' )};
${name}.prototype.observe = observe; ${name}.prototype.observe = ${generator.helper( 'observe' )};
${name}.prototype.on = on; ${name}.prototype.on = ${generator.helper( 'on' )};
${name}.prototype.set = set; ${name}.prototype.set = ${generator.helper( 'set' )};
${name}.prototype._flush = _flush; ${name}.prototype._flush = ${generator.helper( '_flush' )};
` : ` :
deindent` deindent`
${name}.prototype.get = ${shared.get}; ${name}.prototype.get = ${shared.get};
@ -347,7 +363,9 @@ export default function dom ( parsed, source, options, names ) {
throw new Error( `Components with shared helpers must be compiled to ES2015 modules (format: 'es')` ); throw new Error( `Components with shared helpers must be compiled to ES2015 modules (format: 'es')` );
} }
const names = [ 'get', 'fire', 'observe', 'on', 'set', '_flush', 'dispatchObservers' ].concat( Object.keys( generator.uses ) ); const names = [ 'get', 'fire', 'observe', 'on', 'set', '_flush', 'dispatchObservers' ].concat( Object.keys( generator.uses ) )
.map( name => name in generator.aliases ? `${name} as ${generator.aliases[ name ]}` : name );
builders.main.addLineAtStart( builders.main.addLineAtStart(
`import { ${names.join( ', ' )} } from ${JSON.stringify( sharedPath )}` `import { ${names.join( ', ' )} } from ${JSON.stringify( sharedPath )}`
); );

@ -151,9 +151,8 @@ export default {
` ); ` );
} }
generator.uses.teardownEach = true;
generator.current.builders.teardown.addBlock( generator.current.builders.teardown.addBlock(
`teardownEach( ${iterations}, ${isToplevel ? 'detach' : 'false'} );` ); `${generator.helper( 'teardownEach' )}( ${iterations}, ${isToplevel ? 'detach' : 'false'} );` );
if ( node.else ) { if ( node.else ) {
generator.current.builders.teardown.addBlock( deindent` generator.current.builders.teardown.addBlock( deindent`

@ -61,25 +61,21 @@ export default {
if ( local.namespace ) { if ( local.namespace ) {
if ( local.namespace === 'http://www.w3.org/2000/svg' ) { if ( local.namespace === 'http://www.w3.org/2000/svg' ) {
generator.uses.createSvgElement = true; render = `var ${name} = ${generator.helper( 'createSvgElement' )}( '${node.name}' )`;
render = `var ${name} = createSvgElement( '${node.name}' )`;
} else { } else {
render = `var ${name} = document.createElementNS( '${local.namespace}', '${node.name}' );`; render = `var ${name} = document.createElementNS( '${local.namespace}', '${node.name}' );`;
} }
} else { } else {
generator.uses.createElement = true; render = `var ${name} = ${generator.helper( 'createElement' )}( '${node.name}' );`;
render = `var ${name} = createElement( '${node.name}' );`;
} }
if ( generator.cssId && !generator.elementDepth ) { if ( generator.cssId && !generator.elementDepth ) {
generator.uses.setAttribute = true; render += `\n${generator.helper( 'setAttribute' )}( ${name}, '${generator.cssId}', '' );`;
render += `\nsetAttribute( ${name}, '${generator.cssId}', '' );`;
} }
local.init.addLineAtStart( render ); local.init.addLineAtStart( render );
if ( isToplevel ) { if ( isToplevel ) {
generator.uses.detachNode = true; generator.current.builders.detach.addLine( `${generator.helper( 'detachNode' )}( ${name} );` );
generator.current.builders.detach.addLine( `detachNode( ${name} );` );
} }
// special case bound <option> without a value attribute // special case bound <option> without a value attribute

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

@ -9,20 +9,17 @@ export default {
// we would have used comments here, but the `insertAdjacentHTML` api only // we would have used comments here, but the `insertAdjacentHTML` api only
// exists for `Element`s. // exists for `Element`s.
generator.uses.createElement = true;
const before = `${name}_before`; const before = `${name}_before`;
generator.addElement( before, `createElement( 'noscript' )`, true ); generator.addElement( before, `${generator.helper( 'createElement' )}( 'noscript' )`, true );
const after = `${name}_after`; const after = `${name}_after`;
generator.addElement( after, `createElement( 'noscript' )`, true ); generator.addElement( after, `${generator.helper( 'createElement' )}( 'noscript' )`, true );
const isToplevel = generator.current.localElementDepth === 0; const isToplevel = generator.current.localElementDepth === 0;
generator.current.builders.init.addLine( `var last_${name} = ${snippet};` ); generator.current.builders.init.addLine( `var last_${name} = ${snippet};` );
const mountStatement = `${before}.insertAdjacentHTML( 'afterend', last_${name} );`; const mountStatement = `${before}.insertAdjacentHTML( 'afterend', last_${name} );`;
generator.uses.detachBetween = true; const detachStatement = `${generator.helper( 'detachBetween' )}( ${before}, ${after} );`;
const detachStatement = `detachBetween( ${before}, ${after} );`;
if ( isToplevel ) { if ( isToplevel ) {
generator.current.builders.mount.addLine( mountStatement ); generator.current.builders.mount.addLine( mountStatement );

@ -5,8 +5,6 @@ export default {
} }
const name = generator.current.getUniqueName( `text` ); const name = generator.current.getUniqueName( `text` );
generator.addElement( name, `createText( ${JSON.stringify( node.data )} )`, false ); generator.addElement( name, `${generator.helper( 'createText' )}( ${JSON.stringify( node.data )} )`, false );
generator.uses.createText = true;
} }
}; };

@ -21,7 +21,7 @@ export default function addElementAttributes ( generator, node, local ) {
// xlink is a special case... we could maybe extend this to generic // xlink is a special case... we could maybe extend this to generic
// namespaced attributes but I'm not sure that's applicable in // namespaced attributes but I'm not sure that's applicable in
// HTML5? // HTML5?
const helper = isXlink ? 'setXlinkAttribute' : 'setAttribute'; const method = isXlink ? 'setXlinkAttribute' : 'setAttribute';
if ( attribute.value === true ) { if ( attribute.value === true ) {
// attributes without values, e.g. <textarea readonly> // attributes without values, e.g. <textarea readonly>
@ -30,9 +30,8 @@ export default function addElementAttributes ( generator, node, local ) {
`${local.name}.${propertyName} = true;` `${local.name}.${propertyName} = true;`
); );
} else { } else {
generator.uses[ helper ] = true;
local.init.addLine( local.init.addLine(
`${helper}( ${local.name}, '${name}', true );` `${generator.helper( method )}( ${local.name}, '${name}', true );`
); );
} }
@ -48,9 +47,8 @@ export default function addElementAttributes ( generator, node, local ) {
`${local.name}.${propertyName} = '';` `${local.name}.${propertyName} = '';`
); );
} else { } else {
generator.uses[ helper ] = true;
local.init.addLine( local.init.addLine(
`${helper}( ${local.name}, '${name}', '' );` `${generator.helper( method )}( ${local.name}, '${name}', '' );`
); );
} }
} }
@ -79,9 +77,8 @@ export default function addElementAttributes ( generator, node, local ) {
} }
if ( addAttribute ) { if ( addAttribute ) {
generator.uses[ helper ] = true;
local.init.addLine( local.init.addLine(
`${helper}( ${local.name}, '${name}', ${result} );` `${generator.helper( method )}( ${local.name}, '${name}', ${result} );`
); );
} }
} }
@ -99,8 +96,7 @@ export default function addElementAttributes ( generator, node, local ) {
if ( propertyName ) { if ( propertyName ) {
updater = `${local.name}.${propertyName} = ${last};`; updater = `${local.name}.${propertyName} = ${last};`;
} else { } else {
generator.uses[ helper ] = true; updater = `${generator.helper( method )}( ${local.name}, '${name}', ${last} );`; // TODO use snippet both times see note below
updater = `${helper}( ${local.name}, '${name}', ${last} );`; // TODO use snippet both times see note below
} }
local.init.addLine( updater ); local.init.addLine( updater );
@ -133,8 +129,7 @@ export default function addElementAttributes ( generator, node, local ) {
if (propertyName) { if (propertyName) {
updater = `${local.name}.${propertyName} = ${value};`; updater = `${local.name}.${propertyName} = ${value};`;
} else { } else {
generator.uses[ helper ] = true; updater = `${generator.helper( method )}( ${local.name}, '${name}', ${value} );`;
updater = `${helper}( ${local.name}, '${name}', ${value} );`;
} }
local.init.addLine( updater ); local.init.addLine( updater );
@ -194,18 +189,16 @@ export default function addElementAttributes ( generator, node, local ) {
${handlerName}.teardown(); ${handlerName}.teardown();
` ); ` );
} else { } else {
generator.uses.addEventListener = true;
generator.uses.removeEventListener = true;
local.init.addBlock( deindent` local.init.addBlock( deindent`
function ${handlerName} ( event ) { function ${handlerName} ( event ) {
${handlerBody} ${handlerBody}
} }
addEventListener( ${local.name}, '${name}', ${handlerName} ); ${generator.helper( 'addEventListener' )}( ${local.name}, '${name}', ${handlerName} );
` ); ` );
generator.current.builders.teardown.addLine( deindent` generator.current.builders.teardown.addLine( deindent`
removeEventListener( ${local.name}, '${name}', ${handlerName} ); ${generator.helper( 'removeEventListener' )}( ${local.name}, '${name}', ${handlerName} );
` ); ` );
} }
} }

@ -138,8 +138,6 @@ export default function createBinding ( generator, node, attribute, current, loc
updateElement = `${local.name}.${attribute.name} = ${contextual ? attribute.value : `root.${attribute.value}`};`; updateElement = `${local.name}.${attribute.name} = ${contextual ? attribute.value : `root.${attribute.value}`};`;
} }
generator.uses.addEventListener = true;
generator.uses.removeEventListener = true;
local.init.addBlock( deindent` local.init.addBlock( deindent`
var ${local.name}_updating = false; var ${local.name}_updating = false;
@ -149,7 +147,7 @@ export default function createBinding ( generator, node, attribute, current, loc
${local.name}_updating = false; ${local.name}_updating = false;
} }
addEventListener( ${local.name}, '${eventName}', ${handler} ); ${generator.helper( 'addEventListener' )}( ${local.name}, '${eventName}', ${handler} );
` ); ` );
node.initialUpdate = updateElement; node.initialUpdate = updateElement;
@ -159,7 +157,7 @@ export default function createBinding ( generator, node, attribute, current, loc
); );
generator.current.builders.teardown.addLine( deindent` generator.current.builders.teardown.addLine( deindent`
removeEventListener( ${local.name}, '${eventName}', ${handler} ); ${generator.helper( 'removeEventListener' )}( ${local.name}, '${eventName}', ${handler} );
` ); ` );
} }
} }

@ -44,7 +44,7 @@ describe( 'generate', () => {
try { try {
const source = fs.readFileSync( `test/generator/${dir}/main.html`, 'utf-8' ); const source = fs.readFileSync( `test/generator/${dir}/main.html`, 'utf-8' );
compiled = svelte.compile( source ); compiled = svelte.compile( source, compileOptions );
} catch ( err ) { } catch ( err ) {
if ( config.compileError ) { if ( config.compileError ) {
config.compileError( err ); config.compileError( err );

@ -0,0 +1,8 @@
export default {
html: `<span>got</span>`,
test ( assert, component ) {
assert.equal( component.get( 'foo' ), 'got' );
component.teardown();
}
};

@ -0,0 +1,3 @@
export default function get () {
return 'got';
}

@ -0,0 +1,13 @@
<span>{{foo}}</span>
<script>
import get from './get.js';
export default {
data () {
return {
foo: get()
};
}
};
</script>
Loading…
Cancel
Save