|
9 | 9 | import {bold, green, red} from 'chalk'; |
10 | 10 | import {ProgramAwareRuleWalker, RuleFailure, Rules} from 'tslint'; |
11 | 11 | import * as ts from 'typescript'; |
12 | | -import {propertyNames} from '../material/data/property-names'; |
| 12 | +import {MaterialPropertyNameData, propertyNames} from '../material/data/property-names'; |
13 | 13 |
|
14 | 14 | /** |
15 | | - * Rule that walks through every property access expression and updates properties that have |
16 | | - * been changed in favor of the new name. |
| 15 | + * Map of classes that have been updated. Each class name maps to the according property change |
| 16 | + * data. |
| 17 | + */ |
| 18 | +const changedClassesMap = new Map<string, MaterialPropertyNameData>(); |
| 19 | + |
| 20 | +propertyNames.filter(data => data.whitelist && data.whitelist.classes).forEach(data => { |
| 21 | + data.whitelist.classes.forEach(name => changedClassesMap.set(name, data)); |
| 22 | +}); |
| 23 | + |
| 24 | +/** |
| 25 | + * Rule that identifies class declarations that extend CDK or Material classes and had |
| 26 | + * a public property change. |
17 | 27 | */ |
18 | 28 | export class Rule extends Rules.TypedRule { |
19 | 29 | applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): RuleFailure[] { |
20 | | - return this.applyWithWalker( |
21 | | - new CheckInheritanceWalker(sourceFile, this.getOptions(), program)); |
| 30 | + return this.applyWithWalker(new Walker(sourceFile, this.getOptions(), program)); |
22 | 31 | } |
23 | 32 | } |
24 | 33 |
|
25 | | -export class CheckInheritanceWalker extends ProgramAwareRuleWalker { |
26 | | - visitClassDeclaration(declaration: ts.ClassDeclaration) { |
27 | | - // Check if user is extending an Angular Material class whose properties have changed. |
28 | | - const type = this.getTypeChecker().getTypeAtLocation(declaration.name); |
29 | | - const baseTypes = this.getTypeChecker().getBaseTypes(type as ts.InterfaceType); |
30 | | - baseTypes.forEach(t => { |
31 | | - const propertyData = propertyNames.find( |
32 | | - data => data.whitelist && new Set(data.whitelist.classes).has(t.symbol.name)); |
33 | | - if (propertyData) { |
34 | | - this.addFailureAtNode( |
35 | | - declaration, |
36 | | - `Found class "${bold(declaration.name.text)}" which extends class` + |
37 | | - ` "${bold(t.symbol.name)}". Please note that the base class property` + |
38 | | - ` "${red(propertyData.replace)}" has changed to "${green(propertyData.replaceWith)}".` + |
39 | | - ` You may need to update your class as well`); |
| 34 | +export class Walker extends ProgramAwareRuleWalker { |
| 35 | + |
| 36 | + visitClassDeclaration(node: ts.ClassDeclaration) { |
| 37 | + const baseTypes = this._determineBaseTypes(node); |
| 38 | + |
| 39 | + if (!baseTypes) { |
| 40 | + return; |
| 41 | + } |
| 42 | + |
| 43 | + baseTypes.forEach(typeName => { |
| 44 | + const data = changedClassesMap.get(typeName); |
| 45 | + |
| 46 | + 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`); |
40 | 52 | } |
41 | 53 | }); |
42 | 54 | } |
| 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 | + } |
43 | 67 | } |
0 commit comments