Skip to content

Commit 52cb1cb

Browse files
devversionjosephperrott
authored andcommitted
refactor(schematics): cleanup attribute selector rules (#12507)
1 parent a2c6dfc commit 52cb1cb

23 files changed

+316
-308
lines changed

src/lib/schematics/update/material/color.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const colorFns = {
1616

1717
export function color(message: string): string {
1818
// 'r{{text}}' with red 'text', 'g{{text}}' with green 'text', and 'b{{text}}' with bold 'text'.
19-
return message.replace(/(.)\{\{(.*?)\}\}/g, (_m, fnName, text) => {
19+
return message.replace(/(.){{(.*?)}}/g, (_m, fnName, text) => {
2020
const fn = colorFns[fnName];
2121
return fn ? fn(text) : text;
2222
});

src/lib/schematics/update/material/extra-stylsheets.ts

Lines changed: 0 additions & 9 deletions
This file was deleted.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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 {
12+
attributeSelectors,
13+
MaterialAttributeSelectorData,
14+
} from '../../material/data/attribute-selectors';
15+
import {findAllSubstringIndices} from '../../typescript/literal';
16+
import * as ts from 'typescript';
17+
18+
/**
19+
* Rule that walks through every string literal that is part of a call expression and
20+
* switches deprecated attribute selectors to the updated selector.
21+
*/
22+
export class Rule extends Rules.AbstractRule {
23+
apply(sourceFile: ts.SourceFile): RuleFailure[] {
24+
return this.applyWithWalker(new Walker(sourceFile, this.getOptions()));
25+
}
26+
}
27+
28+
class Walker extends RuleWalker {
29+
30+
visitStringLiteral(literal: ts.StringLiteral) {
31+
if (literal.parent && literal.parent.kind !== ts.SyntaxKind.CallExpression) {
32+
return;
33+
}
34+
35+
const literalText = literal.getFullText();
36+
37+
attributeSelectors.forEach(selector => {
38+
findAllSubstringIndices(literalText, selector.replace)
39+
.map(offset => literal.getStart() + offset)
40+
.map(start => new Replacement(start, selector.replace.length, selector.replaceWith))
41+
.forEach(replacement => this._addFailureWithReplacement(literal, replacement, selector));
42+
});
43+
}
44+
45+
/** Adds an attribute selector failure with the given replacement at the specified node. */
46+
private _addFailureWithReplacement(node: ts.Node, replacement: Replacement,
47+
selector: MaterialAttributeSelectorData) {
48+
49+
this.addFailureAtNode(
50+
node,
51+
`Found deprecated attribute selector "${red('[' + selector.replace + ']')}" which ` +
52+
`has been renamed to "${green('[' + selector.replaceWith + ']')}"`,
53+
replacement);
54+
}
55+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
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 {attributeSelectors} from '../../material/data/attribute-selectors';
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+
import * as ts from 'typescript';
21+
22+
/**
23+
* Rule that walks through every stylesheet in the application and updates outdated
24+
* attribute selectors to the updated selector.
25+
*/
26+
export class Rule extends Rules.AbstractRule {
27+
28+
apply(sourceFile: ts.SourceFile): RuleFailure[] {
29+
return this.applyWithWalker(new Walker(sourceFile, this.getOptions()));
30+
}
31+
}
32+
33+
export class Walker extends ComponentWalker {
34+
35+
constructor(sourceFile: ts.SourceFile, options: IOptions) {
36+
// In some applications, developers will have global stylesheets that are not specified in any
37+
// Angular component. Therefore we glob up all css and scss files outside of node_modules and
38+
// dist and check them as well.
39+
const extraFiles = globSync('!(node_modules|dist)/**/*.+(css|scss)');
40+
super(sourceFile, options, extraFiles);
41+
extraFiles.forEach(styleUrl => this._reportExternalStyle(styleUrl));
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 attribute selectors 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+
attributeSelectors.forEach(selector => {
64+
const currentSelector = `[${selector.replace}]`;
65+
const updatedSelector = `[${selector.replaceWith}]`;
66+
67+
const failureMessage = `Found deprecated attribute selector "${red(currentSelector)}"` +
68+
` which has been renamed to "${green(updatedSelector)}"`;
69+
70+
findAllSubstringIndices(stylesheetContent, currentSelector)
71+
.map(offset => node.getStart() + offset)
72+
.map(start => new Replacement(start, currentSelector.length, updatedSelector))
73+
.forEach(replacement => replacements.push({replacement, failureMessage}));
74+
});
75+
76+
return replacements;
77+
}
78+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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 {attributeSelectors} from '../../material/data/attribute-selectors';
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 component template and switches outdated attribute
23+
* selectors to the updated selector.
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 attribute selectors 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+
attributeSelectors.forEach(selector => {
53+
const failureMessage = `Found deprecated attribute selector "[${red(selector.replace)}]"` +
54+
` which has been renamed to "[${green(selector.replaceWith)}]"`;
55+
56+
findAllSubstringIndices(templateContent, selector.replace)
57+
.map(offset => node.getStart() + offset)
58+
.map(start => new Replacement(start, selector.replace.length, selector.replaceWith))
59+
.forEach(replacement => replacements.push({replacement, failureMessage}));
60+
});
61+
62+
return replacements;
63+
}
64+
}

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import * as ts from 'typescript';
1212
import {findInputsOnElementWithTag, findOutputsOnElementWithTag} from '../html/angular';
1313
import {ExternalResource} from '../tslint/component-file';
1414
import {ComponentWalker} from '../tslint/component-walker';
15-
import {findAll} from '../typescript/literal';
15+
import {findAllSubstringIndices} from '../typescript/literal';
1616

1717
/**
1818
* Rule that walks through every component decorator and updates their inline or external
@@ -48,12 +48,14 @@ export class CheckTemplateMiscWalker extends ComponentWalker {
4848
{start: number, end: number, message: string}[] {
4949
let failures: {message: string, start: number, end: number}[] = [];
5050

51-
failures = failures.concat(findAll(templateContent, 'cdk-focus-trap').map(offset => ({
52-
start: offset,
53-
end: offset + 'cdk-focus-trap'.length,
54-
message: `Found deprecated element selector "${red('cdk-focus-trap')}" which has been` +
55-
` changed to an attribute selector "${green('[cdkTrapFocus]')}"`
56-
})));
51+
failures = failures.concat(
52+
findAllSubstringIndices(templateContent, 'cdk-focus-trap').map(offset => ({
53+
start: offset,
54+
end: offset + 'cdk-focus-trap'.length,
55+
message: `Found deprecated element selector "${red('cdk-focus-trap')}" which has been` +
56+
` changed to an attribute selector "${green('[cdkTrapFocus]')}"`
57+
}))
58+
);
5759

5860
failures = failures.concat(
5961
findOutputsOnElementWithTag(templateContent, 'selectionChange', ['mat-list-option'])

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

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

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {green, red} from 'chalk';
1010
import {Replacement, RuleFailure, Rules, RuleWalker} from 'tslint';
1111
import * as ts from 'typescript';
1212
import {cssNames} from '../material/data/css-names';
13-
import {findAll} from '../typescript/literal';
13+
import {findAllSubstringIndices} from '../typescript/literal';
1414

1515
/**
1616
* Rule that walks through every string literal, which includes the outdated Material name and
@@ -34,7 +34,7 @@ export class SwitchStringLiteralCssNamesWalker extends RuleWalker {
3434
cssNames.forEach(name => {
3535
if (!name.whitelist || name.whitelist.strings) {
3636
this.createReplacementsForOffsets(stringLiteral, name,
37-
findAll(stringLiteralText, name.replace)).forEach(replacement => {
37+
findAllSubstringIndices(stringLiteralText, name.replace)).forEach(replacement => {
3838
this.addFailureAtNode(
3939
stringLiteral,
4040
`Found deprecated CSS class "${red(name.replace)}" which has been renamed to` +

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {green, red} from 'chalk';
1010
import {Replacement, RuleFailure, Rules, RuleWalker} from 'tslint';
1111
import * as ts from 'typescript';
1212
import {elementSelectors} from '../material/data/element-selectors';
13-
import {findAll} from '../typescript/literal';
13+
import {findAllSubstringIndices} from '../typescript/literal';
1414

1515
/**
1616
* Rule that walks through every string literal, which includes the outdated Material name and
@@ -33,7 +33,7 @@ export class SwitchStringLiteralElementSelectorsWalker extends RuleWalker {
3333

3434
elementSelectors.forEach(selector => {
3535
this.createReplacementsForOffsets(stringLiteral, selector,
36-
findAll(stringLiteralText, selector.replace)).forEach(replacement => {
36+
findAllSubstringIndices(stringLiteralText, selector.replace)).forEach(replacement => {
3737
this.addFailureAtNode(
3838
stringLiteral,
3939
`Found deprecated element selector "${red(selector.replace)}" which has been` +

0 commit comments

Comments
 (0)