refactor(compiler-cli): add `BananaInBoxCheck` to the template checks (#42984)

Add the implementation of a Template Check that ensures the correct
use of two-way binding syntax. Generates a warning when
'([foo])="bar"' is found instead of '[(foo)]="bar"'.

Refs #42966

PR Close #42984
This commit is contained in:
Daniel Trevino 2021-07-26 18:21:46 +00:00 committed by Andrew Kushnir
parent 9a0a2b6e75
commit d6411c2729
4 changed files with 89 additions and 1 deletions

View File

@ -34,6 +34,7 @@ export enum ErrorCode {
INJECTABLE_DUPLICATE_PROV = 9001,
INLINE_TCB_REQUIRED = 8900,
INLINE_TYPE_CTOR_REQUIRED = 8901,
INVALID_BANANA_IN_BOX = 8101,
MISSING_PIPE = 8004,
MISSING_REFERENCE_TARGET = 8003,
NGMODULE_DECLARATION_NOT_UNIQUE = 6007,
@ -67,7 +68,6 @@ export enum ErrorCode {
WRITE_TO_READ_ONLY_VARIABLE = 8005
}
// (No @packageDocumentation comment for this package)
```

View File

@ -172,6 +172,16 @@ export enum ErrorCode {
*/
SPLIT_TWO_WAY_BINDING = 8007,
/**
* A two way binding in a template has an incorrect syntax,
* parentheses outside brackets. For example:
*
* ```
* <div ([foo])="bar" />
* ```
*/
INVALID_BANANA_IN_BOX = 8101,
/**
* The template type-checking engine would need to generate an inline type check block for a
* component, but the current type-checking environment doesn't support it.

View File

@ -0,0 +1,13 @@
load("//tools:defaults.bzl", "ts_library")
ts_library(
name = "invalid_banana_in_box",
srcs = ["index.ts"],
visibility = ["//packages/compiler-cli/src/ngtsc/typecheck/extended:__subpackages__"],
deps = [
"//packages/compiler",
"//packages/compiler-cli/src/ngtsc/diagnostics",
"//packages/compiler-cli/src/ngtsc/typecheck/extended/api",
"@npm//typescript",
],
)

View File

@ -0,0 +1,65 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {TmplAstBoundEvent, TmplAstNode, TmplAstRecursiveVisitor} from '@angular/compiler';
import * as ts from 'typescript';
import {ErrorCode} from '../../../../../diagnostics';
import {TemplateCheck, TemplateContext, TemplateDiagnostic} from '../../../api/api';
/**
* Ensures the two-way binding syntax is correct.
* Parentheses should be inside the brackets "[()]".
* Will return diagnostic information when "([])" is found.
*/
export class InvalidBananaInBoxCheck implements TemplateCheck<ErrorCode.INVALID_BANANA_IN_BOX> {
code: ErrorCode.INVALID_BANANA_IN_BOX = 8101;
run(ctx: TemplateContext,
template: TmplAstNode[]): TemplateDiagnostic<ErrorCode.INVALID_BANANA_IN_BOX>[] {
const visitor = new BananaVisitor(ctx);
return visitor.getDiagnostics(template);
}
}
class BananaVisitor extends TmplAstRecursiveVisitor {
private diagnostics: ts.Diagnostic[] = [];
constructor(public readonly ctx: TemplateContext) {
super();
}
/**
* Check for outputs with names surrounded in brackets "[]".
* The syntax '([foo])="bar"' would be interpreted as an @Output()
* with name '[foo]'. Just like '(foo)="bar"' would have the name 'foo'.
* Generate diagnostic information for the cases found.
*/
override visitBoundEvent(boundEvent: TmplAstBoundEvent) {
const name = boundEvent.name;
if (name.startsWith('[') && name.endsWith(']')) {
const boundSyntax = boundEvent.sourceSpan.toString();
const expectedBoundSyntax = boundSyntax.replace(`(${name})`, `[(${name.slice(1, -1)})]`);
this.diagnostics.push(
this.ctx.templateTypeChecker.makeTemplateDiagnostic<ErrorCode.INVALID_BANANA_IN_BOX>(
this.ctx.component, boundEvent.sourceSpan, ts.DiagnosticCategory.Warning,
ErrorCode.INVALID_BANANA_IN_BOX,
`In the two-way binding syntax the parentheses should be inside the brackets, ex. '${
expectedBoundSyntax}'.
Find more at https://angular.io/guide/two-way-binding`));
}
}
getDiagnostics(template: TmplAstNode[]): ts.Diagnostic[] {
this.diagnostics = [];
for (const node of template) {
node.visit(this);
}
return this.diagnostics;
}
}