mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
refactor(schematics): cleanup attribute selector rules (#12507)
This commit is contained in:
committed by
Joey Perrott
parent
a2c6dfc92f
commit
52cb1cb1ac
@@ -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';
|
||||
+55
@@ -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);
|
||||
}
|
||||
}
|
||||
+78
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user