blob: 64cdb917f3891cc7db3e404fec35fb76e0190963 [file] [log] [blame]
"use strict";
/**
* @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
*/
Object.defineProperty(exports, "__esModule", { value: true });
const schematics_1 = require("@angular/cdk/schematics");
const ts = require("typescript");
const ripple_speed_factor_1 = require("./ripple-speed-factor");
/** Regular expression that matches [matRippleSpeedFactor]="$NUMBER" in templates. */
const speedFactorNumberRegex = /\[matRippleSpeedFactor]="(\d+(?:\.\d+)?)"/g;
/** Regular expression that matches [matRippleSpeedFactor]="$NOT_A_NUMBER" in templates. */
const speedFactorNotParseable = /\[matRippleSpeedFactor]="(?!\d+(?:\.\d+)?")(.*)"/g;
/**
* Note that will be added whenever a speed factor expression has been converted to calculate
* the according duration. This note should encourage people to clean up their code by switching
* away from the speed factors to explicit durations.
*/
const removeNote = `TODO: Cleanup duration calculation.`;
/**
* Rule that walks through every property assignment and switches the global `baseSpeedFactor`
* ripple option to the new global animation config. Also updates every class member assignment
* that refers to MatRipple#speedFactor.
*/
class RippleSpeedFactorRule extends schematics_1.MigrationRule {
constructor() {
super(...arguments);
// Only enable this rule if the migration targets version 7 as the ripple
// speed factor has been removed in that version.
this.ruleEnabled = this.targetVersion === schematics_1.TargetVersion.V7;
}
visitNode(node) {
if (ts.isBinaryExpression(node)) {
this._visitBinaryExpression(node);
}
else if (ts.isPropertyAssignment(node)) {
this._visitPropertyAssignment(node);
}
}
visitTemplate(template) {
let match;
while ((match = speedFactorNumberRegex.exec(template.content)) !== null) {
const newEnterDuration = ripple_speed_factor_1.convertSpeedFactorToDuration(parseFloat(match[1]));
this._replaceText(template.filePath, template.start + match.index, match[0].length, `[matRippleAnimation]="{enterDuration: ${newEnterDuration}}"`);
}
while ((match = speedFactorNotParseable.exec(template.content)) !== null) {
const newDurationExpression = ripple_speed_factor_1.createSpeedFactorConvertExpression(match[1]);
this._replaceText(template.filePath, template.start + match.index, match[0].length, `[matRippleAnimation]="{enterDuration: (${newDurationExpression})}"`);
}
}
/** Switches binary expressions (e.g. myRipple.speedFactor = 0.5) to the new animation config. */
_visitBinaryExpression(expression) {
if (!ts.isPropertyAccessExpression(expression.left)) {
return;
}
// Left side expression consists of target object and property name (e.g. myInstance.val)
const leftExpression = expression.left;
const targetTypeNode = this.typeChecker.getTypeAtLocation(leftExpression.expression);
if (!targetTypeNode.symbol) {
return;
}
const targetTypeName = targetTypeNode.symbol.getName();
const propertyName = leftExpression.name.getText();
const filePath = leftExpression.getSourceFile().fileName;
if (targetTypeName === 'MatRipple' && propertyName === 'speedFactor') {
if (ts.isNumericLiteral(expression.right)) {
const numericValue = parseFloat(expression.right.text);
const newEnterDurationValue = ripple_speed_factor_1.convertSpeedFactorToDuration(numericValue);
// Replace the `speedFactor` property name with `animation`.
this._replaceText(filePath, leftExpression.name.getStart(), leftExpression.name.getWidth(), 'animation');
// Replace the value assignment with the new animation config.
this._replaceText(filePath, expression.right.getStart(), expression.right.getWidth(), `{enterDuration: ${newEnterDurationValue}}`);
}
else {
// Handle the right expression differently if the previous speed factor value can't
// be resolved statically. In that case, we just create a TypeScript expression that
// calculates the explicit duration based on the non-static speed factor expression.
const newExpression = ripple_speed_factor_1.createSpeedFactorConvertExpression(expression.right.getText());
// Replace the `speedFactor` property name with `animation`.
this._replaceText(filePath, leftExpression.name.getStart(), leftExpression.name.getWidth(), 'animation');
// Replace the value assignment with the new animation config and remove TODO.
this._replaceText(filePath, expression.right.getStart(), expression.right.getWidth(), `/** ${removeNote} */ {enterDuration: ${newExpression}}`);
}
}
}
/**
* Switches the global option `baseSpeedFactor` to the new animation config. For this
* we assume that the `baseSpeedFactor` is not used in combination with individual
* speed factors.
*/
_visitPropertyAssignment(assignment) {
// For switching the `baseSpeedFactor` global option we expect the property assignment
// to be inside of a normal object literal. Custom ripple global options cannot be
// witched automatically.
if (!ts.isObjectLiteralExpression(assignment.parent)) {
return;
}
// The assignment consists of a name (key) and initializer (value).
if (assignment.name.getText() !== 'baseSpeedFactor') {
return;
}
// We could technically lazily check for the MAT_RIPPLE_GLOBAL_OPTIONS injection token to
// be present, but it's not right to assume that everyone sets the ripple global options
// immediately in the provider object (e.g. it can happen that someone just imports the
// config from a separate file).
const { initializer, name } = assignment;
const filePath = assignment.getSourceFile().fileName;
if (ts.isNumericLiteral(initializer)) {
const numericValue = parseFloat(initializer.text);
const newEnterDurationValue = ripple_speed_factor_1.convertSpeedFactorToDuration(numericValue);
// Replace the `baseSpeedFactor` property name with `animation`.
this._replaceText(filePath, name.getStart(), name.getWidth(), 'animation');
// Replace the value assignment initializer with the new animation config.
this._replaceText(filePath, initializer.getStart(), initializer.getWidth(), `{enterDuration: ${newEnterDurationValue}}`);
}
else {
// Handle the right expression differently if the previous speed factor value can't
// be resolved statically. In that case, we just create a TypeScript expression that
// calculates the explicit duration based on the non-static speed factor expression.
const newExpression = ripple_speed_factor_1.createSpeedFactorConvertExpression(initializer.getText());
// Replace the `baseSpeedFactor` property name with `animation`.
this._replaceText(filePath, name.getStart(), name.getWidth(), 'animation');
// Replace the value assignment with the new animation config and remove TODO.
this._replaceText(filePath, initializer.getStart(), initializer.getWidth(), `/** ${removeNote} */ {enterDuration: ${newExpression}}`);
}
}
_replaceText(filePath, start, width, newText) {
const recorder = this.getUpdateRecorder(filePath);
recorder.remove(start, width);
recorder.insertRight(start, newText);
}
}
exports.RippleSpeedFactorRule = RippleSpeedFactorRule;
//# sourceMappingURL=ripple-speed-factor-rule.js.map