Skip to content

refactor(schematics): cleanup method check, class inheritance and template misc rules #12850

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 0 additions & 18 deletions src/lib/schematics/update/material/data/export-as-names.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const methodCallChecks = transformChanges<MaterialMethodCallData>([
invalidArgCounts: [
{
count: 2,
message: '"g{{default}}" is now required as a third argument'
message: '"g{{defaults}}" is now required as a third argument'
}
]
}
Expand Down
44 changes: 0 additions & 44 deletions src/lib/schematics/update/rules/checkClassDeclarationMiscRule.ts

This file was deleted.

92 changes: 0 additions & 92 deletions src/lib/schematics/update/rules/checkMethodCallsRule.ts

This file was deleted.

99 changes: 46 additions & 53 deletions src/lib/schematics/update/rules/checkTemplateMiscRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,76 +15,69 @@ 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.
* Rule that walks through every inline or external template and reports if there are outdated
* usages of the Angular Material API that need to be updated manually.
*/
export class Rule extends Rules.AbstractRule {
apply(sourceFile: ts.SourceFile): RuleFailure[] {
return this.applyWithWalker(new CheckTemplateMiscWalker(sourceFile, this.getOptions()));
return this.applyWithWalker(new Walker(sourceFile, this.getOptions()));
}
}

export class CheckTemplateMiscWalker extends ComponentWalker {
export class Walker extends ComponentWalker {

visitInlineTemplate(template: ts.StringLiteral) {
this.checkTemplate(template.getText()).forEach(failure => {
const ruleFailure = new RuleFailure(template.getSourceFile(), failure.start, failure.end,
failure.message, this.getRuleName());
this.addFailure(ruleFailure);
});
this._createFailuresForContent(template, template.getText())
.forEach(data => this.addFailureFromStartToEnd(data.start, data.end, data.message));
}

visitExternalTemplate(template: ExternalResource) {
this.checkTemplate(template.getFullText()).forEach(failure => {
const ruleFailure = new RuleFailure(template, failure.start + 1, failure.end + 1,
failure.message, this.getRuleName());
this.addFailure(ruleFailure);
this._createFailuresForContent(template, template.getFullText()).forEach(data => {
this.addFailure(new RuleFailure(
template, data.start, data.end, data.message, this.getRuleName()));
});
}

/**
* Replaces the outdated name in the template with the new one and returns an updated template.
*/
private checkTemplate(templateContent: string):
{start: number, end: number, message: string}[] {
let failures: {message: string, start: number, end: number}[] = [];
private _createFailuresForContent(node: ts.Node, content: string) {
const failures: {message: string, start: number, end: number}[] = [];

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]')}"`
}))
);
findAllSubstringIndices(content, 'cdk-focus-trap').forEach(offset => {
failures.push({
start: node.getStart() + offset,
end: node.getStart() + 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'])
.map(offset => ({
start: offset,
end: offset + 'selectionChange'.length,
message: `Found deprecated @Output() "${red('selectionChange')}" on` +
` "${bold('mat-list-option')}". Use "${green('selectionChange')}" on` +
` "${bold('mat-selection-list')}" instead`
})));
findOutputsOnElementWithTag(content, 'selectionChange', ['mat-list-option']).forEach(offset => {
failures.push({
start: node.getStart() + offset,
end: node.getStart() + offset + 'selectionChange'.length,
message: `Found deprecated @Output() "${red('selectionChange')}" on ` +
`"${bold('mat-list-option')}". Use "${green('selectionChange')}" on ` +
`"${bold('mat-selection-list')}" instead.`
});
});

failures = failures.concat(
findOutputsOnElementWithTag(templateContent, 'selectedChanged', ['mat-datepicker'])
.map(offset => ({
start: offset,
end: offset + 'selectionChange'.length,
message: `Found deprecated @Output() "${red('selectedChanged')}" on` +
` "${bold('mat-datepicker')}". Use "${green('dateChange')}" or` +
` "${green('dateInput')}" on "${bold('<input [matDatepicker]>')}" instead`
})));
findOutputsOnElementWithTag(content, 'selectedChanged', ['mat-datepicker']).forEach(offset => {
failures.push({
start: node.getStart() + offset,
end: node.getStart() + offset + 'selectionChange'.length,
message: `Found deprecated @Output() "${red('selectedChanged')}" on ` +
`"${bold('mat-datepicker')}". Use "${green('dateChange')}" or ` +
`"${green('dateInput')}" on "${bold('<input [matDatepicker]>')}" instead.`
});
});

failures = failures.concat(
findInputsOnElementWithTag(templateContent, 'selected', ['mat-button-toggle-group'])
.map(offset => ({
start: offset,
end: offset + 'selected'.length,
message: `Found deprecated @Input() "${red('selected')}" on` +
` "${bold('mat-radio-button-group')}". Use "${green('value')}" instead`
})));
findInputsOnElementWithTag(content, 'selected', ['mat-button-toggle-group']).forEach(offset => {
failures.push({
start: node.getStart() + offset,
end: node.getStart() + offset + 'selected'.length,
message: `Found deprecated @Input() "${red('selected')}" on ` +
`"${bold('mat-radio-button-group')}". Use "${green('value')}" instead.`
});
});

return failures;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
import {bold, green, red} from 'chalk';
import {ProgramAwareRuleWalker, RuleFailure, Rules} from 'tslint';
import * as ts from 'typescript';
import {MaterialPropertyNameData, propertyNames} from '../material/data/property-names';
import {MaterialPropertyNameData, propertyNames} from '../../material/data/property-names';
import {determineBaseTypes} from '../../typescript/base-types';

/**
* Map of classes that have been updated. Each class name maps to the according property change
Expand All @@ -34,7 +35,7 @@ export class Rule extends Rules.TypedRule {
export class Walker extends ProgramAwareRuleWalker {

visitClassDeclaration(node: ts.ClassDeclaration) {
const baseTypes = this._determineBaseTypes(node);
const baseTypes = determineBaseTypes(node);

if (!baseTypes) {
return;
Expand All @@ -44,24 +45,11 @@ export class Walker extends ProgramAwareRuleWalker {
const data = changedClassesMap.get(typeName);

if (data) {
this.addFailureAtNode(node,
`Found class "${bold(node.name.text)}" which extends class ` +
`"${bold(typeName)}". Please note that the base class property ` +
`"${red(data.replace)}" has changed to "${green(data.replaceWith)}". ` +
`You may need to update your class as well`);
this.addFailureAtNode(node, `Found class "${bold(node.name.text)}" which extends class ` +
`"${bold(typeName)}". Please note that the base class property ` +
`"${red(data.replace)}" has changed to "${green(data.replaceWith)}". ` +
`You may need to update your class as well`);
}
});
}

private _determineBaseTypes(node: ts.ClassDeclaration): string[] | null {
if (!node.heritageClauses) {
return null;
}

return node.heritageClauses
.reduce((types, clause) => types.concat(clause.types), [])
.map(typeExpression => typeExpression.expression)
.filter(expression => expression && ts.isIdentifier(expression))
.map(identifier => identifier.text);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* @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 {bold, green} from 'chalk';
import {ProgramAwareRuleWalker, RuleFailure, Rules} from 'tslint';
import * as ts from 'typescript';
import {determineBaseTypes} from '../../typescript/base-types';

/**
* Rule that checks for classes that extend Angular Material or CDK classes which have
* changed their API.
*/
export class Rule extends Rules.TypedRule {
applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): RuleFailure[] {
return this.applyWithWalker(new Walker(sourceFile, this.getOptions(), program));
}
}

export class Walker extends ProgramAwareRuleWalker {

visitClassDeclaration(node: ts.ClassDeclaration) {
const baseTypes = determineBaseTypes(node);

if (!baseTypes) {
return;
}

if (baseTypes.includes('MatFormFieldControl')) {
const hasFloatLabelMember = node.members
.find(member => member.name && member.name.getText() === 'shouldFloatLabel');

if (!hasFloatLabelMember) {
this.addFailureAtNode(node, `Found class "${bold(node.name.text)}" which extends ` +
`"${bold('MatFormFieldControl')}". This class must define ` +
`"${green('shouldLabelFloat')}" which is now a required property.`);
}
}
}
}
Loading