refactor(schematics): cleanup attribute selector rules (#12507)

This commit is contained in:
Paul Gschwendtner
2018-08-09 23:11:49 +02:00
committed by Joey Perrott
parent a2c6dfc92f
commit 52cb1cb1ac
23 changed files with 316 additions and 308 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ const colorFns = {
export function color(message: string): string {
// 'r{{text}}' with red 'text', 'g{{text}}' with green 'text', and 'b{{text}}' with bold 'text'.
return message.replace(/(.)\{\{(.*?)\}\}/g, (_m, fnName, text) => {
return message.replace(/(.){{(.*?)}}/g, (_m, fnName, text) => {
const fn = colorFns[fnName];
return fn ? fn(text) : text;
});
@@ -1,9 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
export const EXTRA_STYLESHEETS_GLOB_KEY = 'MD_EXTRA_STYLESHEETS_GLOB';
@@ -0,0 +1,55 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {green, red} from 'chalk';
import {Replacement, RuleFailure, Rules, RuleWalker} from 'tslint';
import {
attributeSelectors,
MaterialAttributeSelectorData,
} from '../../material/data/attribute-selectors';
import {findAllSubstringIndices} from '../../typescript/literal';
import * as ts from 'typescript';
/**
* Rule that walks through every string literal that is part of a call expression and
* switches deprecated attribute selectors to the updated selector.
*/
export class Rule extends Rules.AbstractRule {
apply(sourceFile: ts.SourceFile): RuleFailure[] {
return this.applyWithWalker(new Walker(sourceFile, this.getOptions()));
}
}
class Walker extends RuleWalker {
visitStringLiteral(literal: ts.StringLiteral) {
if (literal.parent && literal.parent.kind !== ts.SyntaxKind.CallExpression) {
return;
}
const literalText = literal.getFullText();
attributeSelectors.forEach(selector => {
findAllSubstringIndices(literalText, selector.replace)
.map(offset => literal.getStart() + offset)
.map(start => new Replacement(start, selector.replace.length, selector.replaceWith))
.forEach(replacement => this._addFailureWithReplacement(literal, replacement, selector));
});
}
/** Adds an attribute selector failure with the given replacement at the specified node. */
private _addFailureWithReplacement(node: ts.Node, replacement: Replacement,
selector: MaterialAttributeSelectorData) {
this.addFailureAtNode(
node,
`Found deprecated attribute selector "${red('[' + selector.replace + ']')}" which ` +
`has been renamed to "${green('[' + selector.replaceWith + ']')}"`,
replacement);
}
}
@@ -0,0 +1,78 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {green, red} from 'chalk';
import {sync as globSync} from 'glob';
import {IOptions, Replacement, RuleFailure, Rules} from 'tslint';
import {attributeSelectors} from '../../material/data/attribute-selectors';
import {ExternalResource} from '../../tslint/component-file';
import {ComponentWalker} from '../../tslint/component-walker';
import {
addFailureAtReplacement,
createExternalReplacementFailure,
} from '../../tslint/rule-failures';
import {findAllSubstringIndices} from '../../typescript/literal';
import * as ts from 'typescript';
/**
* Rule that walks through every stylesheet in the application and updates outdated
* attribute selectors to the updated selector.
*/
export class Rule extends Rules.AbstractRule {
apply(sourceFile: ts.SourceFile): RuleFailure[] {
return this.applyWithWalker(new Walker(sourceFile, this.getOptions()));
}
}
export class Walker extends ComponentWalker {
constructor(sourceFile: ts.SourceFile, options: IOptions) {
// In some applications, developers will have global stylesheets that are not specified in any
// Angular component. Therefore we glob up all css and scss files outside of node_modules and
// dist and check them as well.
const extraFiles = globSync('!(node_modules|dist)/**/*.+(css|scss)');
super(sourceFile, options, extraFiles);
extraFiles.forEach(styleUrl => this._reportExternalStyle(styleUrl));
}
visitInlineStylesheet(literal: ts.StringLiteral) {
this._createReplacementsForContent(literal, literal.getText())
.forEach(data => addFailureAtReplacement(this, data.failureMessage, data.replacement));
}
visitExternalStylesheet(node: ExternalResource) {
this._createReplacementsForContent(node, node.getFullText())
.map(data => createExternalReplacementFailure(node, data.failureMessage,
this.getRuleName(), data.replacement))
.forEach(failure => this.addFailure(failure));
}
/**
* Searches for outdated attribute selectors in the specified content and creates replacements
* with the according messages that can be added to a rule failure.
*/
private _createReplacementsForContent(node: ts.Node, stylesheetContent: string) {
const replacements: {failureMessage: string, replacement: Replacement}[] = [];
attributeSelectors.forEach(selector => {
const currentSelector = `[${selector.replace}]`;
const updatedSelector = `[${selector.replaceWith}]`;
const failureMessage = `Found deprecated attribute selector "${red(currentSelector)}"` +
` which has been renamed to "${green(updatedSelector)}"`;
findAllSubstringIndices(stylesheetContent, currentSelector)
.map(offset => node.getStart() + offset)
.map(start => new Replacement(start, currentSelector.length, updatedSelector))
.forEach(replacement => replacements.push({replacement, failureMessage}));
});
return replacements;
}
}
@@ -0,0 +1,64 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {green, red} from 'chalk';
import {Replacement, RuleFailure, Rules} from 'tslint';
import * as ts from 'typescript';
import {attributeSelectors} from '../../material/data/attribute-selectors';
import {ExternalResource} from '../../tslint/component-file';
import {ComponentWalker} from '../../tslint/component-walker';
import {
addFailureAtReplacement,
createExternalReplacementFailure,
} from '../../tslint/rule-failures';
import {findAllSubstringIndices} from '../../typescript/literal';
/**
* Rule that walks through every component template and switches outdated attribute
* selectors to the updated selector.
*/
export class Rule extends Rules.AbstractRule {
apply(sourceFile: ts.SourceFile): RuleFailure[] {
return this.applyWithWalker(new Walker(sourceFile, this.getOptions()));
}
}
export class Walker extends ComponentWalker {
visitInlineTemplate(template: ts.StringLiteral) {
this._createReplacementsForContent(template, template.getText())
.forEach(data => addFailureAtReplacement(this, data.failureMessage, data.replacement));
}
visitExternalTemplate(template: ExternalResource) {
this._createReplacementsForContent(template, template.getFullText())
.map(data => createExternalReplacementFailure(template, data.failureMessage,
this.getRuleName(), data.replacement))
.forEach(failure => this.addFailure(failure));
}
/**
* Searches for outdated attribute selectors in the specified content and creates replacements
* with the according messages that can be added to a rule failure.
*/
private _createReplacementsForContent(node: ts.Node, templateContent: string) {
const replacements: {failureMessage: string, replacement: Replacement}[] = [];
attributeSelectors.forEach(selector => {
const failureMessage = `Found deprecated attribute selector "[${red(selector.replace)}]"` +
` which has been renamed to "[${green(selector.replaceWith)}]"`;
findAllSubstringIndices(templateContent, selector.replace)
.map(offset => node.getStart() + offset)
.map(start => new Replacement(start, selector.replace.length, selector.replaceWith))
.forEach(replacement => replacements.push({replacement, failureMessage}));
});
return replacements;
}
}
@@ -12,7 +12,7 @@ import * as ts from 'typescript';
import {findInputsOnElementWithTag, findOutputsOnElementWithTag} from '../html/angular';
import {ExternalResource} from '../tslint/component-file';
import {ComponentWalker} from '../tslint/component-walker';
import {findAll} from '../typescript/literal';
import {findAllSubstringIndices} from '../typescript/literal';
/**
* Rule that walks through every component decorator and updates their inline or external
@@ -48,12 +48,14 @@ export class CheckTemplateMiscWalker extends ComponentWalker {
{start: number, end: number, message: string}[] {
let failures: {message: string, start: number, end: number}[] = [];
failures = failures.concat(findAll(templateContent, 'cdk-focus-trap').map(offset => ({
start: offset,
end: offset + 'cdk-focus-trap'.length,
message: `Found deprecated element selector "${red('cdk-focus-trap')}" which has been` +
` changed to an attribute selector "${green('[cdkTrapFocus]')}"`
})));
failures = failures.concat(
findAllSubstringIndices(templateContent, 'cdk-focus-trap').map(offset => ({
start: offset,
end: offset + 'cdk-focus-trap'.length,
message: `Found deprecated element selector "${red('cdk-focus-trap')}" which has been` +
` changed to an attribute selector "${green('[cdkTrapFocus]')}"`
}))
);
failures = failures.concat(
findOutputsOnElementWithTag(templateContent, 'selectionChange', ['mat-list-option'])
@@ -1,52 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {green, red} from 'chalk';
import {Replacement, RuleFailure, Rules, RuleWalker} from 'tslint';
import * as ts from 'typescript';
import {attributeSelectors} from '../material/data/attribute-selectors';
import {findAll} from '../typescript/literal';
/**
* Rule that walks through every string literal, which includes the outdated Material name and
* is part of a call expression. Those string literals will be changed to the new name.
*/
export class Rule extends Rules.AbstractRule {
apply(sourceFile: ts.SourceFile): RuleFailure[] {
return this.applyWithWalker(
new SwitchStringLiteralAttributeSelectorsWalker(sourceFile, this.getOptions()));
}
}
export class SwitchStringLiteralAttributeSelectorsWalker extends RuleWalker {
visitStringLiteral(stringLiteral: ts.StringLiteral) {
if (stringLiteral.parent && stringLiteral.parent.kind !== ts.SyntaxKind.CallExpression) {
return;
}
let stringLiteralText = stringLiteral.getFullText();
attributeSelectors.forEach(selector => {
this.createReplacementsForOffsets(stringLiteral, selector,
findAll(stringLiteralText, selector.replace)).forEach(replacement => {
this.addFailureAtNode(
stringLiteral,
`Found deprecated attribute selector "${red('[' + selector.replace + ']')}" which has` +
` been renamed to "${green('[' + selector.replaceWith + ']')}"`,
replacement);
});
});
}
private createReplacementsForOffsets(node: ts.Node,
update: {replace: string, replaceWith: string},
offsets: number[]): Replacement[] {
return offsets.map(offset => this.createReplacement(
node.getStart() + offset, update.replace.length, update.replaceWith));
}
}
@@ -10,7 +10,7 @@ import {green, red} from 'chalk';
import {Replacement, RuleFailure, Rules, RuleWalker} from 'tslint';
import * as ts from 'typescript';
import {cssNames} from '../material/data/css-names';
import {findAll} from '../typescript/literal';
import {findAllSubstringIndices} from '../typescript/literal';
/**
* Rule that walks through every string literal, which includes the outdated Material name and
@@ -34,7 +34,7 @@ export class SwitchStringLiteralCssNamesWalker extends RuleWalker {
cssNames.forEach(name => {
if (!name.whitelist || name.whitelist.strings) {
this.createReplacementsForOffsets(stringLiteral, name,
findAll(stringLiteralText, name.replace)).forEach(replacement => {
findAllSubstringIndices(stringLiteralText, name.replace)).forEach(replacement => {
this.addFailureAtNode(
stringLiteral,
`Found deprecated CSS class "${red(name.replace)}" which has been renamed to` +
@@ -10,7 +10,7 @@ import {green, red} from 'chalk';
import {Replacement, RuleFailure, Rules, RuleWalker} from 'tslint';
import * as ts from 'typescript';
import {elementSelectors} from '../material/data/element-selectors';
import {findAll} from '../typescript/literal';
import {findAllSubstringIndices} from '../typescript/literal';
/**
* Rule that walks through every string literal, which includes the outdated Material name and
@@ -33,7 +33,7 @@ export class SwitchStringLiteralElementSelectorsWalker extends RuleWalker {
elementSelectors.forEach(selector => {
this.createReplacementsForOffsets(stringLiteral, selector,
findAll(stringLiteralText, selector.replace)).forEach(replacement => {
findAllSubstringIndices(stringLiteralText, selector.replace)).forEach(replacement => {
this.addFailureAtNode(
stringLiteral,
`Found deprecated element selector "${red(selector.replace)}" which has been` +
@@ -1,90 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {green, red} from 'chalk';
import {sync as globSync} from 'glob';
import {IOptions, Replacement, RuleFailure, Rules} from 'tslint';
import {attributeSelectors} from '../material/data/attribute-selectors';
import {ExternalResource} from '../tslint/component-file';
import {ComponentWalker} from '../tslint/component-walker';
import {findAll} from '../typescript/literal';
import * as ts from 'typescript';
/**
* Rule that walks through every component decorator and updates their inline or external
* stylesheets.
*/
export class Rule extends Rules.AbstractRule {
apply(sourceFile: ts.SourceFile): RuleFailure[] {
return this.applyWithWalker(
new SwitchStylesheetAtributeSelectorsWalker(sourceFile, this.getOptions()));
}
}
export class SwitchStylesheetAtributeSelectorsWalker extends ComponentWalker {
constructor(sourceFile: ts.SourceFile, options: IOptions) {
// In some applications, developers will have global stylesheets that are not specified in any
// Angular component. Therefore we glob up all css and scss files outside of node_modules and
// dist and check them as well.
const extraFiles = globSync('!(node_modules|dist)/**/*.+(css|scss)');
super(sourceFile, options, extraFiles);
extraFiles.forEach(styleUrl => this._reportExternalStyle(styleUrl));
}
visitInlineStylesheet(stylesheet: ts.StringLiteral) {
this.replaceNamesInStylesheet(stylesheet, stylesheet.getText()).forEach(replacement => {
const fix = replacement.replacement;
const ruleFailure = new RuleFailure(stylesheet.getSourceFile(), fix.start, fix.end,
replacement.message, this.getRuleName(), fix);
this.addFailure(ruleFailure);
});
}
visitExternalStylesheet(stylesheet: ExternalResource) {
this.replaceNamesInStylesheet(stylesheet, stylesheet.getFullText()).forEach(replacement => {
const fix = replacement.replacement;
const ruleFailure = new RuleFailure(stylesheet, fix.start + 1, fix.end + 1,
replacement.message, this.getRuleName(), fix);
this.addFailure(ruleFailure);
});
}
/**
* Replaces the outdated name in the stylesheet with the new one and returns an updated
* stylesheet.
*/
private replaceNamesInStylesheet(node: ts.Node, stylesheetContent: string):
{message: string, replacement: Replacement}[] {
const replacements: {message: string, replacement: Replacement}[] = [];
attributeSelectors.forEach(selector => {
const bracketedSelector = {
replace: `[${selector.replace}]`,
replaceWith: `[${selector.replaceWith}]`
};
this.createReplacementsForOffsets(node, bracketedSelector,
findAll(stylesheetContent, bracketedSelector.replace)).forEach(replacement => {
replacements.push({
message: `Found deprecated attribute selector "${red(bracketedSelector.replace)}"` +
` which has been renamed to "${green(bracketedSelector.replaceWith)}"`,
replacement
});
});
});
return replacements;
}
private createReplacementsForOffsets(node: ts.Node,
update: {replace: string, replaceWith: string},
offsets: number[]): Replacement[] {
return offsets.map(offset => this.createReplacement(
node.getStart() + offset, update.replace.length, update.replaceWith));
}
}
@@ -13,7 +13,7 @@ import * as ts from 'typescript';
import {cssNames} from '../material/data/css-names';
import {ExternalResource} from '../tslint/component-file';
import {ComponentWalker} from '../tslint/component-walker';
import {findAll} from '../typescript/literal';
import {findAllSubstringIndices} from '../typescript/literal';
/**
* Rule that walks through every component decorator and updates their inline or external
@@ -64,14 +64,15 @@ export class SwitchStylesheetCssNamesWalker extends ComponentWalker {
cssNames.forEach(name => {
if (!name.whitelist || name.whitelist.css) {
this.createReplacementsForOffsets(node, name, findAll(stylesheetContent, name.replace))
.forEach(replacement => {
replacements.push({
message: `Found CSS class "${red(name.replace)}" which has been renamed to` +
` "${green(name.replaceWith)}"`,
replacement
});
});
const foundOffsets = findAllSubstringIndices(stylesheetContent, name.replace);
this.createReplacementsForOffsets(node, name, foundOffsets).forEach(replacement => {
replacements.push({
message: `Found CSS class "${red(name.replace)}" which has been renamed to` +
` "${green(name.replaceWith)}"`,
replacement
});
});
}
});
@@ -12,7 +12,7 @@ import {IOptions, Replacement, RuleFailure, Rules} from 'tslint';
import {elementSelectors} from '../material/data/element-selectors';
import {ExternalResource} from '../tslint/component-file';
import {ComponentWalker} from '../tslint/component-walker';
import {findAll} from '../typescript/literal';
import {findAllSubstringIndices} from '../typescript/literal';
import * as ts from 'typescript';
/**
@@ -65,7 +65,7 @@ export class SwitchStylesheetElementSelectorsWalker extends ComponentWalker {
elementSelectors.forEach(selector => {
this.createReplacementsForOffsets(node, selector,
findAll(stylesheetContent, selector.replace)).forEach(replacement => {
findAllSubstringIndices(stylesheetContent, selector.replace)).forEach(replacement => {
replacements.push({
message: `Found deprecated element selector "${red(selector.replace)}" which has` +
` been renamed to "${green(selector.replaceWith)}"`,
@@ -12,7 +12,7 @@ import {IOptions, Replacement, RuleFailure, Rules} from 'tslint';
import {inputNames} from '../material/data/input-names';
import {ExternalResource} from '../tslint/component-file';
import {ComponentWalker} from '../tslint/component-walker';
import {findAll} from '../typescript/literal';
import {findAllSubstringIndices} from '../typescript/literal';
import * as ts from 'typescript';
/**
@@ -66,14 +66,15 @@ export class SwitchStylesheetInputNamesWalker extends ComponentWalker {
inputNames.forEach(name => {
if (!name.whitelist || name.whitelist.css) {
const bracketedName = {replace: `[${name.replace}]`, replaceWith: `[${name.replaceWith}]`};
this.createReplacementsForOffsets(node, name,
findAll(stylesheetContent, bracketedName.replace)).forEach(replacement => {
replacements.push({
message: `Found deprecated @Input() "${red(name.replace)}" which has been renamed` +
` to "${green(name.replaceWith)}"`,
replacement
});
});
const foundOffsets = findAllSubstringIndices(stylesheetContent, bracketedName.replace);
this.createReplacementsForOffsets(node, name, foundOffsets).forEach(replacement => {
replacements.push({
message: `Found deprecated @Input() "${red(name.replace)}" which has been renamed` +
` to "${green(name.replaceWith)}"`,
replacement
});
});
}
});
@@ -12,7 +12,7 @@ import {IOptions, Replacement, RuleFailure, Rules} from 'tslint';
import {outputNames} from '../material/data/output-names';
import {ExternalResource} from '../tslint/component-file';
import {ComponentWalker} from '../tslint/component-walker';
import {findAll} from '../typescript/literal';
import {findAllSubstringIndices} from '../typescript/literal';
import * as ts from 'typescript';
/**
@@ -66,14 +66,15 @@ export class SwitchStylesheetOutputNamesWalker extends ComponentWalker {
outputNames.forEach(name => {
if (!name.whitelist || name.whitelist.css) {
const bracketedName = {replace: `[${name.replace}]`, replaceWith: `[${name.replaceWith}]`};
this.createReplacementsForOffsets(node, name,
findAll(stylesheetContent, bracketedName.replace)).forEach(replacement => {
replacements.push({
message: `Found deprecated @Output() "${red(name.replace)}" which has been` +
` renamed to "${green(name.replaceWith)}"`,
replacement
});
});
const foundOffsets = findAllSubstringIndices(stylesheetContent, bracketedName.replace);
this.createReplacementsForOffsets(node, name, foundOffsets).forEach(replacement => {
replacements.push({
message: `Found deprecated @Output() "${red(name.replace)}" which has been` +
` renamed to "${green(name.replaceWith)}"`,
replacement
});
});
}
});
@@ -1,78 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {green, red} from 'chalk';
import {Replacement, RuleFailure, Rules} from 'tslint';
import * as ts from 'typescript';
import {attributeSelectors} from '../material/data/attribute-selectors';
import {ExternalResource} from '../tslint/component-file';
import {ComponentWalker} from '../tslint/component-walker';
import {findAll} from '../typescript/literal';
/**
* Rule that walks through every component decorator and updates their inline or external
* templates.
*/
export class Rule extends Rules.AbstractRule {
apply(sourceFile: ts.SourceFile): RuleFailure[] {
return this.applyWithWalker(
new SwitchTemplateAttributeSelectorsWalker(sourceFile, this.getOptions()));
}
}
export class SwitchTemplateAttributeSelectorsWalker extends ComponentWalker {
visitInlineTemplate(template: ts.StringLiteral) {
this.replaceNamesInTemplate(template, template.getText()).forEach(replacement => {
const fix = replacement.replacement;
const ruleFailure = new RuleFailure(template.getSourceFile(), fix.start, fix.end,
replacement.message, this.getRuleName(), fix);
this.addFailure(ruleFailure);
});
}
visitExternalTemplate(template: ExternalResource) {
this.replaceNamesInTemplate(template, template.getFullText()).forEach(replacement => {
const fix = replacement.replacement;
const ruleFailure = new RuleFailure(template, fix.start + 1, fix.end + 1,
replacement.message, this.getRuleName(), fix);
this.addFailure(ruleFailure);
});
}
/**
* Replaces the outdated name in the template with the new one and returns an updated template.
*/
private replaceNamesInTemplate(node: ts.Node, templateContent: string):
{message: string, replacement: Replacement}[] {
const replacements: {message: string, replacement: Replacement}[] = [];
attributeSelectors.forEach(selector => {
// Being more aggressive with that replacement here allows us to also handle inline
// style elements. Normally we would check if the selector is surrounded by the HTML tag
// characters.
this.createReplacementsForOffsets(node, selector, findAll(templateContent, selector.replace))
.forEach(replacement => {
replacements.push({
message: `Found deprecated attribute selector` +
` "${red('[' + selector.replace + ']')}" which has been renamed to` +
` "${green('[' + selector.replaceWith + ']')}"`,
replacement
});
});
});
return replacements;
}
private createReplacementsForOffsets(node: ts.Node,
update: {replace: string, replaceWith: string},
offsets: number[]): Replacement[] {
return offsets.map(offset => this.createReplacement(
node.getStart() + offset, update.replace.length, update.replaceWith));
}
}
@@ -12,7 +12,7 @@ import * as ts from 'typescript';
import {cssNames} from '../material/data/css-names';
import {ExternalResource} from '../tslint/component-file';
import {ComponentWalker} from '../tslint/component-walker';
import {findAll} from '../typescript/literal';
import {findAllSubstringIndices} from '../typescript/literal';
/**
* Rule that walks through every component decorator and updates their inline or external
@@ -52,14 +52,15 @@ export class SwitchTemplateCaaNamesWalker extends ComponentWalker {
cssNames.forEach(name => {
if (!name.whitelist || name.whitelist.html) {
this.createReplacementsForOffsets(node, name, findAll(templateContent, name.replace))
.forEach(replacement => {
replacements.push({
message: `Found deprecated CSS class "${red(name.replace)}" which has been` +
` renamed to "${green(name.replaceWith)}"`,
replacement
});
});
const foundOffsets = findAllSubstringIndices(templateContent, name.replace);
this.createReplacementsForOffsets(node, name, foundOffsets).forEach(replacement => {
replacements.push({
message: `Found deprecated CSS class "${red(name.replace)}" which has been` +
` renamed to "${green(name.replaceWith)}"`,
replacement
});
});
}
});
@@ -12,7 +12,7 @@ import * as ts from 'typescript';
import {elementSelectors} from '../material/data/element-selectors';
import {ExternalResource} from '../tslint/component-file';
import {ComponentWalker} from '../tslint/component-walker';
import {findAll} from '../typescript/literal';
import {findAllSubstringIndices} from '../typescript/literal';
/**
* Rule that walks through every component decorator and updates their inline or external
@@ -55,14 +55,15 @@ export class SwitchTemplateElementSelectorsWalker extends ComponentWalker {
// Being more aggressive with that replacement here allows us to also handle inline
// style elements. Normally we would check if the selector is surrounded by the HTML tag
// characters.
this.createReplacementsForOffsets(node, selector, findAll(templateContent, selector.replace))
.forEach(replacement => {
replacements.push({
message: `Found deprecated element selector "${red(selector.replace)}" which has` +
` been renamed to "${green(selector.replaceWith)}"`,
replacement
});
});
const foundOffsets = findAllSubstringIndices(templateContent, selector.replace);
this.createReplacementsForOffsets(node, selector, foundOffsets).forEach(replacement => {
replacements.push({
message: `Found deprecated element selector "${red(selector.replace)}" which has` +
` been renamed to "${green(selector.replaceWith)}"`,
replacement
});
});
});
return replacements;
@@ -12,7 +12,7 @@ import * as ts from 'typescript';
import {exportAsNames} from '../material/data/export-as-names';
import {ExternalResource} from '../tslint/component-file';
import {ComponentWalker} from '../tslint/component-walker';
import {findAll} from '../typescript/literal';
import {findAllSubstringIndices} from '../typescript/literal';
/**
* Rule that walks through every component decorator and updates their inline or external
@@ -52,14 +52,15 @@ export class SwitchTemplateExportAsNamesWalker extends ComponentWalker {
const replacements: {message: string, replacement: Replacement}[] = [];
exportAsNames.forEach(name => {
this.createReplacementsForOffsets(node, name, findAll(templateContent, name.replace))
.forEach(replacement => {
replacements.push({
message: `Found deprecated exportAs reference "${red(name.replace)}" which has been` +
` renamed to "${green(name.replaceWith)}"`,
replacement
});
});
const foundOffsets = findAllSubstringIndices(templateContent, name.replace);
this.createReplacementsForOffsets(node, name, foundOffsets).forEach(replacement => {
replacements.push({
message: `Found deprecated exportAs reference "${red(name.replace)}" which has been` +
` renamed to "${green(name.replaceWith)}"`,
replacement
});
});
});
return replacements;
@@ -13,7 +13,7 @@ import {findInputsOnElementWithAttr, findInputsOnElementWithTag} from '../html/a
import {inputNames} from '../material/data/input-names';
import {ExternalResource} from '../tslint/component-file';
import {ComponentWalker} from '../tslint/component-walker';
import {findAll} from '../typescript/literal';
import {findAllSubstringIndices} from '../typescript/literal';
/**
* Rule that walks through every component decorator and updates their inline or external
@@ -62,7 +62,7 @@ export class SwitchTemplateInputNamesWalker extends ComponentWalker {
templateContent, name.replace, name.whitelist.elements));
}
if (!name.whitelist) {
offsets = offsets.concat(findAll(templateContent, name.replace));
offsets = offsets.concat(findAllSubstringIndices(templateContent, name.replace));
}
this.createReplacementsForOffsets(node, name, offsets).forEach(replacement => {
replacements.push({
@@ -13,7 +13,7 @@ import {findOutputsOnElementWithAttr, findOutputsOnElementWithTag} from '../html
import {outputNames} from '../material/data/output-names';
import {ExternalResource} from '../tslint/component-file';
import {ComponentWalker} from '../tslint/component-walker';
import {findAll} from '../typescript/literal';
import {findAllSubstringIndices} from '../typescript/literal';
/**
* Rule that walks through every component decorator and updates their inline or external
@@ -62,7 +62,7 @@ export class SwitchTemplateOutputNamesWalker extends ComponentWalker {
templateContent, name.replace, name.whitelist.elements));
}
if (!name.whitelist) {
offsets = offsets.concat(findAll(templateContent, name.replace));
offsets = offsets.concat(findAllSubstringIndices(templateContent, name.replace));
}
this.createReplacementsForOffsets(node, name, offsets).forEach(replacement => {
replacements.push({
@@ -0,0 +1,27 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Replacement, RuleFailure, RuleWalker} from 'tslint';
import {ExternalResource} from './component-file';
/**
* Creates a standalone rule failure that can be used to apply a replacement for an
* external resource from the custom ComponentWalker.
*/
export function createExternalReplacementFailure(node: ExternalResource, failureMessage: string,
ruleName: string, replacement: Replacement) {
return new RuleFailure(node, replacement.start, replacement.end, failureMessage,
ruleName, replacement);
}
/** Adds a failure to the given walker at the location of the specified replacement. */
export function addFailureAtReplacement(walker: RuleWalker, failureMessage: string,
replacement: Replacement) {
walker.addFailureAt(replacement.start, replacement.end, failureMessage, replacement);
}
@@ -13,11 +13,11 @@ export function getLiteralTextWithoutQuotes(literal: ts.StringLiteral) {
return literal.getText().substring(1, literal.getText().length - 1);
}
/** Method that can be used to replace all search occurrences in a string. */
export function findAll(str: string, search: string): number[] {
/** Finds all start indices of the given search string in the input string. */
export function findAllSubstringIndices(input: string, search: string): number[] {
const result: number[] = [];
let i = -1;
while ((i = str.indexOf(search, i + 1)) !== -1) {
while ((i = input.indexOf(search, i + 1)) !== -1) {
result.push(i);
}
return result;
+9 -4
View File
@@ -26,26 +26,31 @@ export default function(): Rule {
for (const tsconfig of allTsConfigPaths) {
// Run the update tslint rules.
tslintFixTasks.push(context.addTask(new TslintFixTask({
rulesDirectory: path.join(__dirname, 'rules/'),
rulesDirectory: [
path.join(__dirname, 'rules/'),
path.join(__dirname, 'rules/attribute-selectors'),
],
rules: {
// Automatic fixes.
'switch-identifiers': true,
'switch-property-names': true,
'switch-string-literal-attribute-selectors': true,
'switch-string-literal-css-names': true,
'switch-string-literal-element-selectors': true,
'switch-stylesheet-attribute-selectors': true,
'switch-stylesheet-css-names': true,
'switch-stylesheet-element-selectors': true,
'switch-stylesheet-input-names': true,
'switch-stylesheet-output-names': true,
'switch-template-attribute-selectors': true,
'switch-template-css-names': true,
'switch-template-element-selectors': true,
'switch-template-export-as-names': true,
'switch-template-input-names': true,
'switch-template-output-names': true,
// Attribute selector update rules.
'attribute-selectors-string-literal': true,
'attribute-selectors-stylesheet': true,
'attribute-selectors-template': true,
// Additional issues we can detect but not automatically fix.
'check-class-declaration-misc': true,
'check-identifier-misc': true,