Skip to content

Commit e08d5cc

Browse files
committed
Migrate SASS easing functions
1 parent 187f18c commit e08d5cc

File tree

5 files changed

+404
-187
lines changed

5 files changed

+404
-187
lines changed

polaris-migrator/src/migrations/replace-sass-motion/replace-sass-motion.ts

Lines changed: 119 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import type {FileInfo, API, Options} from 'jscodeshift';
22
import postcss, {Plugin, Declaration, Helpers} from 'postcss';
3-
import valueParser, {ParsedValue, Node} from 'postcss-value-parser';
3+
import valueParser, {
4+
ParsedValue,
5+
Node,
6+
FunctionNode,
7+
} from 'postcss-value-parser';
48

59
import {POLARIS_MIGRATOR_COMMENT} from '../../constants';
610
import {
@@ -15,6 +19,7 @@ import {isKeyOf} from '../../utilities/type-guards';
1519

1620
const processed = Symbol('processed');
1721
const DEFAULT_DURATION = 'base';
22+
const DEFAULT_FUNCTION = 'base';
1823

1924
const durationFuncMap = {
2025
none: '--p-duration-0',
@@ -52,6 +57,36 @@ const durationConstantsMap = {
5257
'5s': '--p-duration-5000',
5358
};
5459

60+
const easingFuncMap = {
61+
base: '--p-ease',
62+
in: '--p-ease-in',
63+
out: '--p-ease-out',
64+
};
65+
66+
const easingFuncConstantsMap = {
67+
linear: '--p-linear',
68+
ease: '--p-ease',
69+
'ease-in': '--p-ease-in',
70+
'ease-out': '--p-ease-out',
71+
'ease-in-out': '--p-ease-in-out',
72+
};
73+
74+
const deprecatedEasingFuncs = ['anticipate', 'excite', 'overshoot'];
75+
76+
// Per the spec for transition easing functions:
77+
// https://w3c.github.io/csswg-drafts/css-easing/#easing-functions
78+
const cssEasingBuiltinFuncs = [
79+
'linear',
80+
'ease',
81+
'ease-in',
82+
'ease-out',
83+
'ease-in-out',
84+
'cubic-bezier',
85+
'step-start',
86+
'step-end',
87+
'steps',
88+
];
89+
5590
function normaliseStringifiedNumber(number: string): string {
5691
return Number(number).toString();
5792
}
@@ -99,11 +134,52 @@ function withParsedValue(
99134
}) as DeclarationProcessor;
100135
}
101136

137+
function insertUnexpectedEasingFunctionComment(
138+
node: Node,
139+
decl: ParsedValueDeclaration,
140+
) {
141+
decl.before(
142+
postcss.comment({
143+
text: `${POLARIS_MIGRATOR_COMMENT} Unexpected easing function '${node.value}'. See https://polaris.shopify.com/tokens/motion for possible values.`,
144+
}),
145+
);
146+
}
147+
148+
function migrateLegacySassEasingFunction(
149+
node: FunctionNode,
150+
decl: ParsedValueDeclaration,
151+
) {
152+
const easingFunc = node.nodes[0]?.value ?? DEFAULT_FUNCTION;
153+
154+
if (isKeyOf(easingFuncMap, easingFunc)) {
155+
const easingCustomProperty = easingFuncMap[easingFunc];
156+
setNodeValue(node, `var(${easingCustomProperty})`);
157+
} else {
158+
const comment = deprecatedEasingFuncs.includes(easingFunc)
159+
? `The ${easingFunc} easing function is no longer available in Polaris. See https://polaris.shopify.com/tokens/motion for possible values.`
160+
: `Unexpected easing function '${easingFunc}'.`;
161+
decl.before(
162+
postcss.comment({
163+
text: `${POLARIS_MIGRATOR_COMMENT} ${comment}`,
164+
}),
165+
);
166+
}
167+
}
168+
102169
interface PluginOptions extends Options, NamespaceOptions {}
103170

104171
const plugin = (options: PluginOptions = {}): Plugin => {
105172
const durationFunc = namespace('duration', options);
106173

174+
const easingFuncHandlers = {
175+
[namespace('easing', options)]: migrateLegacySassEasingFunction,
176+
// Per the spec, these can all be functions:
177+
// https://w3c.github.io/csswg-drafts/css-easing/#easing-functions
178+
linear: insertUnexpectedEasingFunctionComment,
179+
'cubic-bezier': insertUnexpectedEasingFunctionComment,
180+
steps: insertUnexpectedEasingFunctionComment,
181+
};
182+
107183
function mutateTransitionDurationValue(
108184
node: Node,
109185
decl: ParsedValueDeclaration,
@@ -153,6 +229,32 @@ const plugin = (options: PluginOptions = {}): Plugin => {
153229
return false;
154230
}
155231

232+
function mutateTransitionFunctionValue(
233+
node: Node,
234+
decl: ParsedValueDeclaration,
235+
): boolean {
236+
if (isPolarisVar(node)) {
237+
return true;
238+
}
239+
240+
if (node.type === 'function' && isKeyOf(easingFuncHandlers, node.value)) {
241+
easingFuncHandlers[node.value](node, decl);
242+
return true;
243+
}
244+
245+
if (node.type === 'word') {
246+
if (isKeyOf(easingFuncConstantsMap, node.value)) {
247+
setNodeValue(node, `var(${easingFuncConstantsMap[node.value]})`);
248+
return true;
249+
} else if (cssEasingBuiltinFuncs.includes(node.value)) {
250+
insertUnexpectedEasingFunctionComment(node, decl);
251+
return true;
252+
}
253+
}
254+
255+
return false;
256+
}
257+
156258
function mutateTransitionDelayValue(
157259
node: Node,
158260
decl: ParsedValueDeclaration,
@@ -200,7 +302,10 @@ const plugin = (options: PluginOptions = {}): Plugin => {
200302
if (isTransformableDuration(unit) || isSassFunction(durationFunc, node)) {
201303
timings.push(node);
202304
} else {
203-
// TODO: Try process it as an easing function
305+
// This node could be either the property to animate, or an easing
306+
// function. We try mutate the easing function, but if not we assume
307+
// it's the property to animate and therefore do not leave a comment.
308+
mutateTransitionFunctionValue(node, decl);
204309
}
205310
});
206311

@@ -270,6 +375,18 @@ const plugin = (options: PluginOptions = {}): Plugin => {
270375
});
271376
}),
272377

378+
'transition-timing-function': withParsedValue((decl) => {
379+
decl.parsedValue.nodes.forEach((node) => {
380+
if (!mutateTransitionFunctionValue(node, decl)) {
381+
decl.before(
382+
postcss.comment({
383+
text: POLARIS_MIGRATOR_COMMENT,
384+
}),
385+
);
386+
}
387+
});
388+
}),
389+
273390
transition: withParsedValue((decl) => {
274391
if (hasNumericOperator(decl.parsedValue)) {
275392
decl.before(

0 commit comments

Comments
 (0)