Skip to content

Commit 4830be8

Browse files
devversionjelbourn
authored andcommitted
fix(ng-update): form-field css name incorrectly updated (#12768)
* Cleans up the `class-names` and `css-names` update rules. Moves them into separate directories for a better overview (similar to the attribute selectors; grouped by data input) * Fixes that the `css-name` rules incorrectly replace `mat-form-field-placeholder-wrapper` with `mat-form-field-labeler`. This happens because we first look for `mat-form-field-placeholder` -> create the replacement -> and then check on the same content (for proper offsets and reporting) and create another replacement for `mat-form-field-placeholder-wrapper`. `mat-form-field-placeholder-wrapper` should be just updated as part of `mat-form-field-placeholder`. * Updates an incorrect Github PR link for the `$mat-font-family` removal.
1 parent 8673e40 commit 4830be8

13 files changed

+325
-244
lines changed

src/lib/schematics/update/material/data/css-names.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ export interface MaterialCssNameData {
1515
replaceWith: string;
1616
/** Whitelist where this replacement is made. If omitted it is made in all files. */
1717
whitelist?: {
18-
/** Replace this name in CSS files. */
19-
css?: boolean,
18+
/** Replace this name in stylesheet files. */
19+
stylesheet?: boolean,
2020
/** Replace this name in HTML files. */
2121
html?: boolean,
2222
/** Replace this name in TypeScript strings. */
@@ -32,10 +32,6 @@ export const cssNames = transformChanges<MaterialCssNameData>([
3232
replace: 'mat-form-field-placeholder',
3333
replaceWith: 'mat-form-field-label'
3434
},
35-
{
36-
replace: 'mat-form-field-placeholder-wrapper',
37-
replaceWith: 'mat-form-field-label-wrapper'
38-
},
3935
{
4036
replace: 'mat-input-container',
4137
replaceWith: 'mat-form-field'
@@ -96,13 +92,13 @@ export const cssNames = transformChanges<MaterialCssNameData>([
9692
},
9793

9894
{
99-
pr: 'https://github.com/angular/material2/pull/10325',
95+
pr: 'https://github.com/angular/material2/pull/10430',
10096
changes: [
10197
{
10298
replace: '$mat-font-family',
10399
replaceWith: "Roboto, 'Helvetica Neue', sans-serif",
104100
whitelist: {
105-
css: true
101+
stylesheet: true
106102
}
107103
}
108104
]

src/lib/schematics/update/rules/checkIdentifierMiscRule.ts renamed to src/lib/schematics/update/rules/class-names/classNamesIdentifierMiscRule.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,22 @@
77
*/
88

99
import {bold, red} from 'chalk';
10-
import {ProgramAwareRuleWalker, RuleFailure, Rules} from 'tslint';
10+
import {RuleFailure, Rules, RuleWalker} from 'tslint';
1111
import * as ts from 'typescript';
1212

1313
/**
14-
* Rule that walks through every identifier that is part of Angular Material and replaces the
15-
* outdated name with the new one.
14+
* Rule that looks for class name identifiers that have been removed but cannot be
15+
* automatically migrated.
1616
*/
17-
export class Rule extends Rules.TypedRule {
18-
applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): RuleFailure[] {
19-
return this.applyWithWalker(
20-
new CheckIdentifierMiscWalker(sourceFile, this.getOptions(), program));
17+
export class Rule extends Rules.AbstractRule {
18+
19+
apply(sourceFile: ts.SourceFile): RuleFailure[] {
20+
return this.applyWithWalker(new Walker(sourceFile, this.getOptions()));
2121
}
2222
}
2323

24-
export class CheckIdentifierMiscWalker extends ProgramAwareRuleWalker {
24+
export class Walker extends RuleWalker {
25+
2526
visitIdentifier(identifier: ts.Identifier) {
2627
if (identifier.getText() === 'MatDrawerToggleResult') {
2728
this.addFailureAtNode(

src/lib/schematics/update/rules/switchIdentifiersRule.ts renamed to src/lib/schematics/update/rules/class-names/classNamesIdentifierRule.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@
99
import {green, red} from 'chalk';
1010
import {RuleFailure, Rules, RuleWalker} from 'tslint';
1111
import * as ts from 'typescript';
12-
import {classNames} from '../material/data/class-names';
12+
import {classNames} from '../../material/data/class-names';
1313
import {
1414
isMaterialExportDeclaration,
1515
isMaterialImportDeclaration,
16-
} from '../material/typescript-specifiers';
16+
} from '../../material/typescript-specifiers';
1717
import {
1818
isExportSpecifierNode,
1919
isImportSpecifierNode,
2020
isNamespaceImportNode,
21-
} from '../typescript/imports';
21+
} from '../../typescript/imports';
2222

2323
/**
2424
* Rule that walks through every identifier that is part of Angular Material and replaces the
@@ -27,11 +27,11 @@ import {
2727
export class Rule extends Rules.AbstractRule {
2828

2929
apply(sourceFile: ts.SourceFile): RuleFailure[] {
30-
return this.applyWithWalker(new SwitchIdentifiersWalker(sourceFile, this.getOptions()));
30+
return this.applyWithWalker(new Walker(sourceFile, this.getOptions()));
3131
}
3232
}
3333

34-
export class SwitchIdentifiersWalker extends RuleWalker {
34+
export class Walker extends RuleWalker {
3535

3636
/**
3737
* List of identifier names that have been imported from `@angular/material` or `@angular/cdk`
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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 {Replacement, RuleFailure, Rules, RuleWalker} from 'tslint';
11+
import * as ts from 'typescript';
12+
import {cssNames, MaterialCssNameData} from '../../material/data/css-names';
13+
import {findAllSubstringIndices} from '../../typescript/literal';
14+
15+
/**
16+
* Rule that walks through every string literal that is wrapped inside of a call expression.
17+
* All string literals which include an outdated CSS class name will be migrated.
18+
*/
19+
export class Rule extends Rules.AbstractRule {
20+
apply(sourceFile: ts.SourceFile): RuleFailure[] {
21+
return this.applyWithWalker(new Walker(sourceFile, this.getOptions()));
22+
}
23+
}
24+
25+
export class Walker extends RuleWalker {
26+
27+
visitStringLiteral(node: ts.StringLiteral) {
28+
if (node.parent && node.parent.kind !== ts.SyntaxKind.CallExpression) {
29+
return;
30+
}
31+
32+
const textContent = node.getFullText();
33+
34+
cssNames.forEach(name => {
35+
if (name.whitelist && !name.whitelist.strings) {
36+
return;
37+
}
38+
39+
findAllSubstringIndices(textContent, name.replace)
40+
.map(offset => node.getStart() + offset)
41+
.map(start => new Replacement(start, name.replace.length, name.replaceWith))
42+
.forEach(replacement => this._addFailureWithReplacement(node, replacement, name));
43+
});
44+
}
45+
46+
/** Adds a css name failure with the given replacement at the specified node. */
47+
private _addFailureWithReplacement(node: ts.Node, replacement: Replacement,
48+
name: MaterialCssNameData) {
49+
this.addFailureAtNode(node, `Found deprecated CSS class "${red(name.replace)}" which has ` +
50+
`been renamed to "${green(name.replaceWith)}"`, replacement);
51+
}
52+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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 {sync as globSync} from 'glob';
11+
import {IOptions, Replacement, RuleFailure, Rules} from 'tslint';
12+
import * as ts from 'typescript';
13+
import {cssNames} from '../../material/data/css-names';
14+
import {ExternalResource} from '../../tslint/component-file';
15+
import {ComponentWalker} from '../../tslint/component-walker';
16+
import {
17+
addFailureAtReplacement,
18+
createExternalReplacementFailure,
19+
} from '../../tslint/rule-failures';
20+
import {findAllSubstringIndices} from '../../typescript/literal';
21+
22+
/**
23+
* Rule that walks through every inline or external CSs stylesheet and updates outdated
24+
* CSS classes.
25+
*/
26+
export class Rule extends Rules.AbstractRule {
27+
apply(sourceFile: ts.SourceFile): RuleFailure[] {
28+
return this.applyWithWalker(new Walker(sourceFile, this.getOptions()));
29+
}
30+
}
31+
32+
export class Walker extends ComponentWalker {
33+
34+
constructor(sourceFile: ts.SourceFile, options: IOptions) {
35+
// In some applications, developers will have global stylesheets that are not specified in any
36+
// Angular component. Therefore we glob up all css and scss files outside of node_modules and
37+
// dist and check them as well.
38+
const extraFiles = globSync('!(node_modules|dist)/**/*.+(css|scss)');
39+
super(sourceFile, options, extraFiles);
40+
extraFiles.forEach(styleUrl => this._reportExternalStyle(styleUrl));
41+
}
42+
43+
44+
visitInlineStylesheet(literal: ts.StringLiteral) {
45+
this._createReplacementsForContent(literal, literal.getText())
46+
.forEach(data => addFailureAtReplacement(this, data.failureMessage, data.replacement));
47+
}
48+
49+
visitExternalStylesheet(node: ExternalResource) {
50+
this._createReplacementsForContent(node, node.getFullText())
51+
.map(data => createExternalReplacementFailure(node, data.failureMessage,
52+
this.getRuleName(), data.replacement))
53+
.forEach(failure => this.addFailure(failure));
54+
}
55+
56+
/**
57+
* Searches for outdated CSs classes in the specified content and creates replacements
58+
* with the according messages that can be added to a rule failure.
59+
*/
60+
private _createReplacementsForContent(node: ts.Node, stylesheetContent: string) {
61+
const replacements: {failureMessage: string, replacement: Replacement}[] = [];
62+
63+
cssNames.forEach(name => {
64+
if (name.whitelist && !name.whitelist.stylesheet) {
65+
return;
66+
}
67+
68+
const failureMessage = `Found deprecated CSS class "${red(name.replace)}" ` +
69+
`which has been renamed to "${green(name.replaceWith)}"`;
70+
71+
findAllSubstringIndices(stylesheetContent, name.replace)
72+
.map(offset => node.getStart() + offset)
73+
.map(start => new Replacement(start, name.replace.length, name.replaceWith))
74+
.forEach(replacement => replacements.push({replacement, failureMessage}));
75+
});
76+
77+
return replacements;
78+
}
79+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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 {Replacement, RuleFailure, Rules} from 'tslint';
11+
import * as ts from 'typescript';
12+
import {cssNames} from '../../material/data/css-names';
13+
import {ExternalResource} from '../../tslint/component-file';
14+
import {ComponentWalker} from '../../tslint/component-walker';
15+
import {
16+
addFailureAtReplacement,
17+
createExternalReplacementFailure,
18+
} from '../../tslint/rule-failures';
19+
import {findAllSubstringIndices} from '../../typescript/literal';
20+
21+
/**
22+
* Rule that walks through every inline or external HTML template and updates outdated
23+
* CSS classes.
24+
*/
25+
export class Rule extends Rules.AbstractRule {
26+
apply(sourceFile: ts.SourceFile): RuleFailure[] {
27+
return this.applyWithWalker(new Walker(sourceFile, this.getOptions()));
28+
}
29+
}
30+
31+
export class Walker extends ComponentWalker {
32+
33+
visitInlineTemplate(template: ts.StringLiteral) {
34+
this._createReplacementsForContent(template, template.getText())
35+
.forEach(data => addFailureAtReplacement(this, data.failureMessage, data.replacement));
36+
}
37+
38+
visitExternalTemplate(template: ExternalResource) {
39+
this._createReplacementsForContent(template, template.getFullText())
40+
.map(data => createExternalReplacementFailure(template, data.failureMessage,
41+
this.getRuleName(), data.replacement))
42+
.forEach(failure => this.addFailure(failure));
43+
}
44+
45+
/**
46+
* Searches for outdated css names in the specified content and creates replacements
47+
* with the according messages that can be added to a rule failure.
48+
*/
49+
private _createReplacementsForContent(node: ts.Node, templateContent: string) {
50+
const replacements: {failureMessage: string, replacement: Replacement}[] = [];
51+
52+
cssNames.forEach(selector => {
53+
if (selector.whitelist && !selector.whitelist.html) {
54+
return;
55+
}
56+
57+
const failureMessage = `Found deprecated CSS class "${red(selector.replace)}"` +
58+
` which has been renamed to "${green(selector.replaceWith)}"`;
59+
60+
findAllSubstringIndices(templateContent, selector.replace)
61+
.map(offset => node.getStart() + offset)
62+
.map(start => new Replacement(start, selector.replace.length, selector.replaceWith))
63+
.forEach(replacement => replacements.push({replacement, failureMessage}));
64+
});
65+
66+
return replacements;
67+
}
68+
}

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

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

0 commit comments

Comments
 (0)