X Tutup
Skip to content

Commit a43ed79

Browse files
committed
feat(parser): allows users install custom AST transformers
Closes #5382
1 parent 125fa38 commit a43ed79

File tree

4 files changed

+80
-26
lines changed

4 files changed

+80
-26
lines changed

modules/angular2/src/compiler/template_parser.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import {
1313
assertionsEnabled,
1414
isBlank
1515
} from 'angular2/src/facade/lang';
16-
import {Injectable} from 'angular2/src/core/di';
16+
import {Injectable, Inject, Injector, OpaqueToken, Optional} from 'angular2/core';
17+
import {CONST_EXPR} from 'angular2/src/facade/lang';
1718
import {BaseException} from 'angular2/src/facade/exceptions';
1819
import {Parser, AST, ASTWithSource} from 'angular2/src/core/change_detection/change_detection';
1920
import {TemplateBinding} from 'angular2/src/core/change_detection/parser/ast';
@@ -28,6 +29,8 @@ import {
2829
BoundEventAst,
2930
VariableAst,
3031
TemplateAst,
32+
TemplateAstVisitor,
33+
templateVisitAll,
3134
TextAst,
3235
BoundTextAst,
3336
EmbeddedTemplateAst,
@@ -78,14 +81,17 @@ const STYLE_PREFIX = 'style';
7881

7982
var TEXT_CSS_SELECTOR = CssSelector.parse('*')[0];
8083

84+
export const TEMPLATE_TRANSFORMS = CONST_EXPR(new OpaqueToken('TemplateTransforms'));
85+
8186
export class TemplateParseError extends ParseError {
8287
constructor(message: string, location: ParseLocation) { super(location, message); }
8388
}
8489

8590
@Injectable()
8691
export class TemplateParser {
8792
constructor(private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry,
88-
private _htmlParser: HtmlParser) {}
93+
private _htmlParser: HtmlParser,
94+
@Optional() @Inject(TEMPLATE_TRANSFORMS) public transforms: TemplateAstVisitor[]) {}
8995

9096
parse(template: string, directives: CompileDirectiveMetadata[],
9197
templateUrl: string): TemplateAst[] {
@@ -97,6 +103,10 @@ export class TemplateParser {
97103
var errorString = errors.join('\n');
98104
throw new BaseException(`Template parse errors:\n${errorString}`);
99105
}
106+
if (isPresent(this.transforms)) {
107+
this.transforms.forEach(
108+
(transform: TemplateAstVisitor) => { result = templateVisitAll(transform, result); });
109+
}
100110
return result;
101111
}
102112
}

modules/angular2/src/core/change_detection/parser/ast.ts

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ export class KeyedWrite extends AST {
5858

5959
export class BindingPipe extends AST {
6060
constructor(public exp: AST, public name: string, public args: any[]) { super(); }
61-
6261
visit(visitor: AstVisitor): any { return visitor.visitPipe(this); }
6362
}
6463

@@ -79,7 +78,7 @@ export class LiteralMap extends AST {
7978

8079
export class Interpolation extends AST {
8180
constructor(public strings: any[], public expressions: any[]) { super(); }
82-
visit(visitor: AstVisitor) { visitor.visitInterpolation(this); }
81+
visit(visitor: AstVisitor): any { return visitor.visitInterpolation(this); }
8382
}
8483

8584
export class Binary extends AST {
@@ -214,68 +213,66 @@ export class RecursiveAstVisitor implements AstVisitor {
214213
}
215214

216215
export class AstTransformer implements AstVisitor {
217-
visitImplicitReceiver(ast: ImplicitReceiver): ImplicitReceiver { return ast; }
216+
visitImplicitReceiver(ast: ImplicitReceiver): AST { return ast; }
218217

219-
visitInterpolation(ast: Interpolation): Interpolation {
218+
visitInterpolation(ast: Interpolation): AST {
220219
return new Interpolation(ast.strings, this.visitAll(ast.expressions));
221220
}
222221

223-
visitLiteralPrimitive(ast: LiteralPrimitive): LiteralPrimitive {
224-
return new LiteralPrimitive(ast.value);
225-
}
222+
visitLiteralPrimitive(ast: LiteralPrimitive): AST { return new LiteralPrimitive(ast.value); }
226223

227-
visitPropertyRead(ast: PropertyRead): PropertyRead {
224+
visitPropertyRead(ast: PropertyRead): AST {
228225
return new PropertyRead(ast.receiver.visit(this), ast.name, ast.getter);
229226
}
230227

231-
visitPropertyWrite(ast: PropertyWrite): PropertyWrite {
228+
visitPropertyWrite(ast: PropertyWrite): AST {
232229
return new PropertyWrite(ast.receiver.visit(this), ast.name, ast.setter, ast.value);
233230
}
234231

235-
visitSafePropertyRead(ast: SafePropertyRead): SafePropertyRead {
232+
visitSafePropertyRead(ast: SafePropertyRead): AST {
236233
return new SafePropertyRead(ast.receiver.visit(this), ast.name, ast.getter);
237234
}
238235

239-
visitMethodCall(ast: MethodCall): MethodCall {
236+
visitMethodCall(ast: MethodCall): AST {
240237
return new MethodCall(ast.receiver.visit(this), ast.name, ast.fn, this.visitAll(ast.args));
241238
}
242239

243-
visitSafeMethodCall(ast: SafeMethodCall): SafeMethodCall {
240+
visitSafeMethodCall(ast: SafeMethodCall): AST {
244241
return new SafeMethodCall(ast.receiver.visit(this), ast.name, ast.fn, this.visitAll(ast.args));
245242
}
246243

247-
visitFunctionCall(ast: FunctionCall): FunctionCall {
244+
visitFunctionCall(ast: FunctionCall): AST {
248245
return new FunctionCall(ast.target.visit(this), this.visitAll(ast.args));
249246
}
250247

251-
visitLiteralArray(ast: LiteralArray): LiteralArray {
248+
visitLiteralArray(ast: LiteralArray): AST {
252249
return new LiteralArray(this.visitAll(ast.expressions));
253250
}
254251

255-
visitLiteralMap(ast: LiteralMap): LiteralMap {
252+
visitLiteralMap(ast: LiteralMap): AST {
256253
return new LiteralMap(ast.keys, this.visitAll(ast.values));
257254
}
258255

259-
visitBinary(ast: Binary): Binary {
256+
visitBinary(ast: Binary): AST {
260257
return new Binary(ast.operation, ast.left.visit(this), ast.right.visit(this));
261258
}
262259

263-
visitPrefixNot(ast: PrefixNot): PrefixNot { return new PrefixNot(ast.expression.visit(this)); }
260+
visitPrefixNot(ast: PrefixNot): AST { return new PrefixNot(ast.expression.visit(this)); }
264261

265-
visitConditional(ast: Conditional): Conditional {
262+
visitConditional(ast: Conditional): AST {
266263
return new Conditional(ast.condition.visit(this), ast.trueExp.visit(this),
267264
ast.falseExp.visit(this));
268265
}
269266

270-
visitPipe(ast: BindingPipe): BindingPipe {
267+
visitPipe(ast: BindingPipe): AST {
271268
return new BindingPipe(ast.exp.visit(this), ast.name, this.visitAll(ast.args));
272269
}
273270

274-
visitKeyedRead(ast: KeyedRead): KeyedRead {
271+
visitKeyedRead(ast: KeyedRead): AST {
275272
return new KeyedRead(ast.obj.visit(this), ast.key.visit(this));
276273
}
277274

278-
visitKeyedWrite(ast: KeyedWrite): KeyedWrite {
275+
visitKeyedWrite(ast: KeyedWrite): AST {
279276
return new KeyedWrite(ast.obj.visit(this), ast.key.visit(this), ast.value.visit(this));
280277
}
281278

@@ -287,5 +284,5 @@ export class AstTransformer implements AstVisitor {
287284
return res;
288285
}
289286

290-
visitChain(ast: Chain): Chain { return new Chain(this.visitAll(ast.expressions)); }
287+
visitChain(ast: Chain): AST { return new Chain(this.visitAll(ast.expressions)); }
291288
}

modules/angular2/test/compiler/template_parser_spec.ts

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ import {provide} from 'angular2/src/core/di';
1414

1515
import {TEST_PROVIDERS} from './test_bindings';
1616
import {isPresent} from 'angular2/src/facade/lang';
17-
import {TemplateParser, splitClasses} from 'angular2/src/compiler/template_parser';
17+
import {
18+
TemplateParser,
19+
splitClasses,
20+
TEMPLATE_TRANSFORMS
21+
} from 'angular2/src/compiler/template_parser';
1822
import {
1923
CompileDirectiveMetadata,
2024
CompileTypeMetadata,
@@ -69,6 +73,22 @@ export function main() {
6973
return parser.parse(template, directives, 'TestComp');
7074
}
7175

76+
describe('template transform', () => {
77+
beforeEachProviders(
78+
() => [provide(TEMPLATE_TRANSFORMS, {useValue: new FooAstTransformer(), multi: true})]);
79+
80+
it('should transform TemplateAST',
81+
() => { expect(humanizeTplAst(parse('<div>', []))).toEqual([[ElementAst, 'foo']]); });
82+
83+
describe('multiple', () => {
84+
beforeEachProviders(
85+
() => [provide(TEMPLATE_TRANSFORMS, {useValue: new BarAstTransformer(), multi: true})]);
86+
87+
it('should compose transformers',
88+
() => { expect(humanizeTplAst(parse('<div>', []))).toEqual([[ElementAst, 'bar']]); });
89+
});
90+
});
91+
7292
describe('parse', () => {
7393
describe('nodes without bindings', () => {
7494

@@ -1068,3 +1088,29 @@ class TemplateContentProjectionHumanizer implements TemplateAstVisitor {
10681088
visitDirective(ast: DirectiveAst, context: any): any { return null; }
10691089
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
10701090
}
1091+
1092+
class FooAstTransformer implements TemplateAstVisitor {
1093+
visitNgContent(ast: NgContentAst, context: any): any { throw 'not implemented'; }
1094+
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any { throw 'not implemented'; }
1095+
visitElement(ast: ElementAst, context: any): any {
1096+
if (ast.name != 'div') return ast;
1097+
return new ElementAst('foo', [], [], [], [], [], [], ast.ngContentIndex, ast.sourceSpan);
1098+
}
1099+
visitVariable(ast: VariableAst, context: any): any { throw 'not implemented'; }
1100+
visitEvent(ast: BoundEventAst, context: any): any { throw 'not implemented'; }
1101+
visitElementProperty(ast: BoundElementPropertyAst, context: any): any { throw 'not implemented'; }
1102+
visitAttr(ast: AttrAst, context: any): any { throw 'not implemented'; }
1103+
visitBoundText(ast: BoundTextAst, context: any): any { throw 'not implemented'; }
1104+
visitText(ast: TextAst, context: any): any { throw 'not implemented'; }
1105+
visitDirective(ast: DirectiveAst, context: any): any { throw 'not implemented'; }
1106+
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any {
1107+
throw 'not implemented';
1108+
}
1109+
}
1110+
1111+
class BarAstTransformer extends FooAstTransformer {
1112+
visitElement(ast: ElementAst, context: any): any {
1113+
if (ast.name != 'foo') return ast;
1114+
return new ElementAst('bar', [], [], [], [], [], [], ast.ngContentIndex, ast.sourceSpan);
1115+
}
1116+
}

modules_dart/transform/lib/src/transform/common/ng_compiler.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ TemplateCompiler createTemplateCompiler(AssetReader reader,
2222
var _htmlParser = new HtmlParser();
2323
var _urlResolver = const TransformerUrlResolver();
2424

25+
// TODO(yjbanov): add router AST transformer when ready
2526
var templateParser = new TemplateParser(new ng.Parser(new ng.Lexer()),
26-
new DomElementSchemaRegistry(), _htmlParser);
27+
new DomElementSchemaRegistry(), _htmlParser, null);
2728

2829
var cdCompiler = changeDetectionConfig != null
2930
? new ChangeDetectionCompiler(changeDetectionConfig)

0 commit comments

Comments
 (0)
X Tutup