Skip to content

Commit 6ab17b3

Browse files
committed
refactor(schematics): cleanup remaining rules
* Fixes that the `method-check` rule does not throw if the type of a class instantiation could not be determined. * Cleans up the remaining rules. * Removes the `exportAs` rule for now. Since it's unused and also extremely brittle (search & replace).
1 parent c09da0b commit 6ab17b3

File tree

10 files changed

+176
-256
lines changed

10 files changed

+176
-256
lines changed

src/lib/schematics/update/material/data/export-as-names.ts

Lines changed: 0 additions & 18 deletions
This file was deleted.

src/lib/schematics/update/material/data/method-call-checks.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export const methodCallChecks = transformChanges<MaterialMethodCallData>([
4343
invalidArgCounts: [
4444
{
4545
count: 2,
46-
message: '"g{{default}}" is now required as a third argument'
46+
message: '"g{{defaults}}" is now required as a third argument'
4747
}
4848
]
4949
}

src/lib/schematics/update/rules/checkClassDeclarationMiscRule.ts

Lines changed: 0 additions & 44 deletions
This file was deleted.

src/lib/schematics/update/rules/checkMethodCallsRule.ts

Lines changed: 0 additions & 92 deletions
This file was deleted.

src/lib/schematics/update/rules/checkInheritanceRule.ts renamed to src/lib/schematics/update/rules/class-inheritance/classInheritanceCheckRule.ts

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
import {bold, green, red} from 'chalk';
1010
import {ProgramAwareRuleWalker, RuleFailure, Rules} from 'tslint';
1111
import * as ts from 'typescript';
12-
import {MaterialPropertyNameData, propertyNames} from '../material/data/property-names';
12+
import {MaterialPropertyNameData, propertyNames} from '../../material/data/property-names';
13+
import {determineBaseTypes} from '../../typescript/base-types';
1314

1415
/**
1516
* Map of classes that have been updated. Each class name maps to the according property change
@@ -34,7 +35,7 @@ export class Rule extends Rules.TypedRule {
3435
export class Walker extends ProgramAwareRuleWalker {
3536

3637
visitClassDeclaration(node: ts.ClassDeclaration) {
37-
const baseTypes = this._determineBaseTypes(node);
38+
const baseTypes = determineBaseTypes(node);
3839

3940
if (!baseTypes) {
4041
return;
@@ -44,24 +45,11 @@ export class Walker extends ProgramAwareRuleWalker {
4445
const data = changedClassesMap.get(typeName);
4546

4647
if (data) {
47-
this.addFailureAtNode(node,
48-
`Found class "${bold(node.name.text)}" which extends class ` +
49-
`"${bold(typeName)}". Please note that the base class property ` +
50-
`"${red(data.replace)}" has changed to "${green(data.replaceWith)}". ` +
51-
`You may need to update your class as well`);
48+
this.addFailureAtNode(node, `Found class "${bold(node.name.text)}" which extends class ` +
49+
`"${bold(typeName)}". Please note that the base class property ` +
50+
`"${red(data.replace)}" has changed to "${green(data.replaceWith)}". ` +
51+
`You may need to update your class as well`);
5252
}
5353
});
5454
}
55-
56-
private _determineBaseTypes(node: ts.ClassDeclaration): string[] | null {
57-
if (!node.heritageClauses) {
58-
return null;
59-
}
60-
61-
return node.heritageClauses
62-
.reduce((types, clause) => types.concat(clause.types), [])
63-
.map(typeExpression => typeExpression.expression)
64-
.filter(expression => expression && ts.isIdentifier(expression))
65-
.map(identifier => identifier.text);
66-
}
6755
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {bold, green} from 'chalk';
10+
import {ProgramAwareRuleWalker, RuleFailure, Rules} from 'tslint';
11+
import * as ts from 'typescript';
12+
import {determineBaseTypes} from '../../typescript/base-types';
13+
14+
/**
15+
* Rule that checks for classes that extend Angular Material or CDK classes which have
16+
* changed their API.
17+
*/
18+
export class Rule extends Rules.TypedRule {
19+
applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): RuleFailure[] {
20+
return this.applyWithWalker(new Walker(sourceFile, this.getOptions(), program));
21+
}
22+
}
23+
24+
export class Walker extends ProgramAwareRuleWalker {
25+
26+
visitClassDeclaration(node: ts.ClassDeclaration) {
27+
const baseTypes = determineBaseTypes(node);
28+
29+
if (!baseTypes) {
30+
return;
31+
}
32+
33+
if (baseTypes.includes('MatFormFieldControl')) {
34+
const hasFloatLabelMember = node.members
35+
.find(member => member.name && member.name.getText() === 'shouldFloatLabel');
36+
37+
if (!hasFloatLabelMember) {
38+
this.addFailureAtNode(node, `Found class "${bold(node.name.text)}" which extends ` +
39+
`"${bold('MatFormFieldControl')}". This class must define ` +
40+
`"${green('shouldLabelFloat')}" which is now a required property.`);
41+
}
42+
}
43+
}
44+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {bold} from 'chalk';
10+
import {ProgramAwareRuleWalker, RuleFailure, Rules} from 'tslint';
11+
import * as ts from 'typescript';
12+
import {color} from '../../material/color';
13+
import {methodCallChecks} from '../../material/data/method-call-checks';
14+
15+
/**
16+
* Rule that visits every TypeScript call expression or TypeScript new expression and checks
17+
* if the argument count is invalid and needs to be *manually* updated.
18+
*/
19+
export class Rule extends Rules.TypedRule {
20+
applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): RuleFailure[] {
21+
return this.applyWithWalker(new Walker(sourceFile, this.getOptions(), program));
22+
}
23+
}
24+
25+
export class Walker extends ProgramAwareRuleWalker {
26+
27+
visitNewExpression(expression: ts.NewExpression) {
28+
const classType = this.getTypeChecker().getTypeAtLocation(expression);
29+
30+
if (classType && classType.symbol) {
31+
this.checkConstructor(expression, classType.symbol.name);
32+
}
33+
}
34+
35+
visitCallExpression(node: ts.CallExpression) {
36+
if (node.expression.kind === ts.SyntaxKind.SuperKeyword) {
37+
const superClassType = this.getTypeChecker().getTypeAtLocation(node.expression);
38+
const superClassName = superClassType.symbol && superClassType.symbol.name;
39+
40+
if (superClassName) {
41+
this.checkConstructor(node, superClassName);
42+
}
43+
}
44+
45+
if (ts.isPropertyAccessExpression(node.expression)) {
46+
this._checkPropertyAccessMethodCall(node);
47+
}
48+
49+
return super.visitCallExpression(node);
50+
}
51+
52+
private _checkPropertyAccessMethodCall(node: ts.CallExpression) {
53+
const propertyAccess = node.expression as ts.PropertyAccessExpression;
54+
55+
if (!ts.isIdentifier(propertyAccess.name)) {
56+
return;
57+
}
58+
59+
const hostType = this.getTypeChecker().getTypeAtLocation(propertyAccess.expression);
60+
const hostTypeName = hostType.symbol && hostType.symbol.name;
61+
const methodName = propertyAccess.name.text;
62+
63+
if (!hostTypeName) {
64+
return;
65+
}
66+
67+
const failure = methodCallChecks
68+
.filter(data => data.method === methodName && data.className === hostTypeName)
69+
.map(data => data.invalidArgCounts.find(f => f.count === node.arguments.length))[0];
70+
71+
if (!failure) {
72+
return;
73+
}
74+
75+
this.addFailureAtNode(node, `Found call to "${bold(hostTypeName + '.' + methodName)}" ` +
76+
`with ${bold(`${failure.count}`)} arguments. Message: ${color(failure.message)}`);
77+
}
78+
79+
private checkConstructor(node: ts.NewExpression | ts.CallExpression, className: string) {
80+
const argumentsLength = node.arguments ? node.arguments.length : 0;
81+
const failure = methodCallChecks
82+
.filter(data => data.method === 'constructor' && data.className === className)
83+
.map(data => data.invalidArgCounts.find(f => f.count === argumentsLength))[0];
84+
85+
if (!failure) {
86+
return;
87+
}
88+
89+
this.addFailureAtNode(node, `Found "${bold(className)}" constructed with ` +
90+
`${bold(`${failure.count}`)} arguments. Message: ${color(failure.message)}`);
91+
}
92+
}

0 commit comments

Comments
 (0)