refactor(template): remove supporter deprecated `var` / `#` (#11084)
BREAKING CHANGES: - `#` and `var` are not supported any more in expressions, use `let`, - `var-<name>` could not be used any more on templates, use `let-<name>`, - `var-<name>` could not be used any more to create a reference, use `ref-<name>`.
This commit is contained in:
parent
ce08982f78
commit
b867764b0d
|
@ -237,16 +237,11 @@ export class _ParseAST {
|
|||
|
||||
peekKeywordLet(): boolean { return this.next.isKeywordLet(); }
|
||||
|
||||
peekDeprecatedKeywordVar(): boolean { return this.next.isKeywordDeprecatedVar(); }
|
||||
|
||||
peekDeprecatedOperatorHash(): boolean { return this.next.isOperator('#'); }
|
||||
|
||||
expectCharacter(code: number) {
|
||||
if (this.optionalCharacter(code)) return;
|
||||
this.error(`Missing expected ${StringWrapper.fromCharCode(code)}`);
|
||||
}
|
||||
|
||||
|
||||
optionalOperator(op: string): boolean {
|
||||
if (this.next.isOperator(op)) {
|
||||
this.advance();
|
||||
|
@ -659,15 +654,7 @@ export class _ParseAST {
|
|||
let prefix: string = null;
|
||||
let warnings: string[] = [];
|
||||
while (this.index < this.tokens.length) {
|
||||
var keyIsVar: boolean = this.peekKeywordLet();
|
||||
if (!keyIsVar && this.peekDeprecatedKeywordVar()) {
|
||||
keyIsVar = true;
|
||||
warnings.push(`"var" inside of expressions is deprecated. Use "let" instead!`);
|
||||
}
|
||||
if (!keyIsVar && this.peekDeprecatedOperatorHash()) {
|
||||
keyIsVar = true;
|
||||
warnings.push(`"#" inside of expressions is deprecated. Use "let" instead!`);
|
||||
}
|
||||
const keyIsVar: boolean = this.peekKeywordLet();
|
||||
if (keyIsVar) {
|
||||
this.advance();
|
||||
}
|
||||
|
@ -688,12 +675,10 @@ export class _ParseAST {
|
|||
} else {
|
||||
name = '\$implicit';
|
||||
}
|
||||
} else if (
|
||||
this.next !== EOF && !this.peekKeywordLet() && !this.peekDeprecatedKeywordVar() &&
|
||||
!this.peekDeprecatedOperatorHash()) {
|
||||
} else if (this.next !== EOF && !this.peekKeywordLet()) {
|
||||
const start = this.inputIndex;
|
||||
var ast = this.parsePipe();
|
||||
var source = this.input.substring(start, this.inputIndex);
|
||||
const ast = this.parsePipe();
|
||||
const source = this.input.substring(start, this.inputIndex);
|
||||
expression = new ASTWithSource(ast, source, this.location, this.errors);
|
||||
}
|
||||
bindings.push(new TemplateBinding(key, keyIsVar, name, expression));
|
||||
|
@ -723,7 +708,7 @@ export class _ParseAST {
|
|||
// of the '(' begins an '(' <expr> ')' production). The recovery points of grouping symbols
|
||||
// must be conditional as they must be skipped if none of the calling productions are not
|
||||
// expecting the closing token else we will never make progress in the case of an
|
||||
// extrainious group closing symbol (such as a stray ')'). This is not the case for ';' because
|
||||
// extraneous group closing symbol (such as a stray ')'). This is not the case for ';' because
|
||||
// parseChain() is always the root production and it expects a ';'.
|
||||
|
||||
// If a production expects one of these token it increments the corresponding nesting count,
|
||||
|
|
|
@ -34,18 +34,28 @@ import {PreparsedElementType, preparseElement} from './template_preparser';
|
|||
|
||||
|
||||
// Group 1 = "bind-"
|
||||
// Group 2 = "var-"
|
||||
// Group 3 = "let-"
|
||||
// Group 4 = "ref-/#"
|
||||
// Group 5 = "on-"
|
||||
// Group 6 = "bindon-"
|
||||
// Group 7 = "@"
|
||||
// Group 8 = the identifier after "bind-", "var-/#", or "on-"
|
||||
// Group 9 = identifier inside [()]
|
||||
// Group 10 = identifier inside []
|
||||
// Group 11 = identifier inside ()
|
||||
// Group 2 = "let-"
|
||||
// Group 3 = "ref-/#"
|
||||
// Group 4 = "on-"
|
||||
// Group 5 = "bindon-"
|
||||
// Group 6 = "@"
|
||||
// Group 7 = the identifier after "bind-", "let-", "ref-/#", "on-", "bindon-" or "@"
|
||||
// Group 8 = identifier inside [()]
|
||||
// Group 9 = identifier inside []
|
||||
// Group 10 = identifier inside ()
|
||||
const BIND_NAME_REGEXP =
|
||||
/^(?:(?:(?:(bind-)|(var-)|(let-)|(ref-|#)|(on-)|(bindon-)|(@))(.+))|\[\(([^\)]+)\)\]|\[([^\]]+)\]|\(([^\)]+)\))$/;
|
||||
/^(?:(?:(?:(bind-)|(let-)|(ref-|#)|(on-)|(bindon-)|(@))(.+))|\[\(([^\)]+)\)\]|\[([^\]]+)\]|\(([^\)]+)\))$/;
|
||||
|
||||
const KW_BIND_IDX = 1;
|
||||
const KW_LET_IDX = 2;
|
||||
const KW_REF_IDX = 3;
|
||||
const KW_ON_IDX = 4;
|
||||
const KW_BINDON_IDX = 5;
|
||||
const KW_AT_IDX = 6;
|
||||
const IDENT_KW_IDX = 7;
|
||||
const IDENT_BANANA_BOX_IDX = 8;
|
||||
const IDENT_PROPERTY_IDX = 9;
|
||||
const IDENT_EVENT_IDX = 10;
|
||||
|
||||
const ANIMATE_PROP_PREFIX = 'animate-';
|
||||
const TEMPLATE_ELEMENT = 'template';
|
||||
|
@ -490,90 +500,82 @@ class TemplateParseVisitor implements html.Visitor {
|
|||
targetProps: BoundElementOrDirectiveProperty[],
|
||||
targetAnimationProps: BoundElementPropertyAst[], targetEvents: BoundEventAst[],
|
||||
targetRefs: ElementOrDirectiveRef[], targetVars: VariableAst[]): boolean {
|
||||
const attrName = this._normalizeAttributeName(attr.name);
|
||||
const attrValue = attr.value;
|
||||
const bindParts = attrName.match(BIND_NAME_REGEXP);
|
||||
const name = this._normalizeAttributeName(attr.name);
|
||||
const value = attr.value;
|
||||
const srcSpan = attr.sourceSpan;
|
||||
|
||||
const bindParts = name.match(BIND_NAME_REGEXP);
|
||||
let hasBinding = false;
|
||||
|
||||
if (bindParts !== null) {
|
||||
hasBinding = true;
|
||||
if (isPresent(bindParts[1])) { // match: bind-prop
|
||||
if (isPresent(bindParts[KW_BIND_IDX])) {
|
||||
this._parsePropertyOrAnimation(
|
||||
bindParts[8], attrValue, attr.sourceSpan, targetMatchableAttrs, targetProps,
|
||||
bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetProps,
|
||||
targetAnimationProps);
|
||||
|
||||
} else if (isPresent(bindParts[2])) { // match: var-name / var-name="iden"
|
||||
const identifier = bindParts[8];
|
||||
} else if (bindParts[KW_LET_IDX]) {
|
||||
if (isTemplateElement) {
|
||||
this._reportError(
|
||||
`"var-" on <template> elements is deprecated. Use "let-" instead!`, attr.sourceSpan,
|
||||
ParseErrorLevel.WARNING);
|
||||
this._parseVariable(identifier, attrValue, attr.sourceSpan, targetVars);
|
||||
const identifier = bindParts[IDENT_KW_IDX];
|
||||
this._parseVariable(identifier, value, srcSpan, targetVars);
|
||||
} else {
|
||||
this._reportError(
|
||||
`"var-" on non <template> elements is deprecated. Use "ref-" instead!`,
|
||||
attr.sourceSpan, ParseErrorLevel.WARNING);
|
||||
this._parseReference(identifier, attrValue, attr.sourceSpan, targetRefs);
|
||||
this._reportError(`"let-" is only supported on template elements.`, srcSpan);
|
||||
}
|
||||
|
||||
} else if (isPresent(bindParts[3])) { // match: let-name
|
||||
if (isTemplateElement) {
|
||||
const identifier = bindParts[8];
|
||||
this._parseVariable(identifier, attrValue, attr.sourceSpan, targetVars);
|
||||
} else {
|
||||
this._reportError(`"let-" is only supported on template elements.`, attr.sourceSpan);
|
||||
}
|
||||
} else if (bindParts[KW_REF_IDX]) {
|
||||
const identifier = bindParts[IDENT_KW_IDX];
|
||||
this._parseReference(identifier, value, srcSpan, targetRefs);
|
||||
|
||||
} else if (isPresent(bindParts[4])) { // match: ref- / #iden
|
||||
const identifier = bindParts[8];
|
||||
this._parseReference(identifier, attrValue, attr.sourceSpan, targetRefs);
|
||||
|
||||
} else if (isPresent(bindParts[5])) { // match: on-event
|
||||
} else if (bindParts[KW_ON_IDX]) {
|
||||
this._parseEvent(
|
||||
bindParts[8], attrValue, attr.sourceSpan, targetMatchableAttrs, targetEvents);
|
||||
bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
|
||||
|
||||
} else if (isPresent(bindParts[6])) { // match: bindon-prop
|
||||
} else if (bindParts[KW_BINDON_IDX]) {
|
||||
this._parsePropertyOrAnimation(
|
||||
bindParts[8], attrValue, attr.sourceSpan, targetMatchableAttrs, targetProps,
|
||||
bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetProps,
|
||||
targetAnimationProps);
|
||||
this._parseAssignmentEvent(
|
||||
bindParts[8], attrValue, attr.sourceSpan, targetMatchableAttrs, targetEvents);
|
||||
bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
|
||||
|
||||
} else if (isPresent(bindParts[7])) { // match: animate-name
|
||||
if (attrName[0] == '@' && isPresent(attrValue) && attrValue.length > 0) {
|
||||
} else if (bindParts[KW_AT_IDX]) {
|
||||
if (name[0] == '@' && isPresent(value) && value.length > 0) {
|
||||
this._reportError(
|
||||
`Assigning animation triggers via @prop="exp" attributes with an expression is invalid. Use property bindings (e.g. [@prop]="exp") or use an attribute without a value \(e.g. @prop\) instead.`,
|
||||
attr.sourceSpan, ParseErrorLevel.FATAL);
|
||||
`Assigning animation triggers via @prop="exp" attributes with an expression is invalid.` +
|
||||
` Use property bindings (e.g. [@prop]="exp") or use an attribute without a value (e.g. @prop) instead.`,
|
||||
srcSpan, ParseErrorLevel.FATAL);
|
||||
}
|
||||
this._parseAnimation(
|
||||
bindParts[8], attrValue, attr.sourceSpan, targetMatchableAttrs, targetAnimationProps);
|
||||
} else if (isPresent(bindParts[9])) { // match: [(expr)]
|
||||
bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetAnimationProps);
|
||||
} else if (bindParts[IDENT_BANANA_BOX_IDX]) {
|
||||
this._parsePropertyOrAnimation(
|
||||
bindParts[9], attrValue, attr.sourceSpan, targetMatchableAttrs, targetProps,
|
||||
bindParts[IDENT_BANANA_BOX_IDX], value, srcSpan, targetMatchableAttrs, targetProps,
|
||||
targetAnimationProps);
|
||||
this._parseAssignmentEvent(
|
||||
bindParts[9], attrValue, attr.sourceSpan, targetMatchableAttrs, targetEvents);
|
||||
bindParts[IDENT_BANANA_BOX_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
|
||||
|
||||
} else if (isPresent(bindParts[10])) { // match: [expr]
|
||||
} else if (bindParts[IDENT_PROPERTY_IDX]) {
|
||||
this._parsePropertyOrAnimation(
|
||||
bindParts[10], attrValue, attr.sourceSpan, targetMatchableAttrs, targetProps,
|
||||
bindParts[IDENT_PROPERTY_IDX], value, srcSpan, targetMatchableAttrs, targetProps,
|
||||
targetAnimationProps);
|
||||
|
||||
} else if (isPresent(bindParts[11])) { // match: (event)
|
||||
} else if (bindParts[IDENT_EVENT_IDX]) {
|
||||
this._parseEvent(
|
||||
bindParts[11], attrValue, attr.sourceSpan, targetMatchableAttrs, targetEvents);
|
||||
bindParts[IDENT_EVENT_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
|
||||
}
|
||||
} else {
|
||||
hasBinding = this._parsePropertyInterpolation(
|
||||
attrName, attrValue, attr.sourceSpan, targetMatchableAttrs, targetProps);
|
||||
hasBinding =
|
||||
this._parsePropertyInterpolation(name, value, srcSpan, targetMatchableAttrs, targetProps);
|
||||
}
|
||||
|
||||
if (!hasBinding) {
|
||||
this._parseLiteralAttr(attrName, attrValue, attr.sourceSpan, targetProps);
|
||||
this._parseLiteralAttr(name, value, srcSpan, targetProps);
|
||||
}
|
||||
|
||||
return hasBinding;
|
||||
}
|
||||
|
||||
private _normalizeAttributeName(attrName: string): string {
|
||||
return attrName.toLowerCase().startsWith('data-') ? attrName.substring(5) : attrName;
|
||||
return /^data-/i.test(attrName) ? attrName.substring(5) : attrName;
|
||||
}
|
||||
|
||||
private _parseVariable(
|
||||
|
|
|
@ -389,22 +389,6 @@ export function main() {
|
|||
expect(bindings[0].expression.location).toEqual('location');
|
||||
});
|
||||
|
||||
it('should support var notation with a deprecation warning', () => {
|
||||
var bindings = createParser().parseTemplateBindings('var i', null);
|
||||
expect(keyValues(bindings.templateBindings)).toEqual(['let i=\$implicit']);
|
||||
expect(bindings.warnings).toEqual([
|
||||
'"var" inside of expressions is deprecated. Use "let" instead!'
|
||||
]);
|
||||
});
|
||||
|
||||
it('should support # notation with a deprecation warning', () => {
|
||||
var bindings = createParser().parseTemplateBindings('#i', null);
|
||||
expect(keyValues(bindings.templateBindings)).toEqual(['let i=\$implicit']);
|
||||
expect(bindings.warnings).toEqual([
|
||||
'"#" inside of expressions is deprecated. Use "let" instead!'
|
||||
]);
|
||||
});
|
||||
|
||||
it('should support let notation', () => {
|
||||
var bindings = parseTemplateBindings('let i');
|
||||
expect(keyValues(bindings)).toEqual(['let i=\$implicit']);
|
||||
|
|
|
@ -838,15 +838,6 @@ Can't bind to 'invalidProp' since it isn't a known property of 'my-component'.
|
|||
]))).toEqual([[ElementAst, 'div'], [ReferenceAst, 'a', null]]);
|
||||
});
|
||||
|
||||
it('should parse references via var-... and report them as deprecated', () => {
|
||||
expect(humanizeTplAst(parse('<div var-a>', [
|
||||
]))).toEqual([[ElementAst, 'div'], [ReferenceAst, 'a', null]]);
|
||||
expect(console.warnings).toEqual([[
|
||||
'Template parse warnings:',
|
||||
'"var-" on non <template> elements is deprecated. Use "ref-" instead! ("<div [ERROR ->]var-a>"): TestComp@0:5'
|
||||
].join('\n')]);
|
||||
});
|
||||
|
||||
it('should parse camel case references', () => {
|
||||
expect(humanizeTplAst(parse('<div ref-someA>', [
|
||||
]))).toEqual([[ElementAst, 'div'], [ReferenceAst, 'someA', null]]);
|
||||
|
@ -960,15 +951,6 @@ Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>
|
|||
]))).toEqual([[EmbeddedTemplateAst], [VariableAst, 'a', 'b']]);
|
||||
});
|
||||
|
||||
it('should parse variables via var-... and report them as deprecated', () => {
|
||||
expect(humanizeTplAst(parse('<template var-a="b">', [
|
||||
]))).toEqual([[EmbeddedTemplateAst], [VariableAst, 'a', 'b']]);
|
||||
expect(console.warnings).toEqual([[
|
||||
'Template parse warnings:',
|
||||
'"var-" on <template> elements is deprecated. Use "let-" instead! ("<template [ERROR ->]var-a="b">"): TestComp@0:10'
|
||||
].join('\n')]);
|
||||
});
|
||||
|
||||
it('should not locate directives in variables', () => {
|
||||
var dirA = CompileDirectiveMetadata.create({
|
||||
selector: '[a]',
|
||||
|
@ -1000,22 +982,9 @@ Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>
|
|||
]);
|
||||
});
|
||||
|
||||
it('should parse variables via #... and report them as deprecated', () => {
|
||||
expect(humanizeTplAst(parse('<div *ngIf="#a=b">', [
|
||||
]))).toEqual([[EmbeddedTemplateAst], [VariableAst, 'a', 'b'], [ElementAst, 'div']]);
|
||||
expect(console.warnings).toEqual([[
|
||||
'Template parse warnings:',
|
||||
'"#" inside of expressions is deprecated. Use "let" instead! ("<div [ERROR ->]*ngIf="#a=b">"): TestComp@0:5'
|
||||
].join('\n')]);
|
||||
});
|
||||
|
||||
it('should parse variables via var ... and report them as deprecated', () => {
|
||||
expect(humanizeTplAst(parse('<div *ngIf="var a=b">', [
|
||||
]))).toEqual([[EmbeddedTemplateAst], [VariableAst, 'a', 'b'], [ElementAst, 'div']]);
|
||||
expect(console.warnings).toEqual([[
|
||||
'Template parse warnings:',
|
||||
'"var" inside of expressions is deprecated. Use "let" instead! ("<div [ERROR ->]*ngIf="var a=b">"): TestComp@0:5'
|
||||
].join('\n')]);
|
||||
it('should report an error on variables declared with #', () => {
|
||||
expect(() => humanizeTplAst(parse('<div *ngIf="#a=b">', [])))
|
||||
.toThrowError(/Parser Error: Unexpected token # at column 6/);
|
||||
});
|
||||
|
||||
it('should parse variables via let ...', () => {
|
||||
|
|
Loading…
Reference in New Issue