refactor(schematics): cleanup input and output name rules (#12800)

* Cleans up the update rules for `@Input()` and `@Output()` names.
* Adds thorough test cases for input and output names.
This commit is contained in:
Paul Gschwendtner
2018-08-23 22:28:10 +02:00
committed by Jeremy Elbourn
parent 3723191be8
commit 1840008837
18 changed files with 458 additions and 371 deletions
+20 -12
View File
@@ -10,10 +10,26 @@ import {findAttributeOnElementWithAttrs, findAttributeOnElementWithTag} from './
/** Finds the specified Angular @Input in the given elements with tag name. */
export function findInputsOnElementWithTag(html: string, inputName: string, tagNames: string[]) {
// Add one column to the mapped offset because the first bracket for the @Input
// is part of the attribute and therefore also part of the offset. We only want to return
// the offset for the inner name of the input.
return findAttributeOnElementWithTag(html, `[${inputName}]`, tagNames).map(offset => offset + 1);
return [
// Inputs can be also used without brackets (e.g. `<mat-toolbar color="primary">`)
...findAttributeOnElementWithTag(html, inputName, tagNames),
// Add one column to the mapped offset because the first bracket for the @Input
// is part of the attribute and therefore also part of the offset. We only want to return
// the offset for the inner name of the bracketed input.
...findAttributeOnElementWithTag(html, `[${inputName}]`, tagNames).map(offset => offset + 1),
];
}
/** Finds the specified Angular @Input in elements that have one of the specified attributes. */
export function findInputsOnElementWithAttr(html: string, inputName: string, attrs: string[]) {
return [
// Inputs can be also used without brackets (e.g. `<button mat-button color="primary">`)
...findAttributeOnElementWithAttrs(html, inputName, attrs),
// Add one column to the mapped offset because the first bracket for the @Input
// is part of the attribute and therefore also part of the offset. We only want to return
// the offset for the inner name of the bracketed input.
...findAttributeOnElementWithAttrs(html, `[${inputName}]`, attrs).map(offset => offset + 1),
];
}
/** Finds the specified Angular @Output in the given elements with tag name. */
@@ -24,14 +40,6 @@ export function findOutputsOnElementWithTag(html: string, outputName: string, ta
return findAttributeOnElementWithTag(html, `(${outputName})`, tagNames).map(offset => offset + 1);
}
/** Finds the specified Angular @Input in elements that have one of the specified attributes. */
export function findInputsOnElementWithAttr(html: string, inputName: string, attrs: string[]) {
// Add one column to the mapped offset because the first bracket for the @Input
// is part of the attribute and therefore also part of the offset. We only want to return
// the offset for the inner name of the input.
return findAttributeOnElementWithAttrs(html, `[${inputName}]`, attrs).map(offset => offset + 1);
}
/** Finds the specified Angular @Output in elements that have one of the specified attributes. */
export function findOutputsOnElementWithAttr(html: string, outputName: string, attrs: string[]) {
// Add one column to the mapped offset because the first bracket for the @Output
@@ -19,8 +19,12 @@ export interface MaterialInputNameData {
elements?: string[],
/** Limit to elements with any of these attributes. */
attributes?: string[],
/** Whether to ignore CSS attribute selectors when doing this replacement. */
css?: boolean,
/**
* Whether inputs in stylesheets should be updated or not. Note that inputs inside of
* stylesheets usually don't make sense, but if developers use an input as a plain one-time
* attribute, it can be targeted through CSS selectors.
*/
stylesheet?: boolean,
};
}
@@ -19,8 +19,6 @@ export interface MaterialOutputNameData {
elements?: string[],
/** Limit to elements with any of these attributes. */
attributes?: string[],
/** Whether to ignore CSS attribute selectors when doing this replacement. */
css?: boolean,
};
}
@@ -0,0 +1,86 @@
/**
* @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 * as ts from 'typescript';
import {inputNames} from '../../material/data/input-names';
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 inline or external stylesheet and replaces outdated CSS selectors
* that query for an @Input() with the new input name.
*
* Note that inputs inside of stylesheets usually don't make sense, but if developers use an
* input as a plain one-time attribute, it can be targeted through CSS selectors.
*
* e.g. `<my-component color="primary">` becomes `my-component[color]`
*/
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}[] = [];
inputNames.forEach(name => {
if (name.whitelist && !name.whitelist.stylesheet) {
return;
}
const currentSelector = `[${name.replace}]`;
const updatedSelector = `[${name.replaceWith}]`;
const failureMessage = `Found deprecated @Input() CSS 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,76 @@
/**
* @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 {findInputsOnElementWithAttr, findInputsOnElementWithTag} from '../../html/angular';
import {inputNames} from '../../material/data/input-names';
import {ExternalResource} from '../../tslint/component-file';
import {ComponentWalker} from '../../tslint/component-walker';
import {
addFailureAtReplacement,
createExternalReplacementFailure,
} from '../../tslint/rule-failures';
/**
* Rule that walks through every inline or external HTML template and switches changed input
* bindings to the proper new name.
*/
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 input bindings 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}[] = [];
inputNames.forEach(name => {
const whitelist = name.whitelist;
const relativeOffsets = [];
const failureMessage = `Found deprecated @Input() "${red(name.replace)}"` +
` which has been renamed to "${green(name.replaceWith)}"`;
if (!whitelist || whitelist.attributes) {
relativeOffsets.push(
...findInputsOnElementWithAttr(templateContent, name.replace, whitelist.attributes));
}
if (!whitelist || whitelist.elements) {
relativeOffsets.push(
...findInputsOnElementWithTag(templateContent, name.replace, whitelist.elements));
}
relativeOffsets
.map(offset => node.getStart() + offset)
.map(start => new Replacement(start, name.replace.length, name.replaceWith))
.forEach(replacement => replacements.push({replacement, failureMessage}));
});
return replacements;
}
}
@@ -0,0 +1,76 @@
/**
* @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 {findOutputsOnElementWithAttr, findOutputsOnElementWithTag} from '../../html/angular';
import {outputNames} from '../../material/data/output-names';
import {ExternalResource} from '../../tslint/component-file';
import {ComponentWalker} from '../../tslint/component-walker';
import {
addFailureAtReplacement,
createExternalReplacementFailure,
} from '../../tslint/rule-failures';
/**
* Rule that walks through every inline or external HTML template and switches changed output
* bindings to the proper new output name.
*/
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 output bindings 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}[] = [];
outputNames.forEach(name => {
const whitelist = name.whitelist;
const relativeOffsets = [];
const failureMessage = `Found deprecated @Output() "${red(name.replace)}"` +
` which has been renamed to "${green(name.replaceWith)}"`;
if (!whitelist || whitelist.attributes) {
relativeOffsets.push(
...findOutputsOnElementWithAttr(templateContent, name.replace, whitelist.attributes));
}
if (!whitelist || whitelist.elements) {
relativeOffsets.push(
...findOutputsOnElementWithTag(templateContent, name.replace, whitelist.elements));
}
relativeOffsets
.map(offset => node.getStart() + offset)
.map(start => new Replacement(start, name.replace.length, name.replaceWith))
.forEach(replacement => replacements.push({replacement, failureMessage}));
});
return replacements;
}
}
@@ -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 {inputNames} from '../material/data/input-names';
import {ExternalResource} from '../tslint/component-file';
import {ComponentWalker} from '../tslint/component-walker';
import {findAllSubstringIndices} 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 SwitchStylesheetInputNamesWalker(sourceFile, this.getOptions()));
}
}
export class SwitchStylesheetInputNamesWalker 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}[] = [];
inputNames.forEach(name => {
if (!name.whitelist || name.whitelist.css) {
const bracketedName = {replace: `[${name.replace}]`, replaceWith: `[${name.replaceWith}]`};
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
});
});
}
});
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));
}
}
@@ -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 {outputNames} from '../material/data/output-names';
import {ExternalResource} from '../tslint/component-file';
import {ComponentWalker} from '../tslint/component-walker';
import {findAllSubstringIndices} 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 SwitchStylesheetOutputNamesWalker(sourceFile, this.getOptions()));
}
}
export class SwitchStylesheetOutputNamesWalker 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}[] = [];
outputNames.forEach(name => {
if (!name.whitelist || name.whitelist.css) {
const bracketedName = {replace: `[${name.replace}]`, replaceWith: `[${name.replaceWith}]`};
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
});
});
}
});
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));
}
}
@@ -1,85 +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 {findInputsOnElementWithAttr, findInputsOnElementWithTag} from '../html/angular';
import {inputNames} from '../material/data/input-names';
import {ExternalResource} from '../tslint/component-file';
import {ComponentWalker} from '../tslint/component-walker';
import {findAllSubstringIndices} 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 SwitchTemplateInputNamesWalker(sourceFile, this.getOptions()));
}
}
export class SwitchTemplateInputNamesWalker 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}[] = [];
inputNames.forEach(name => {
let offsets: number[] = [];
if (name.whitelist && name.whitelist.attributes && name.whitelist.attributes.length) {
offsets = offsets.concat(findInputsOnElementWithAttr(
templateContent, name.replace, name.whitelist.attributes));
}
if (name.whitelist && name.whitelist.elements && name.whitelist.elements.length) {
offsets = offsets.concat(findInputsOnElementWithTag(
templateContent, name.replace, name.whitelist.elements));
}
if (!name.whitelist) {
offsets = offsets.concat(findAllSubstringIndices(templateContent, name.replace));
}
this.createReplacementsForOffsets(node, name, offsets).forEach(replacement => {
replacements.push({
message: `Found deprecated @Input() "${red(name.replace)}" which has been renamed to` +
` "${green(name.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));
}
}
@@ -1,85 +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 {findOutputsOnElementWithAttr, findOutputsOnElementWithTag} from '../html/angular';
import {outputNames} from '../material/data/output-names';
import {ExternalResource} from '../tslint/component-file';
import {ComponentWalker} from '../tslint/component-walker';
import {findAllSubstringIndices} 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 SwitchTemplateOutputNamesWalker(sourceFile, this.getOptions()));
}
}
export class SwitchTemplateOutputNamesWalker 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}[] = [];
outputNames.forEach(name => {
let offsets: number[] = [];
if (name.whitelist && name.whitelist.attributes && name.whitelist.attributes.length) {
offsets = offsets.concat(findOutputsOnElementWithAttr(
templateContent, name.replace, name.whitelist.attributes));
}
if (name.whitelist && name.whitelist.elements && name.whitelist.elements.length) {
offsets = offsets.concat(findOutputsOnElementWithTag(
templateContent, name.replace, name.whitelist.elements));
}
if (!name.whitelist) {
offsets = offsets.concat(findAllSubstringIndices(templateContent, name.replace));
}
this.createReplacementsForOffsets(node, name, offsets).forEach(replacement => {
replacements.push({
message: `Found deprecated @Output() "${red(name.replace)}" which has been renamed to` +
` "${green(name.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));
}
}
@@ -16,9 +16,11 @@ describe('test cases', () => {
*/
const testCases = [
'v5/attribute-selectors',
'v5/class-names',
'v5/css-names',
'v5/element-selectors',
'v5/ts-class-names',
'v5/input-names',
'v5/output-names',
];
// Iterates through every test case directory and generates a jasmine test block that will
@@ -0,0 +1,62 @@
import {Component} from '@angular/core';
@Component({
template: `
<ng-template cdkConnectedOverlay
[cdkConnectedOverlayOrigin]="myOrigin"
[cdkConnectedOverlayPositions]="myPositions"
[cdkConnectedOverlayOffsetX]="myOffsetX"
[cdkConnectedOverlayOffsetY]="myOffsetY"
[cdkConnectedOverlayWidth]="myWidth"
[cdkConnectedOverlayHeight]="myHeight"
[cdkConnectedOverlayMinWidth]="myMinWidth"
[cdkConnectedOverlayMinHeight]="myMinHeight"
[cdkConnectedOverlayBackdropClass]="myBackdropClass"
[cdkConnectedOverlayScrollStrategy]="myScrollStrategy"
[cdkConnectedOverlayOpen]="isOpen"
[cdkConnectedOverlayHasBackdrop]="hasBackdrop">
</ng-template>
`
})
class A {}
@Component({
template: `
<mat-radio-group labelPosition="end"></mat-radio-group>
<mat-radio-group [labelPosition]="myAlign">
<mat-radio-button [labelPosition]="myAlign"></mat-radio-button>
<mat-radio-button labelPosition="start"></mat-radio-button>
</mat-radio-group>
`
})
class B {}
@Component({
template: `
<mat-drawer position="end"></mat-drawer>
<mat-drawer [position]="myAlign"></mat-drawer>
<mat-sidenav [position]="myAlign"></mat-sidenav>
`
})
class C {}
@Component({
template: `
<mat-form-field color="primary"></mat-form-field>
<mat-form-field [color]="myColor"></mat-form-field>
<mat-form-field floatLabel="always"></mat-form-field>
<mat-form-field [floatLabel]="floatState"></mat-form-field>
`
})
class D {}
@Component({
template: `
<mat-tab-group [dynamicHeight]="myHeight"></mat-tab-group>
<mat-checkbox labelPosition="end"></mat-checkbox>
<div matTooltip [matTooltipPosition]="end"></div>
<mat-slider [tickInterval]="interval"></mat-slider>
<mat-slider [thumbLabel]="myLabel"></mat-slider>
`
})
class E {}
@@ -0,0 +1,62 @@
import {Component} from '@angular/core';
@Component({
template: `
<ng-template cdkConnectedOverlay
[origin]="myOrigin"
[positions]="myPositions"
[offsetX]="myOffsetX"
[offsetY]="myOffsetY"
[width]="myWidth"
[height]="myHeight"
[minWidth]="myMinWidth"
[minHeight]="myMinHeight"
[backdropClass]="myBackdropClass"
[scrollStrategy]="myScrollStrategy"
[open]="isOpen"
[hasBackdrop]="hasBackdrop">
</ng-template>
`
})
class A {}
@Component({
template: `
<mat-radio-group align="end"></mat-radio-group>
<mat-radio-group [align]="myAlign">
<mat-radio-button [align]="myAlign"></mat-radio-button>
<mat-radio-button align="start"></mat-radio-button>
</mat-radio-group>
`
})
class B {}
@Component({
template: `
<mat-drawer align="end"></mat-drawer>
<mat-drawer [align]="myAlign"></mat-drawer>
<mat-sidenav [align]="myAlign"></mat-sidenav>
`
})
class C {}
@Component({
template: `
<mat-form-field dividerColor="primary"></mat-form-field>
<mat-form-field [dividerColor]="myColor"></mat-form-field>
<mat-form-field floatPlaceholder="always"></mat-form-field>
<mat-form-field [floatPlaceholder]="floatState"></mat-form-field>
`
})
class D {}
@Component({
template: `
<mat-tab-group [mat-dynamic-height]="myHeight"></mat-tab-group>
<mat-checkbox align="end"></mat-checkbox>
<div matTooltip [tooltip-position]="end"></div>
<mat-slider [tick-interval]="interval"></mat-slider>
<mat-slider [thumb-label]="myLabel"></mat-slider>
`
})
class E {}
@@ -0,0 +1,29 @@
import {Component} from '@angular/core';
@Component({
template: `
<mat-select (selectionChange)="onChange($event)"></mat-select>
<mat-select (closed)="onClose($event)"></mat-select>
<mat-select (opened)="onOpen($event)"></mat-select>
`
})
class A {}
@Component({
template: `
<mat-drawer (positionChanged)="onAlignChanged()"></mat-drawer>
<mat-drawer (closed)="onClose()" (opened)="onOpen()"></mat-drawer>
<mat-tab-group (selectedTabChange)="onSelectionChange()"></mat-tab-group>
`
})
class B {}
@Component({
template: `
<mat-chip (removed)="removeFromList()"></mat-chip>
<mat-basic-chip (removed)="removeFromList()"></mat-basic-chip>
<mat-chip (destroyed)="onDestroy()"></mat-chip>
<mat-basic-chip (destroyed)="onDestroy()"></mat-basic-chip>
`
})
class C {}
@@ -0,0 +1,29 @@
import {Component} from '@angular/core';
@Component({
template: `
<mat-select (change)="onChange($event)"></mat-select>
<mat-select (onClose)="onClose($event)"></mat-select>
<mat-select (onOpen)="onOpen($event)"></mat-select>
`
})
class A {}
@Component({
template: `
<mat-drawer (align-changed)="onAlignChanged()"></mat-drawer>
<mat-drawer (close)="onClose()" (open)="onOpen()"></mat-drawer>
<mat-tab-group (selectChange)="onSelectionChange()"></mat-tab-group>
`
})
class B {}
@Component({
template: `
<mat-chip (remove)="removeFromList()"></mat-chip>
<mat-basic-chip (remove)="removeFromList()"></mat-basic-chip>
<mat-chip (destroy)="onDestroy()"></mat-chip>
<mat-basic-chip (destroy)="onDestroy()"></mat-basic-chip>
`
})
class C {}
+9 -4
View File
@@ -31,15 +31,13 @@ export default function(): Rule {
path.join(__dirname, 'rules/attribute-selectors'),
path.join(__dirname, 'rules/class-names'),
path.join(__dirname, 'rules/css-names'),
path.join(__dirname, 'rules/input-names'),
path.join(__dirname, 'rules/output-names'),
],
rules: {
// Automatic fixes.
'switch-property-names': true,
'switch-stylesheet-input-names': true,
'switch-stylesheet-output-names': 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,
@@ -60,6 +58,13 @@ export default function(): Rule {
'element-selectors-stylesheet': true,
'element-selectors-template': true,
// Input name update rules
'input-names-stylesheet': true,
'input-names-template': true,
// Output name update rules
'output-names-template': true,
// Additional issues we can detect but not automatically fix.
'check-class-declaration-misc': true,
'check-import-misc': true,