Skip to content

Commit 9c7986b

Browse files
committed
Auto migrate non-parseable expressions
1 parent 47419cc commit 9c7986b

File tree

3 files changed

+78
-28
lines changed

3 files changed

+78
-28
lines changed

src/lib/schematics/update/rules/ripple-v7/ripple-speed-factor.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,15 @@ export function convertSpeedFactorToDuration(factor: number) {
1212
// now calculate the exact `enterDuration`. 450ms is the enter duration without factor.
1313
return 450 / (factor || 1);
1414
}
15+
16+
/**
17+
* Creates a runtime TypeScript expression that can be used in order to calculate the duration
18+
* from the speed factor expression that couldn't be statically analyzed.
19+
*
20+
* @param speedFactorValue Speed factor expression that couldn't be statically analyzed.
21+
*/
22+
export function createSpeedFactorConvertExpression(speedFactorValue: string): string {
23+
// To be sure that the speed factor value expression is calculated properly, we need to add
24+
// the according parenthesis.
25+
return `450 / (${speedFactorValue})`;
26+
}

src/lib/schematics/update/rules/ripple-v7/switchRippleSpeedFactorRule.ts

Lines changed: 56 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,18 @@
88

99
import {ProgramAwareRuleWalker, RuleFailure, Rules} from 'tslint';
1010
import * as ts from 'typescript';
11-
import {bold, red, green} from 'chalk';
12-
import {convertSpeedFactorToDuration} from './ripple-speed-factor';
11+
import {bold, red} from 'chalk';
12+
import {
13+
convertSpeedFactorToDuration,
14+
createSpeedFactorConvertExpression,
15+
} from './ripple-speed-factor';
16+
17+
/**
18+
* Note that will be added whenever a speed factor expression has been converted to calculate
19+
* the according duration. This note should encourage people to clean up their code by switching
20+
* away from the speed factors to explicit durations.
21+
*/
22+
const removeNote = `TODO: Cleanup duration calculation.`;
1323

1424
/**
1525
* Rule that walks through property assignment and switches the global `baseSpeedFactor`
@@ -19,7 +29,7 @@ import {convertSpeedFactorToDuration} from './ripple-speed-factor';
1929
export class Rule extends Rules.TypedRule {
2030
applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): RuleFailure[] {
2131
return this.applyWithWalker(
22-
new SwitchRippleSpeedFactorRule(sourceFile, this.getOptions(), program));
32+
new SwitchRippleSpeedFactorRule(sourceFile, this.getOptions(), program));
2333
}
2434
}
2535

@@ -53,21 +63,32 @@ export class SwitchRippleSpeedFactorRule extends ProgramAwareRuleWalker {
5363
const propertyNameReplacement = this.createReplacement(leftExpression.name.getStart(),
5464
leftExpression.name.getWidth(), 'animation');
5565

66+
// Replace the value assignment with the new animation config.
5667
const rightExpressionReplacement = this.createReplacement(expression.right.getStart(),
5768
expression.right.getWidth(), `{enterDuration: ${newEnterDurationValue}}`);
5869

5970
this.addFailureAtNode(
6071
expression,
61-
`Found deprecated member assignment for "${bold('MatRipple')}#${red('speedFactor')}"`,
72+
`Found deprecated variable assignment for "${bold('MatRipple')}#${red('speedFactor')}"`,
6273
[propertyNameReplacement, rightExpressionReplacement]);
6374
} else {
64-
// In case the speed factor is dynamically calculated or passed to the assignment, we just
65-
// print the failure and notify about the breaking change.
75+
// Handle the right expression differently if the previous speed factor value can't
76+
// be resolved statically. In that case, we just create a TypeScript expression that
77+
// calculates the explicit duration based on the non-static speed factor expression.
78+
const newExpression = createSpeedFactorConvertExpression(expression.right.getText());
79+
80+
// Replace the `speedFactor` property name with `animation`.
81+
const propertyNameReplacement = this.createReplacement(leftExpression.name.getStart(),
82+
leftExpression.name.getWidth(), 'animation');
83+
84+
// Replace the value assignment with the new animation config and remove todo.
85+
const rightExpressionReplacement = this.createReplacement(expression.right.getStart(),
86+
expression.right.getWidth(), `/** ${removeNote} */ {enterDuration: ${newExpression}}`);
87+
6688
this.addFailureAtNode(
67-
expression, `Found deprecated member assignment for "${bold('MatRipple')}#` +
68-
`${red('speedFactor')}. Please manually switch from "${red('speedFactor')}" to ` +
69-
`"${green('animation')}". Note that the animation property only accepts explicit ` +
70-
`durations in milliseconds.`);
89+
expression,
90+
`Found deprecated variable assignment for "${bold('MatRipple')}#${red('speedFactor')}"`,
91+
[propertyNameReplacement, rightExpressionReplacement]);
7192
}
7293
}
7394
}
@@ -94,29 +115,42 @@ export class SwitchRippleSpeedFactorRule extends ProgramAwareRuleWalker {
94115
// immediately in the provider object (e.g. it can happen that someone just imports the
95116
// config from a separate file).
96117

97-
if (assignment.initializer.kind === ts.SyntaxKind.NumericLiteral) {
98-
const numericValue = parseFloat((assignment.initializer as ts.NumericLiteral).text);
118+
const {initializer, name} = assignment;
119+
120+
if (initializer.kind === ts.SyntaxKind.NumericLiteral) {
121+
const numericValue = parseFloat((initializer as ts.NumericLiteral).text);
99122
const newEnterDurationValue = convertSpeedFactorToDuration(numericValue);
100123

101-
const keyNameReplacement = this.createReplacement(assignment.name.getStart(),
124+
const keyNameReplacement = this.createReplacement(name.getStart(),
102125
assignment.name.getWidth(), `animation`);
103126

104-
const initializerReplacement = this.createReplacement(assignment.initializer.getStart(),
105-
assignment.initializer.getWidth(), `{enterDuration: ${newEnterDurationValue}}`);
127+
const initializerReplacement = this.createReplacement(initializer.getStart(),
128+
initializer.getWidth(), `{enterDuration: ${newEnterDurationValue}}`);
106129

107130
this.addFailureAtNode(
108131
assignment,
109-
`Found deprecated property assignment for "${bold('MAT_RIPPLE_GLOBAL_OPTIONS')} -> ` +
132+
`Found deprecated property assignment for "${bold('MAT_RIPPLE_GLOBAL_OPTIONS')}:` +
110133
`${red('baseSpeedFactor')}"`,
111134
[keyNameReplacement, initializerReplacement]);
112135
} else {
113-
// In case the base speed factor is dynamically calculated and inside of an Angular provider,
114-
// we just print the failure and notify about the breaking change.
136+
// Handle the right expression differently if the previous speed factor value can't
137+
// be resolved statically. In that case, we just create a TypeScript expression that
138+
// calculates the explicit duration based on the non-static speed factor expression.
139+
const newExpression = createSpeedFactorConvertExpression(initializer.getText());
140+
141+
// Replace the `baseSpeedFactor` property name with `animation`.
142+
const propertyNameReplacement = this.createReplacement(name.getStart(),
143+
name.getWidth(), 'animation');
144+
145+
// Replace the value assignment with the new animation config and remove todo.
146+
const rightExpressionReplacement = this.createReplacement(initializer.getStart(),
147+
initializer.getWidth(), `/** ${removeNote} */ {enterDuration: ${newExpression}}`);
148+
115149
this.addFailureAtNode(
116-
assignment, `Found a deprecated property assignment for ` +
117-
`"${bold('MAT_RIPPLE_GLOBAL_OPTIONS')} -> ${red('speedFactor')}. Please manually switch ` +
118-
`from "${red('baseSpeedFactor')}" to "${green('animation')}". Note that the animation ` +
119-
`property only accepts explicit durations in milliseconds.`);
150+
assignment,
151+
`Found a deprecated property assignment for "${bold('MAT_RIPPLE_GLOBAL_OPTIONS')}:` +
152+
`${red('baseSpeedFactor')}.`,
153+
[propertyNameReplacement, rightExpressionReplacement]);
120154
}
121155
}
122156
}

src/lib/schematics/update/rules/ripple-v7/switchRippleSpeedFactorTemplateRule.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88

99
import {RuleFailure, Rules} from 'tslint';
1010
import * as ts from 'typescript';
11-
import {convertSpeedFactorToDuration} from './ripple-speed-factor';
11+
import {
12+
convertSpeedFactorToDuration,
13+
createSpeedFactorConvertExpression,
14+
} from './ripple-speed-factor';
1215
import {ExternalResource} from '../../tslint/component-file';
1316
import {ComponentWalker} from '../../tslint/component-walker';
14-
import {red, green} from 'chalk';
1517

1618
/** Regular expression that matches [matRippleSpeedFactor]="$NUMBER" in templates. */
1719
const speedFactorNumberRegex = /\[matRippleSpeedFactor]="(\d+(?:\.\d+)?)"/g;
@@ -26,7 +28,7 @@ const speedFactorNotParseable = /\[matRippleSpeedFactor]="(?!\d+(?:\.\d+)?")(.*)
2628
export class Rule extends Rules.AbstractRule {
2729
apply(sourceFile: ts.SourceFile): RuleFailure[] {
2830
return this.applyWithWalker(
29-
new SwitchRippleSpeedFactorTemplateRule(sourceFile, this.getOptions()));
31+
new SwitchRippleSpeedFactorTemplateRule(sourceFile, this.getOptions()));
3032
}
3133
}
3234

@@ -56,10 +58,12 @@ export class SwitchRippleSpeedFactorTemplateRule extends ComponentWalker {
5658
}
5759

5860
while ((match = speedFactorNotParseable.exec(templateText)) !== null) {
61+
const newDurationExpression = createSpeedFactorConvertExpression(match[1]);
62+
const fix = this.createReplacement(startPos + match.index!, match[0].length,
63+
`[matRippleAnimation]="{enterDuration: (${newDurationExpression})}"`);
64+
5965
this.addFailureAt(startPos + match.index!, match[0].length,
60-
`Detected deprecated ${red('[matRippleSpeedFactor]')} input binding with non-parseable ` +
61-
`value. Please switch manually to ${green('[matRippleAnimation]')}. Note that the ` +
62-
`animation config only accepts explicit durations in milliseconds.`);
66+
'Detected deprecated [matRippleSpeedFactor] input binding with non-parseable value.', fix);
6367
}
6468
}
6569
}

0 commit comments

Comments
 (0)