fix(compiler): give ASTWithSource its own visit method (#31347)
ASTWithSource contains more information that AST and should have its own visit method, if desired. This implements that. PR Close #31347
This commit is contained in:
parent
50c4ec6687
commit
6aaca21c27
|
@ -7,6 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AbsoluteSourceSpan, IdentifierKind} from '..';
|
import {AbsoluteSourceSpan, IdentifierKind} from '..';
|
||||||
|
import {runInEachFileSystem} from '../../file_system/testing';
|
||||||
import {getTemplateIdentifiers} from '../src/template';
|
import {getTemplateIdentifiers} from '../src/template';
|
||||||
import * as util from './util';
|
import * as util from './util';
|
||||||
|
|
||||||
|
@ -17,112 +18,114 @@ function bind(template: string) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('getTemplateIdentifiers', () => {
|
runInEachFileSystem(() => {
|
||||||
it('should generate nothing in HTML-only template', () => {
|
describe('getTemplateIdentifiers', () => {
|
||||||
const refs = getTemplateIdentifiers(bind('<div></div>'));
|
it('should generate nothing in HTML-only template', () => {
|
||||||
|
const refs = getTemplateIdentifiers(bind('<div></div>'));
|
||||||
|
|
||||||
expect(refs.size).toBe(0);
|
expect(refs.size).toBe(0);
|
||||||
});
|
|
||||||
|
|
||||||
it('should ignore comments', () => {
|
|
||||||
const refs = getTemplateIdentifiers(bind('<!-- {{comment}} -->'));
|
|
||||||
|
|
||||||
expect(refs.size).toBe(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle arbitrary whitespace', () => {
|
|
||||||
const template = '<div>\n\n {{foo}}</div>';
|
|
||||||
const refs = getTemplateIdentifiers(bind(template));
|
|
||||||
|
|
||||||
const [ref] = Array.from(refs);
|
|
||||||
expect(ref).toEqual({
|
|
||||||
name: 'foo',
|
|
||||||
kind: IdentifierKind.Property,
|
|
||||||
span: new AbsoluteSourceSpan(12, 15),
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('should ignore identifiers defined in the template', () => {
|
it('should ignore comments', () => {
|
||||||
const template = `
|
const refs = getTemplateIdentifiers(bind('<!-- {{comment}} -->'));
|
||||||
|
|
||||||
|
expect(refs.size).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle arbitrary whitespace', () => {
|
||||||
|
const template = '<div>\n\n {{foo}}</div>';
|
||||||
|
const refs = getTemplateIdentifiers(bind(template));
|
||||||
|
|
||||||
|
const [ref] = Array.from(refs);
|
||||||
|
expect(ref).toEqual({
|
||||||
|
name: 'foo',
|
||||||
|
kind: IdentifierKind.Property,
|
||||||
|
span: new AbsoluteSourceSpan(12, 15),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ignore identifiers defined in the template', () => {
|
||||||
|
const template = `
|
||||||
<input #model />
|
<input #model />
|
||||||
{{model.valid}}
|
{{model.valid}}
|
||||||
`;
|
`;
|
||||||
const refs = getTemplateIdentifiers(bind(template));
|
|
||||||
|
|
||||||
const refArr = Array.from(refs);
|
|
||||||
const modelId = refArr.find(ref => ref.name === 'model');
|
|
||||||
expect(modelId).toBeUndefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('generates identifiers for PropertyReads', () => {
|
|
||||||
it('should discover component properties', () => {
|
|
||||||
const template = '{{foo}}';
|
|
||||||
const refs = getTemplateIdentifiers(bind(template));
|
|
||||||
expect(refs.size).toBe(1);
|
|
||||||
|
|
||||||
const [ref] = Array.from(refs);
|
|
||||||
expect(ref).toEqual({
|
|
||||||
name: 'foo',
|
|
||||||
kind: IdentifierKind.Property,
|
|
||||||
span: new AbsoluteSourceSpan(2, 5),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should discover nested properties', () => {
|
|
||||||
const template = '<div><span>{{foo}}</span></div>';
|
|
||||||
const refs = getTemplateIdentifiers(bind(template));
|
const refs = getTemplateIdentifiers(bind(template));
|
||||||
|
|
||||||
const refArr = Array.from(refs);
|
const refArr = Array.from(refs);
|
||||||
expect(refArr).toEqual(jasmine.arrayContaining([{
|
const modelId = refArr.find(ref => ref.name === 'model');
|
||||||
name: 'foo',
|
expect(modelId).toBeUndefined();
|
||||||
kind: IdentifierKind.Property,
|
|
||||||
span: new AbsoluteSourceSpan(13, 16),
|
|
||||||
}]));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should ignore identifiers that are not implicitly received by the template', () => {
|
describe('generates identifiers for PropertyReads', () => {
|
||||||
const template = '{{foo.bar.baz}}';
|
it('should discover component properties', () => {
|
||||||
const refs = getTemplateIdentifiers(bind(template));
|
const template = '{{foo}}';
|
||||||
expect(refs.size).toBe(1);
|
const refs = getTemplateIdentifiers(bind(template));
|
||||||
|
expect(refs.size).toBe(1);
|
||||||
|
|
||||||
const [ref] = Array.from(refs);
|
const [ref] = Array.from(refs);
|
||||||
expect(ref.name).toBe('foo');
|
expect(ref).toEqual({
|
||||||
});
|
name: 'foo',
|
||||||
});
|
kind: IdentifierKind.Property,
|
||||||
|
span: new AbsoluteSourceSpan(2, 5),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('generates identifiers for MethodCalls', () => {
|
it('should discover nested properties', () => {
|
||||||
it('should discover component method calls', () => {
|
const template = '<div><span>{{foo}}</span></div>';
|
||||||
const template = '{{foo()}}';
|
const refs = getTemplateIdentifiers(bind(template));
|
||||||
const refs = getTemplateIdentifiers(bind(template));
|
|
||||||
expect(refs.size).toBe(1);
|
|
||||||
|
|
||||||
const [ref] = Array.from(refs);
|
const refArr = Array.from(refs);
|
||||||
expect(ref).toEqual({
|
expect(refArr).toEqual(jasmine.arrayContaining([{
|
||||||
name: 'foo',
|
name: 'foo',
|
||||||
kind: IdentifierKind.Method,
|
kind: IdentifierKind.Property,
|
||||||
span: new AbsoluteSourceSpan(2, 5),
|
span: new AbsoluteSourceSpan(13, 16),
|
||||||
|
}]));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ignore identifiers that are not implicitly received by the template', () => {
|
||||||
|
const template = '{{foo.bar.baz}}';
|
||||||
|
const refs = getTemplateIdentifiers(bind(template));
|
||||||
|
expect(refs.size).toBe(1);
|
||||||
|
|
||||||
|
const [ref] = Array.from(refs);
|
||||||
|
expect(ref.name).toBe('foo');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should discover nested properties', () => {
|
describe('generates identifiers for MethodCalls', () => {
|
||||||
const template = '<div><span>{{foo()}}</span></div>';
|
it('should discover component method calls', () => {
|
||||||
const refs = getTemplateIdentifiers(bind(template));
|
const template = '{{foo()}}';
|
||||||
|
const refs = getTemplateIdentifiers(bind(template));
|
||||||
|
expect(refs.size).toBe(1);
|
||||||
|
|
||||||
const refArr = Array.from(refs);
|
const [ref] = Array.from(refs);
|
||||||
expect(refArr).toEqual(jasmine.arrayContaining([{
|
expect(ref).toEqual({
|
||||||
name: 'foo',
|
name: 'foo',
|
||||||
kind: IdentifierKind.Method,
|
kind: IdentifierKind.Method,
|
||||||
span: new AbsoluteSourceSpan(13, 16),
|
span: new AbsoluteSourceSpan(2, 5),
|
||||||
}]));
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should ignore identifiers that are not implicitly received by the template', () => {
|
it('should discover nested properties', () => {
|
||||||
const template = '{{foo().bar().baz()}}';
|
const template = '<div><span>{{foo()}}</span></div>';
|
||||||
const refs = getTemplateIdentifiers(bind(template));
|
const refs = getTemplateIdentifiers(bind(template));
|
||||||
expect(refs.size).toBe(1);
|
|
||||||
|
|
||||||
const [ref] = Array.from(refs);
|
const refArr = Array.from(refs);
|
||||||
expect(ref.name).toBe('foo');
|
expect(refArr).toEqual(jasmine.arrayContaining([{
|
||||||
|
name: 'foo',
|
||||||
|
kind: IdentifierKind.Method,
|
||||||
|
span: new AbsoluteSourceSpan(13, 16),
|
||||||
|
}]));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ignore identifiers that are not implicitly received by the template', () => {
|
||||||
|
const template = '{{foo().bar().baz()}}';
|
||||||
|
const refs = getTemplateIdentifiers(bind(template));
|
||||||
|
expect(refs.size).toBe(1);
|
||||||
|
|
||||||
|
const [ref] = Array.from(refs);
|
||||||
|
expect(ref.name).toBe('foo');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -209,7 +209,12 @@ export class ASTWithSource extends AST {
|
||||||
public errors: ParserError[]) {
|
public errors: ParserError[]) {
|
||||||
super(new ParseSpan(0, source == null ? 0 : source.length));
|
super(new ParseSpan(0, source == null ? 0 : source.length));
|
||||||
}
|
}
|
||||||
visit(visitor: AstVisitor, context: any = null): any { return this.ast.visit(visitor, context); }
|
visit(visitor: AstVisitor, context: any = null): any {
|
||||||
|
if (visitor.visitASTWithSource) {
|
||||||
|
return visitor.visitASTWithSource(this, context);
|
||||||
|
}
|
||||||
|
return this.ast.visit(visitor, context);
|
||||||
|
}
|
||||||
toString(): string { return `${this.source} in ${this.location}`; }
|
toString(): string { return `${this.source} in ${this.location}`; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,6 +245,7 @@ export interface AstVisitor {
|
||||||
visitQuote(ast: Quote, context: any): any;
|
visitQuote(ast: Quote, context: any): any;
|
||||||
visitSafeMethodCall(ast: SafeMethodCall, context: any): any;
|
visitSafeMethodCall(ast: SafeMethodCall, context: any): any;
|
||||||
visitSafePropertyRead(ast: SafePropertyRead, context: any): any;
|
visitSafePropertyRead(ast: SafePropertyRead, context: any): any;
|
||||||
|
visitASTWithSource?(ast: ASTWithSource, context: any): any;
|
||||||
visit?(ast: AST, context?: any): any;
|
visit?(ast: AST, context?: any): any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue