Skip to content

Commit 7ba1c5a

Browse files
committed
refactor(schematics): cleanup attribute selector rules
* Cleans up the `attribute-selector` rules by avoiding duplicate code and making the code generally more readable. Also moves all rules tied to the `attributeSelectors` data into a sub directory. * Renames the `findAll` method to `findAllSearchOccurrences` because just having `findAll` is very ambiguous. * Removes the unused `extra-stylesheets` file.
1 parent 674ebcd commit 7ba1c5a

23 files changed

+325
-310
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: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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 {findAllSearchOccurrences} from '../../typescript/literal';
16+
import * as ts from 'typescript';
17+
18+
/**
19+
* Rule that walks through every string literal that i
20+
*/
21+
export class Rule extends Rules.AbstractRule {
22+
apply(sourceFile: ts.SourceFile): RuleFailure[] {
23+
return this.applyWithWalker(new Walker(sourceFile, this.getOptions()));
24+
}
25+
}
26+
27+
class Walker extends RuleWalker {
28+
29+
visitStringLiteral(literal: ts.StringLiteral) {
30+
if (literal.parent && literal.parent.kind !== ts.SyntaxKind.CallExpression) {
31+
return;
32+
}
33+
34+
const literalText = literal.getFullText();
35+
36+
attributeSelectors.forEach(selector => {
37+
findAllSearchOccurrences(literalText, selector.replace)
38+
.map(offset => literal.getStart() + offset)
39+
.map(start => new Replacement(start, selector.replace.length, selector.replaceWith))
40+
.forEach(replacement => this._addFailureWithReplacement(literal, replacement, selector));
41+
});
42+
}
43+
44+
/** Adds an attribute selector failure with the given replacement at the specified node. */
45+
private _addFailureWithReplacement(node: ts.Node, replacement: Replacement,
46+
selector: MaterialAttributeSelectorData) {
47+
48+
this.addFailureAtNode(
49+
node,
50+
`Found deprecated attribute selector "${red('[' + selector.replace + ']')}" which ` +
51+
`has been renamed to "${green('[' + selector.replaceWith + ']')}"`,
52+
replacement);
53+
}
54+
}
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 {findAllSearchOccurrences} 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+
findAllSearchOccurrences(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 {findAllSearchOccurrences} 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+
findAllSearchOccurrences(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: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,14 @@
88

99
import {bold, green, red} from 'chalk';
1010
import {RuleFailure, Rules} from 'tslint';
11-
import * as ts from 'typescript';
1211
import {ExternalResource} from '../tslint/component-file';
1312
import {ComponentWalker} from '../tslint/component-walker';
14-
import {findAll, findAllInputsInElWithTag, findAllOutputsInElWithTag} from '../typescript/literal';
13+
import {
14+
findAllInputsInElWithTag,
15+
findAllOutputsInElWithTag,
16+
findAllSearchOccurrences,
17+
} from '../typescript/literal';
18+
import * as ts from 'typescript';
1519

1620
/**
1721
* Rule that walks through every component decorator and updates their inline or external
@@ -47,12 +51,14 @@ export class CheckTemplateMiscWalker extends ComponentWalker {
4751
{start: number, end: number, message: string}[] {
4852
let failures: {message: string, start: number, end: number}[] = [];
4953

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

5763
failures = failures.concat(
5864
findAllOutputsInElWithTag(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 {findAllSearchOccurrences} 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+
findAllSearchOccurrences(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 {findAllSearchOccurrences} 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+
findAllSearchOccurrences(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)