refactor(compiler): add optional `visit()` to `TemplateAstVisitor` (#12209)
This commit is contained in:
parent
12ba62e5e2
commit
7275e1beb3
|
@ -10,7 +10,6 @@ import {SecurityContext} from '@angular/core';
|
|||
|
||||
import {CompileDirectiveMetadata, CompileProviderMetadata, CompileTokenMetadata} from '../compile_metadata';
|
||||
import {AST} from '../expression_parser/ast';
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {ParseSourceSpan} from '../parse_util';
|
||||
import {LifecycleHooks} from '../private_import_core';
|
||||
|
||||
|
@ -84,7 +83,7 @@ export class BoundEventAst implements TemplateAst {
|
|||
return visitor.visitEvent(this, context);
|
||||
}
|
||||
get fullName() {
|
||||
if (isPresent(this.target)) {
|
||||
if (this.target) {
|
||||
return `${this.target}:${this.name}`;
|
||||
} else {
|
||||
return this.name;
|
||||
|
@ -242,6 +241,11 @@ export enum PropertyBindingType {
|
|||
* A visitor for {@link TemplateAst} trees that will process each node.
|
||||
*/
|
||||
export interface TemplateAstVisitor {
|
||||
// Returning a truthy value from `visit()` will prevent `templateVisitAll()` from the call to
|
||||
// the typed method and result returned will become the result included in `visitAll()`s
|
||||
// result array.
|
||||
visit?(ast: TemplateAst, context: any): any;
|
||||
|
||||
visitNgContent(ast: NgContentAst, context: any): any;
|
||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any;
|
||||
visitElement(ast: ElementAst, context: any): any;
|
||||
|
@ -261,10 +265,13 @@ export interface TemplateAstVisitor {
|
|||
*/
|
||||
export function templateVisitAll(
|
||||
visitor: TemplateAstVisitor, asts: TemplateAst[], context: any = null): any[] {
|
||||
var result: any[] = [];
|
||||
const result: any[] = [];
|
||||
const visit = visitor.visit ?
|
||||
(ast: TemplateAst) => visitor.visit(ast, context) || ast.visit(visitor, context) :
|
||||
(ast: TemplateAst) => ast.visit(visitor, context);
|
||||
asts.forEach(ast => {
|
||||
var astResult = ast.visit(visitor, context);
|
||||
if (isPresent(astResult)) {
|
||||
const astResult = visit(ast);
|
||||
if (astResult) {
|
||||
result.push(astResult);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -71,6 +71,116 @@ export function main() {
|
|||
}));
|
||||
}
|
||||
|
||||
describe('TemplateAstVisitor', () => {
|
||||
function expectVisitedNode(visitor: TemplateAstVisitor, node: TemplateAst) {
|
||||
expect(node.visit(visitor, null)).toEqual(node);
|
||||
}
|
||||
|
||||
it('should visit NgContentAst', () => {
|
||||
expectVisitedNode(
|
||||
new class extends
|
||||
NullVisitor{visitNgContent(ast: NgContentAst, context: any): any{return ast;}},
|
||||
new NgContentAst(0, 0, null));
|
||||
});
|
||||
|
||||
it('should visit EmbeddedTemplateAst', () => {
|
||||
expectVisitedNode(
|
||||
new class extends NullVisitor{
|
||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any) { return ast; }
|
||||
},
|
||||
new EmbeddedTemplateAst([], [], [], [], [], [], false, [], 0, null));
|
||||
});
|
||||
|
||||
it('should visit ElementAst', () => {
|
||||
expectVisitedNode(
|
||||
new class extends
|
||||
NullVisitor{visitElement(ast: ElementAst, context: any) { return ast; }},
|
||||
new ElementAst('foo', [], [], [], [], [], [], false, [], 0, null, null));
|
||||
});
|
||||
|
||||
it('should visit RefererenceAst', () => {
|
||||
expectVisitedNode(
|
||||
new class extends
|
||||
NullVisitor{visitReference(ast: ReferenceAst, context: any): any{return ast;}},
|
||||
new ReferenceAst('foo', null, null));
|
||||
});
|
||||
|
||||
it('should visit VariableAst', () => {
|
||||
expectVisitedNode(
|
||||
new class extends
|
||||
NullVisitor{visitVariable(ast: VariableAst, context: any): any{return ast;}},
|
||||
new VariableAst('foo', 'bar', null));
|
||||
});
|
||||
|
||||
it('should visit BoundEventAst', () => {
|
||||
expectVisitedNode(
|
||||
new class extends
|
||||
NullVisitor{visitEvent(ast: BoundEventAst, context: any): any{return ast;}},
|
||||
new BoundEventAst('foo', 'bar', 'goo', null, null));
|
||||
});
|
||||
|
||||
it('should visit BoundElementPropertyAst', () => {
|
||||
expectVisitedNode(
|
||||
new class extends NullVisitor{
|
||||
visitElementProperty(ast: BoundElementPropertyAst, context: any): any{return ast;}
|
||||
},
|
||||
new BoundElementPropertyAst('foo', null, null, null, 'bar', null));
|
||||
});
|
||||
|
||||
it('should visit AttrAst', () => {
|
||||
expectVisitedNode(
|
||||
new class extends NullVisitor{visitAttr(ast: AttrAst, context: any): any{return ast;}},
|
||||
new AttrAst('foo', 'bar', null));
|
||||
});
|
||||
|
||||
it('should visit BoundTextAst', () => {
|
||||
expectVisitedNode(
|
||||
new class extends
|
||||
NullVisitor{visitBoundText(ast: BoundTextAst, context: any): any{return ast;}},
|
||||
new BoundTextAst(null, 0, null));
|
||||
});
|
||||
|
||||
it('should visit TextAst', () => {
|
||||
expectVisitedNode(
|
||||
new class extends NullVisitor{visitText(ast: TextAst, context: any): any{return ast;}},
|
||||
new TextAst('foo', 0, null));
|
||||
});
|
||||
|
||||
it('should visit DirectiveAst', () => {
|
||||
expectVisitedNode(
|
||||
new class extends
|
||||
NullVisitor{visitDirective(ast: DirectiveAst, context: any): any{return ast;}},
|
||||
new DirectiveAst(null, [], [], [], null));
|
||||
});
|
||||
|
||||
it('should visit DirectiveAst', () => {
|
||||
expectVisitedNode(
|
||||
new class extends NullVisitor{
|
||||
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any{return ast;}
|
||||
},
|
||||
new BoundDirectivePropertyAst('foo', 'bar', null, null));
|
||||
});
|
||||
|
||||
it('should skip the typed call of a visitor if visit() returns a truthy value', () => {
|
||||
const visitor = new class extends ThrowingVisitor {
|
||||
visit(ast: TemplateAst, context: any): any { return true; }
|
||||
};
|
||||
const nodes: TemplateAst[] = [
|
||||
new NgContentAst(0, 0, null),
|
||||
new EmbeddedTemplateAst([], [], [], [], [], [], false, [], 0, null),
|
||||
new ElementAst('foo', [], [], [], [], [], [], false, [], 0, null, null),
|
||||
new ReferenceAst('foo', null, null), new VariableAst('foo', 'bar', null),
|
||||
new BoundEventAst('foo', 'bar', 'goo', null, null),
|
||||
new BoundElementPropertyAst('foo', null, null, null, 'bar', null),
|
||||
new AttrAst('foo', 'bar', null), new BoundTextAst(null, 0, null),
|
||||
new TextAst('foo', 0, null), new DirectiveAst(null, [], [], [], null),
|
||||
new BoundDirectivePropertyAst('foo', 'bar', null, null)
|
||||
];
|
||||
const result = templateVisitAll(visitor, nodes, null);
|
||||
expect(result).toEqual(new Array(nodes.length).fill(true));
|
||||
});
|
||||
});
|
||||
|
||||
describe('TemplateParser template transform', () => {
|
||||
beforeEach(() => { TestBed.configureCompiler({providers: TEST_COMPILER_PROVIDERS}); });
|
||||
|
||||
|
@ -1848,15 +1958,10 @@ class TemplateContentProjectionHumanizer implements TemplateAstVisitor {
|
|||
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
|
||||
}
|
||||
|
||||
class FooAstTransformer implements TemplateAstVisitor {
|
||||
class ThrowingVisitor implements TemplateAstVisitor {
|
||||
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', [], [], [], [], [], [], false, [], ast.ngContentIndex, ast.sourceSpan,
|
||||
ast.endSourceSpan);
|
||||
}
|
||||
visitElement(ast: ElementAst, context: any): any { throw 'not implemented'; }
|
||||
visitReference(ast: ReferenceAst, context: any): any { throw 'not implemented'; }
|
||||
visitVariable(ast: VariableAst, context: any): any { throw 'not implemented'; }
|
||||
visitEvent(ast: BoundEventAst, context: any): any { throw 'not implemented'; }
|
||||
|
@ -1870,6 +1975,15 @@ class FooAstTransformer implements TemplateAstVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
class FooAstTransformer extends ThrowingVisitor {
|
||||
visitElement(ast: ElementAst, context: any): any {
|
||||
if (ast.name != 'div') return ast;
|
||||
return new ElementAst(
|
||||
'foo', [], [], [], [], [], [], false, [], ast.ngContentIndex, ast.sourceSpan,
|
||||
ast.endSourceSpan);
|
||||
}
|
||||
}
|
||||
|
||||
class BarAstTransformer extends FooAstTransformer {
|
||||
visitElement(ast: ElementAst, context: any): any {
|
||||
if (ast.name != 'foo') return ast;
|
||||
|
@ -1879,6 +1993,21 @@ class BarAstTransformer extends FooAstTransformer {
|
|||
}
|
||||
}
|
||||
|
||||
class NullVisitor implements TemplateAstVisitor {
|
||||
visitNgContent(ast: NgContentAst, context: any): any {}
|
||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {}
|
||||
visitElement(ast: ElementAst, context: any): any {}
|
||||
visitReference(ast: ReferenceAst, context: any): any {}
|
||||
visitVariable(ast: VariableAst, context: any): any {}
|
||||
visitEvent(ast: BoundEventAst, context: any): any {}
|
||||
visitElementProperty(ast: BoundElementPropertyAst, context: any): any {}
|
||||
visitAttr(ast: AttrAst, context: any): any {}
|
||||
visitBoundText(ast: BoundTextAst, context: any): any {}
|
||||
visitText(ast: TextAst, context: any): any {}
|
||||
visitDirective(ast: DirectiveAst, context: any): any {}
|
||||
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any {}
|
||||
}
|
||||
|
||||
class ArrayConsole implements Console {
|
||||
logs: string[] = [];
|
||||
warnings: string[] = [];
|
||||
|
|
Loading…
Reference in New Issue