-
Notifications
You must be signed in to change notification settings - Fork 27.1k
feat(parser): allow users install custom AST transforms #5382
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -58,7 +58,6 @@ export class KeyedWrite extends AST { | |
|
|
||
| export class BindingPipe extends AST { | ||
| constructor(public exp: AST, public name: string, public args: any[]) { super(); } | ||
|
|
||
| visit(visitor: AstVisitor): any { return visitor.visitPipe(this); } | ||
| } | ||
|
|
||
|
|
@@ -79,7 +78,7 @@ export class LiteralMap extends AST { | |
|
|
||
| export class Interpolation extends AST { | ||
| constructor(public strings: any[], public expressions: any[]) { super(); } | ||
| visit(visitor: AstVisitor) { visitor.visitInterpolation(this); } | ||
| visit(visitor: AstVisitor): any { return visitor.visitInterpolation(this); } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why :any and not :AST?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not every AstVisitor is transforming the AST. You can imagine a visitor that all it does is print the AST to a buffer, or maybe converts it to a 3rd-party AST structure. In contrast, AstTransformer is specifically for transforming AST into another AST and so it is more tightly typed. |
||
| } | ||
|
|
||
| export class Binary extends AST { | ||
|
|
@@ -214,68 +213,66 @@ export class RecursiveAstVisitor implements AstVisitor { | |
| } | ||
|
|
||
| export class AstTransformer implements AstVisitor { | ||
| visitImplicitReceiver(ast: ImplicitReceiver): ImplicitReceiver { return ast; } | ||
| visitImplicitReceiver(ast: ImplicitReceiver): AST { return ast; } | ||
|
|
||
| visitInterpolation(ast: Interpolation): Interpolation { | ||
| visitInterpolation(ast: Interpolation): AST { | ||
| return new Interpolation(ast.strings, this.visitAll(ast.expressions)); | ||
| } | ||
|
|
||
| visitLiteralPrimitive(ast: LiteralPrimitive): LiteralPrimitive { | ||
| return new LiteralPrimitive(ast.value); | ||
| } | ||
| visitLiteralPrimitive(ast: LiteralPrimitive): AST { return new LiteralPrimitive(ast.value); } | ||
|
|
||
| visitPropertyRead(ast: PropertyRead): PropertyRead { | ||
| visitPropertyRead(ast: PropertyRead): AST { | ||
| return new PropertyRead(ast.receiver.visit(this), ast.name, ast.getter); | ||
| } | ||
|
|
||
| visitPropertyWrite(ast: PropertyWrite): PropertyWrite { | ||
| visitPropertyWrite(ast: PropertyWrite): AST { | ||
| return new PropertyWrite(ast.receiver.visit(this), ast.name, ast.setter, ast.value); | ||
| } | ||
|
|
||
| visitSafePropertyRead(ast: SafePropertyRead): SafePropertyRead { | ||
| visitSafePropertyRead(ast: SafePropertyRead): AST { | ||
| return new SafePropertyRead(ast.receiver.visit(this), ast.name, ast.getter); | ||
| } | ||
|
|
||
| visitMethodCall(ast: MethodCall): MethodCall { | ||
| visitMethodCall(ast: MethodCall): AST { | ||
| return new MethodCall(ast.receiver.visit(this), ast.name, ast.fn, this.visitAll(ast.args)); | ||
| } | ||
|
|
||
| visitSafeMethodCall(ast: SafeMethodCall): SafeMethodCall { | ||
| visitSafeMethodCall(ast: SafeMethodCall): AST { | ||
| return new SafeMethodCall(ast.receiver.visit(this), ast.name, ast.fn, this.visitAll(ast.args)); | ||
| } | ||
|
|
||
| visitFunctionCall(ast: FunctionCall): FunctionCall { | ||
| visitFunctionCall(ast: FunctionCall): AST { | ||
| return new FunctionCall(ast.target.visit(this), this.visitAll(ast.args)); | ||
| } | ||
|
|
||
| visitLiteralArray(ast: LiteralArray): LiteralArray { | ||
| visitLiteralArray(ast: LiteralArray): AST { | ||
| return new LiteralArray(this.visitAll(ast.expressions)); | ||
| } | ||
|
|
||
| visitLiteralMap(ast: LiteralMap): LiteralMap { | ||
| visitLiteralMap(ast: LiteralMap): AST { | ||
| return new LiteralMap(ast.keys, this.visitAll(ast.values)); | ||
| } | ||
|
|
||
| visitBinary(ast: Binary): Binary { | ||
| visitBinary(ast: Binary): AST { | ||
| return new Binary(ast.operation, ast.left.visit(this), ast.right.visit(this)); | ||
| } | ||
|
|
||
| visitPrefixNot(ast: PrefixNot): PrefixNot { return new PrefixNot(ast.expression.visit(this)); } | ||
| visitPrefixNot(ast: PrefixNot): AST { return new PrefixNot(ast.expression.visit(this)); } | ||
|
|
||
| visitConditional(ast: Conditional): Conditional { | ||
| visitConditional(ast: Conditional): AST { | ||
| return new Conditional(ast.condition.visit(this), ast.trueExp.visit(this), | ||
| ast.falseExp.visit(this)); | ||
| } | ||
|
|
||
| visitPipe(ast: BindingPipe): BindingPipe { | ||
| visitPipe(ast: BindingPipe): AST { | ||
| return new BindingPipe(ast.exp.visit(this), ast.name, this.visitAll(ast.args)); | ||
| } | ||
|
|
||
| visitKeyedRead(ast: KeyedRead): KeyedRead { | ||
| visitKeyedRead(ast: KeyedRead): AST { | ||
| return new KeyedRead(ast.obj.visit(this), ast.key.visit(this)); | ||
| } | ||
|
|
||
| visitKeyedWrite(ast: KeyedWrite): KeyedWrite { | ||
| visitKeyedWrite(ast: KeyedWrite): AST { | ||
| return new KeyedWrite(ast.obj.visit(this), ast.key.visit(this), ast.value.visit(this)); | ||
| } | ||
|
|
||
|
|
@@ -287,5 +284,5 @@ export class AstTransformer implements AstVisitor { | |
| return res; | ||
| } | ||
|
|
||
| visitChain(ast: Chain): Chain { return new Chain(this.visitAll(ast.expressions)); } | ||
| visitChain(ast: Chain): AST { return new Chain(this.visitAll(ast.expressions)); } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,7 +14,11 @@ import {provide} from 'angular2/src/core/di'; | |
|
|
||
| import {TEST_PROVIDERS} from './test_bindings'; | ||
| import {isPresent} from 'angular2/src/facade/lang'; | ||
| import {TemplateParser, splitClasses} from 'angular2/src/compiler/template_parser'; | ||
| import { | ||
| TemplateParser, | ||
| splitClasses, | ||
| TEMPLATE_TRANSFORMS | ||
| } from 'angular2/src/compiler/template_parser'; | ||
| import { | ||
| CompileDirectiveMetadata, | ||
| CompileTypeMetadata, | ||
|
|
@@ -69,6 +73,22 @@ export function main() { | |
| return parser.parse(template, directives, 'TestComp'); | ||
| } | ||
|
|
||
| describe('template transform', () => { | ||
| beforeEachProviders( | ||
| () => [provide(TEMPLATE_TRANSFORMS, {useValue: new FooAstTransformer(), multi: true})]); | ||
|
|
||
| it('should transform TemplateAST', | ||
| () => { expect(humanizeTplAst(parse('<div>', []))).toEqual([[ElementAst, 'foo']]); }); | ||
|
|
||
| describe('multiple', () => { | ||
| beforeEachProviders( | ||
| () => [provide(TEMPLATE_TRANSFORMS, {useValue: new BarAstTransformer(), multi: true})]); | ||
|
|
||
| it('should compose transformers', | ||
| () => { expect(humanizeTplAst(parse('<div>', []))).toEqual([[ElementAst, 'bar']]); }); | ||
| }); | ||
| }); | ||
|
|
||
| describe('parse', () => { | ||
| describe('nodes without bindings', () => { | ||
|
|
||
|
|
@@ -1068,3 +1088,29 @@ class TemplateContentProjectionHumanizer implements TemplateAstVisitor { | |
| visitDirective(ast: DirectiveAst, context: any): any { return null; } | ||
| visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; } | ||
| } | ||
|
|
||
| class FooAstTransformer implements TemplateAstVisitor { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe it's worth creating TemplateAstTransformer, similar to AstTransformer. The developer will probably only override a few methods in it.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I considered it but decided not to because it would be speculative dead code. I was going to let @btford create the appropriate common utilities (if any) while he's working on a real AST transformer for the router. |
||
| visitNgContent(ast: NgContentAst, context: any): any { throw 'not implemented'; } | ||
| visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any { throw 'not implemented'; } | ||
| visitElement(ast: ElementAst, context: any): any { | ||
| if (ast.name != 'div') return ast; | ||
| return new ElementAst('foo', [], [], [], [], [], [], ast.ngContentIndex, ast.sourceSpan); | ||
| } | ||
| visitVariable(ast: VariableAst, context: any): any { throw 'not implemented'; } | ||
| visitEvent(ast: BoundEventAst, context: any): any { throw 'not implemented'; } | ||
| visitElementProperty(ast: BoundElementPropertyAst, context: any): any { throw 'not implemented'; } | ||
| visitAttr(ast: AttrAst, context: any): any { throw 'not implemented'; } | ||
| visitBoundText(ast: BoundTextAst, context: any): any { throw 'not implemented'; } | ||
| visitText(ast: TextAst, context: any): any { throw 'not implemented'; } | ||
| visitDirective(ast: DirectiveAst, context: any): any { throw 'not implemented'; } | ||
| visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { | ||
| throw 'not implemented'; | ||
| } | ||
| } | ||
|
|
||
| class BarAstTransformer extends FooAstTransformer { | ||
| visitElement(ast: ElementAst, context: any): any { | ||
| if (ast.name != 'foo') return ast; | ||
| return new ElementAst('bar', [], [], [], [], [], [], ast.ngContentIndex, ast.sourceSpan); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OOC, is
Injectorneeded here ?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch: #5424