0
0
mirror of https://github.com/sveltejs/svelte.git synced 2024-12-01 17:30:59 +01:00

Merge pull request #329 from sveltejs/gh-269

allow [arrow] function expressions inside tags
This commit is contained in:
Rich Harris 2017-03-02 07:38:29 -05:00 committed by GitHub
commit 61ae2fd6d3
5 changed files with 146 additions and 7 deletions

View File

@ -6,6 +6,7 @@ import flattenReference from '../utils/flattenReference.js';
import globalWhitelist from '../utils/globalWhitelist.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, names, visitors ) {
@ -49,10 +50,18 @@ export default class Generator {
const { code, helpers } = this;
const { contextDependencies, contexts, indexes } = this.current;
let scope = annotateWithScopes( expression );
walk( expression, {
enter ( node, parent, key ) {
if ( node._scope ) {
scope = node._scope;
return;
}
if ( isReference( node, parent ) ) {
const { name } = flattenReference( node );
if ( scope.has( name ) ) return;
if ( parent && parent.type === 'CallExpression' && node === parent.callee && helpers[ name ] ) {
code.prependRight( node.start, `template.helpers.` );
@ -100,6 +109,10 @@ export default class Generator {
this.skip();
}
},
leave ( node ) {
if ( node._scope ) scope = scope.parent;
}
});
@ -347,3 +360,4 @@ export default class Generator {
if ( visitor.leave ) visitor.leave( this, node );
}
}

View File

@ -0,0 +1,103 @@
import { walk } from 'estree-walker';
export default function annotateWithScopes ( expression ) {
let scope = new Scope( null, false );
walk( expression, {
enter ( node ) {
if ( /Function/.test( node.type ) ) {
if ( node.type === 'FunctionDeclaration' ) {
scope.declarations[ node.id.name ] = true;
} else {
node._scope = scope = new Scope( scope, false );
if ( node.id ) scope.declarations[ node.id.name ] = true;
}
node.params.forEach( param => {
extractNames( param ).forEach( name => {
scope.declarations[ name ] = true;
});
});
}
else if ( /For(?:In|Of)Statement/.test( node.type ) ) {
node._scope = scope = new Scope( scope, true );
}
else if ( node.type === 'BlockStatement' ) {
node._scope = scope = new Scope( scope, true );
}
else if ( /Declaration/.test( node.type ) ) {
scope.addDeclaration( node );
}
},
leave ( node ) {
if ( node._scope ) {
scope = scope.parent;
}
}
});
return scope;
}
class Scope {
constructor ( parent, block ) {
this.parent = parent;
this.block = block;
this.declarations = Object.create( null );
}
addDeclaration ( node ) {
if ( node.kind === 'var' && !this.block && this.parent ) {
this.parent.addDeclaration( node );
} else if ( node.type === 'VariableDeclaration' ) {
node.declarators.forEach( declarator => {
extractNames( declarator.id ).forEach( name => {
this.declarations[ name ] = true;
});
});
} else {
this.declarations[ node.id.name ] = true;
}
}
has ( name ) {
return name in this.declarations || this.parent && this.parent.has( name );
}
}
function extractNames ( param ) {
const names = [];
extractors[ param.type ]( names, param );
return names;
}
const extractors = {
Identifier ( names, param ) {
names.push( param.name );
},
ObjectPattern ( names, param ) {
param.properties.forEach( prop => {
extractors[ prop.value.type ]( names, prop.value );
});
},
ArrayPattern ( names, param ) {
param.elements.forEach( element => {
if ( element ) extractors[ element.type ]( names, element );
});
},
RestElement ( names, param ) {
extractors[ param.argument.type ]( names, param.argument );
},
AssignmentPattern ( names, param ) {
extractors[ param.left.type ]( names, param.left );
}
};

View File

@ -58,13 +58,15 @@ describe( 'generate', () => {
const { code } = compiled;
// check that no ES2015+ syntax slipped in
try {
const startIndex = code.indexOf( 'function renderMainFragment' ); // may change!
const es5 = spaces( startIndex ) + code.slice( startIndex ).replace( /export default .+/, '' );
acorn.parse( es5, { ecmaVersion: 5 });
} catch ( err ) {
if ( !config.show ) console.log( addLineNumbers( code ) ); // eslint-disable-line no-console
throw err;
if ( !config.allowES2015 ) {
try {
const startIndex = code.indexOf( 'function renderMainFragment' ); // may change!
const es5 = spaces( startIndex ) + code.slice( startIndex ).replace( /export default .+/, '' );
acorn.parse( es5, { ecmaVersion: 5 });
} catch ( err ) {
if ( !config.show ) console.log( addLineNumbers( code ) ); // eslint-disable-line no-console
throw err;
}
}
Object.keys( require.cache ).filter( x => x.endsWith( '.html' ) ).forEach( file => {

View File

@ -0,0 +1,19 @@
export default {
allowES2015: true,
data: {
numbers: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
},
html: '1, 3, 5, 7, 9',
test ( assert, component, target ) {
component.set({
numbers: [ 10, 11, 12, 13, 14, 15, 16 ]
});
assert.htmlEqual( target.innerHTML, `11, 13, 15` );
component.destroy();
}
};

View File

@ -0,0 +1 @@
{{ numbers.filter( x => x % 2 ).join( ', ' ) }}