Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 2ffe2ea

Browse filesBrowse files
rafaelss95mgechev
authored andcommitted
fix(templates-no-negated-async): not reporting failures for some cases (#694)
1 parent 5a84041 commit 2ffe2ea
Copy full SHA for 2ffe2ea

2 files changed

+152-144Lines changed: 152 additions & 144 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/templatesNoNegatedAsyncRule.ts‎

Copy file name to clipboardExpand all lines: src/templatesNoNegatedAsyncRule.ts
+47-52Lines changed: 47 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,73 @@
1-
import * as Lint from 'tslint';
2-
import * as ts from 'typescript';
1+
import { IRuleMetadata, RuleFailure, Rules } from 'tslint';
2+
import { SourceFile } from 'typescript';
33
import { NgWalker } from './angular/ngWalker';
44
import { RecursiveAngularExpressionVisitor } from './angular/templates/recursiveAngularExpressionVisitor';
5-
import * as e from '@angular/compiler/src/expression_parser/ast';
6-
import * as ast from '@angular/compiler';
5+
import { Binary, PrefixNot } from '@angular/compiler/src/expression_parser/ast';
6+
import { AST, BindingPipe, LiteralPrimitive } from '@angular/compiler';
77

88
const unstrictEqualityOperator = '==';
99

10-
class TemplateToNgTemplateVisitor extends RecursiveAngularExpressionVisitor {
11-
visitBinary(expr: e.Binary, context: any): any {
12-
if (!this.isAsyncBinding(expr.left)) {
13-
return super.visitBinary(expr, context);
14-
}
15-
if (!(expr.right instanceof ast.LiteralPrimitive) || expr.right.value !== false || expr.operation !== unstrictEqualityOperator) {
16-
return super.visitBinary(expr, context);
17-
}
10+
const isAsyncBinding = (ast: AST): boolean => {
11+
return ast instanceof BindingPipe && ast.name === 'async';
12+
};
1813

19-
const {
20-
left: {
21-
span: { end: endLeftSpan }
22-
},
23-
right: {
24-
span: { start: startRightSpan }
25-
},
26-
span: { end: spanEnd, start: spanStart }
27-
} = expr;
28-
const operator = this.codeWithMap.code.slice(endLeftSpan, startRightSpan);
29-
const operatorStart = /^.*==/.exec(operator)![0].length - unstrictEqualityOperator.length;
14+
class TemplateToNgTemplateVisitor extends RecursiveAngularExpressionVisitor {
15+
visitBinary(ast: Binary, context: any): any {
16+
this.validateBinary(ast);
17+
super.visitBinary(ast, context);
18+
}
3019

31-
this.addFailureFromStartToEnd(spanStart, spanEnd, 'Async pipes must use strict equality `===` when comparing with `false`', [
32-
new Lint.Replacement(this.getSourcePosition(endLeftSpan) + operatorStart, unstrictEqualityOperator.length, '===')
33-
]);
34-
super.visitBinary(expr, context);
20+
visitPrefixNot(ast: PrefixNot, context: any): any {
21+
this.validatePrefixNot(ast);
22+
super.visitPrefixNot(ast, context);
3523
}
3624

37-
visitPrefixNot(expr: e.PrefixNot, context: any): any {
38-
if (!this.isAsyncBinding(expr.expression)) {
39-
return super.visitPrefixNot(expr, context);
25+
private validateBinary(ast: Binary): void {
26+
const { left, operation, right } = ast;
27+
28+
if (!isAsyncBinding(left) || !(right instanceof LiteralPrimitive) || right.value !== false || operation !== unstrictEqualityOperator) {
29+
return;
4030
}
41-
const {
42-
span: { end: spanEnd, start: spanStart }
43-
} = expr;
44-
const absoluteStart = this.getSourcePosition(spanStart);
4531

46-
// Angular includes the whitespace after an expression, we want to trim that
47-
const expressionSource = this.codeWithMap.code.slice(spanStart, spanEnd);
48-
const concreteWidth = spanEnd - spanStart - / *$/.exec(expressionSource)![0].length;
32+
this.generateFailure(ast, Rule.FAILURE_STRING_UNSTRICT_EQUALITY);
33+
}
34+
35+
private validatePrefixNot(ast: PrefixNot): void {
36+
const { expression } = ast;
37+
38+
if (!isAsyncBinding(expression)) {
39+
return;
40+
}
4941

50-
this.addFailureFromStartToEnd(spanStart, spanEnd, 'Async pipes can not be negated, use (observable | async) === false instead', [
51-
new Lint.Replacement(absoluteStart + concreteWidth, 1, ' === false '),
52-
new Lint.Replacement(absoluteStart, 1, '')
53-
]);
42+
this.generateFailure(ast, Rule.FAILURE_STRING_NEGATED_PIPE);
5443
}
5544

56-
protected isAsyncBinding(expr: ast.AST) {
57-
return expr instanceof ast.BindingPipe && expr.name === 'async';
45+
private generateFailure(ast: Binary | PrefixNot, errorMessage: string): void {
46+
const {
47+
span: { end: spanEnd, start: spanStart }
48+
} = ast;
49+
50+
this.addFailureFromStartToEnd(spanStart, spanEnd, errorMessage);
5851
}
5952
}
6053

61-
export class Rule extends Lint.Rules.AbstractRule {
62-
public static metadata: Lint.IRuleMetadata = {
63-
ruleName: 'templates-no-negated-async',
64-
type: 'functionality',
54+
export class Rule extends Rules.AbstractRule {
55+
static readonly metadata: IRuleMetadata = {
6556
description: 'Ensures that strict equality is used when evaluating negations on async pipe output.',
57+
options: null,
58+
optionsDescription: 'Not configurable.',
6659
rationale:
6760
'Async pipe evaluate to `null` before the observable or promise emits, which can lead to layout thrashing as' +
6861
' components load. Prefer strict `=== false` checks instead.',
69-
options: null,
70-
optionsDescription: 'Not configurable.',
71-
typescriptOnly: true,
72-
hasFix: true
62+
ruleName: 'templates-no-negated-async',
63+
type: 'functionality',
64+
typescriptOnly: true
7365
};
7466

75-
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
67+
static readonly FAILURE_STRING_NEGATED_PIPE = 'Async pipes can not be negated, use (observable | async) === false instead';
68+
static readonly FAILURE_STRING_UNSTRICT_EQUALITY = 'Async pipes must use strict equality `===` when comparing with `false`';
69+
70+
apply(sourceFile: SourceFile): RuleFailure[] {
7671
return this.applyWithWalker(
7772
new NgWalker(sourceFile, this.getOptions(), {
7873
expressionVisitorCtrl: TemplateToNgTemplateVisitor

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.