Skip to content

Commit f6cded8

Browse files
devversionjelbourn
authored andcommitted
refactor(schematics): cleanup property name rules (#12820)
* Cleans up the rules for the property names. (e.g. properly determines the host type) * Adds a test case for the property name update rule. * Removes unused function
1 parent 920f32e commit f6cded8

File tree

9 files changed

+300
-148
lines changed

9 files changed

+300
-148
lines changed

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

Lines changed: 0 additions & 60 deletions
This file was deleted.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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 {green, red} from 'chalk';
10+
import {ProgramAwareRuleWalker, RuleFailure, Rules} from 'tslint';
11+
import * as ts from 'typescript';
12+
import {propertyNames} from '../../material/data/property-names';
13+
14+
/**
15+
* Rule that walks through every property access expression and updates properties that have
16+
* been changed in favor of a new name.
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+
visitPropertyAccessExpression(node: ts.PropertyAccessExpression) {
27+
const hostType = this.getTypeChecker().getTypeAtLocation(node.expression);
28+
const typeName = hostType && hostType.symbol && hostType.symbol.getName();
29+
30+
propertyNames.forEach(data => {
31+
if (node.name.text !== data.replace) {
32+
return;
33+
}
34+
35+
if (!data.whitelist || data.whitelist.classes.includes(typeName)) {
36+
const replacement = this.createReplacement(node.name.getStart(),
37+
node.name.getWidth(), data.replaceWith);
38+
this.addFailureAtNode(node.name, `Found deprecated property ${red(data.replace)} which ` +
39+
`has been renamed to "${green(data.replaceWith)}"`, replacement);
40+
}
41+
});
42+
43+
super.visitPropertyAccessExpression(node);
44+
}
45+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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, red} from 'chalk';
10+
import {ProgramAwareRuleWalker, RuleFailure, Rules} from 'tslint';
11+
import * as ts from 'typescript';
12+
13+
/**
14+
* Rule that walks through every property access expression and and reports to TSLint if
15+
* a given property name is no longer existing but cannot be automatically migrated.
16+
*/
17+
export class Rule extends Rules.TypedRule {
18+
applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): RuleFailure[] {
19+
return this.applyWithWalker(new Walker(sourceFile, this.getOptions(), program));
20+
}
21+
}
22+
23+
export class Walker extends ProgramAwareRuleWalker {
24+
25+
visitPropertyAccessExpression(node: ts.PropertyAccessExpression) {
26+
const hostType = this.getTypeChecker().getTypeAtLocation(node.expression);
27+
const typeName = hostType && hostType.symbol && hostType.symbol.getName();
28+
29+
if (typeName === 'MatListOption' && node.name.text === 'selectionChange') {
30+
this.addFailureAtNode(node, `Found deprecated property "${red('selectionChange')}" of ` +
31+
`class "${bold('MatListOption')}". Use the "${green('selectionChange')}" property on ` +
32+
`the parent "${bold('MatSelectionList')}" instead.`);
33+
}
34+
35+
if (typeName === 'MatDatepicker' && node.name.text === 'selectedChanged') {
36+
this.addFailureAtNode(node, `Found deprecated property "${red('selectedChanged')}" of ` +
37+
`class "${bold('MatDatepicker')}". Use the "${green('dateChange')}" or ` +
38+
`"${green('dateInput')}" methods on "${bold('MatDatepickerInput')}" instead`);
39+
}
40+
41+
super.visitPropertyAccessExpression(node);
42+
}
43+
}

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

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

src/lib/schematics/update/test-cases/index.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ describe('test cases', () => {
2121
'v5/element-selectors',
2222
'v5/input-names',
2323
'v5/output-names',
24+
'v5/property-names',
2425
];
2526

2627
// Iterates through every test case directory and generates a jasmine test block that will
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
* Fake definitions because the property name rules can only determine the host type
3+
* properly by using type checking.
4+
*/
5+
6+
class CdkConnectedOverlay {
7+
_deprecatedOrigin: any;
8+
_deprecatedPositions: any;
9+
_deprecatedOffsetX: any;
10+
_deprecatedOffsetY: any;
11+
_deprecatedWidth: any;
12+
_deprecatedHeight: any;
13+
_deprecatedMinWidth: any;
14+
_deprecatedMinHeight: any;
15+
_deprecatedBackdropClass: any;
16+
_deprecatedScrollStrategy: any;
17+
_deprecatedOpen: any;
18+
_deprecatedHasBackdrop: any;
19+
}
20+
21+
class MatSelect {
22+
change: any;
23+
onOpen: any;
24+
onClose: any;
25+
}
26+
27+
class MatRadioGroup {
28+
align: any;
29+
}
30+
31+
class MatSnackBarConfig {
32+
extraClasses: any;
33+
}
34+
35+
class CdkPortalOutlet {
36+
_deprecatedPortal: any;
37+
_deprecatedPortalHost: any;
38+
}
39+
40+
class MatDrawer {
41+
align: any;
42+
onAlignChanged: any;
43+
onOpen: any;
44+
onClose: any;
45+
}
46+
47+
/* Actual test case using the previously defined definitions. */
48+
49+
class A {
50+
self = {me: this};
51+
52+
constructor(private a: CdkConnectedOverlay) {}
53+
54+
onAngularClick() {
55+
this.a.origin = '1';
56+
this.a.positions = '2';
57+
this.a.offsetX = '3';
58+
this.a.offsetY = '4';
59+
this.a.height = '5';
60+
61+
console.log(this.a.width || 10);
62+
console.log(this.a.minWidth || this.a.minHeight);
63+
64+
this.self.me.a.backdropClass = ['a', 'b', 'c'];
65+
66+
const x = ({test: true} || this.a);
67+
68+
if (this.isConnectedOverlay(x)) {
69+
x.scrollStrategy = 'myScrollStrategy';
70+
x.open = false;
71+
x.hasBackdrop = true;
72+
}
73+
}
74+
75+
isConnectedOverlay(val: any): val is CdkConnectedOverlay {
76+
return val instanceof CdkConnectedOverlay;
77+
}
78+
}
79+
80+
class B {
81+
self = {me: this};
82+
b: MatRadioGroup;
83+
84+
constructor(private a: MatSelect,
85+
public c: MatSnackBarConfig,
86+
protected d: CdkPortalOutlet,
87+
private e: MatDrawer) {}
88+
89+
onClick() {
90+
this.a.selectionChange.subscribe(() => console.log('On Change'));
91+
this.a.openedChange.pipe(filter(isOpen => isOpen)).subscribe(() => console.log('On Open'));
92+
this.a.openedChange.pipe(filter(isOpen => !isOpen)).subscribe(() => console.log('On Close'));
93+
94+
this.b.labelPosition = 'end';
95+
this.c.panelClass = ['x', 'y', 'z'];
96+
this.d.portal = this.d.portal = 'myNewPortal';
97+
98+
this.e.position = 'end';
99+
this.e.onPositionChanged.subscribe(() => console.log('Align Changed'));
100+
this.e.openedChange.pipe(filter(isOpen => isOpen)).subscribe(() => console.log('Open'));
101+
this.e.openedChange.pipe(filter(isOpen => !isOpen)).subscribe(() => console.log('Close'));
102+
}
103+
}

0 commit comments

Comments
 (0)