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:
commit
61ae2fd6d3
@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
103
src/generators/annotateWithScopes.js
Normal file
103
src/generators/annotateWithScopes.js
Normal 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 );
|
||||
}
|
||||
};
|
||||
|
@ -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 => {
|
||||
|
19
test/generator/function-in-expression/_config.js
Normal file
19
test/generator/function-in-expression/_config.js
Normal 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();
|
||||
}
|
||||
};
|
1
test/generator/function-in-expression/main.html
Normal file
1
test/generator/function-in-expression/main.html
Normal file
@ -0,0 +1 @@
|
||||
{{ numbers.filter( x => x % 2 ).join( ', ' ) }}
|
Loading…
Reference in New Issue
Block a user