@atlaskit/eslint-plugin-platform 2.9.2 → 2.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/cjs/index.js +9 -1
- package/dist/cjs/rules/compiled/expand-motion-shorthand/index.js +281 -0
- package/dist/cjs/rules/compiled/use-motion-token-values/index.js +506 -0
- package/dist/cjs/rules/editor-example-type-import-required/index.js +321 -0
- package/dist/es2019/index.js +9 -1
- package/dist/es2019/rules/compiled/expand-motion-shorthand/index.js +239 -0
- package/dist/es2019/rules/compiled/use-motion-token-values/index.js +444 -0
- package/dist/es2019/rules/editor-example-type-import-required/index.js +286 -0
- package/dist/esm/index.js +9 -1
- package/dist/esm/rules/compiled/expand-motion-shorthand/index.js +275 -0
- package/dist/esm/rules/compiled/use-motion-token-values/index.js +499 -0
- package/dist/esm/rules/editor-example-type-import-required/index.js +314 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/rules/compiled/expand-motion-shorthand/index.d.ts +3 -0
- package/dist/types/rules/compiled/use-motion-token-values/index.d.ts +3 -0
- package/dist/types/rules/editor-example-type-import-required/index.d.ts +4 -0
- package/dist/types-ts4.5/index.d.ts +4 -0
- package/dist/types-ts4.5/rules/compiled/expand-motion-shorthand/index.d.ts +3 -0
- package/dist/types-ts4.5/rules/compiled/use-motion-token-values/index.d.ts +3 -0
- package/dist/types-ts4.5/rules/editor-example-type-import-required/index.d.ts +4 -0
- package/package.json +2 -1
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
import tokenDefaultValues from '@atlaskit/tokens/token-default-values';
|
|
2
|
+
const DURATION_TOKEN_NAMES = ['motion.duration.instant', 'motion.duration.xxshort', 'motion.duration.xshort', 'motion.duration.short', 'motion.duration.medium', 'motion.duration.long', 'motion.duration.xlong', 'motion.duration.xxlong'];
|
|
3
|
+
function parseDurationMs(value) {
|
|
4
|
+
const ms = value.match(/^(\d+(?:\.\d+)?)ms$/);
|
|
5
|
+
if (ms) {
|
|
6
|
+
return parseFloat(ms[1]);
|
|
7
|
+
}
|
|
8
|
+
const s = value.match(/^(\d+(?:\.\d+)?)s$/);
|
|
9
|
+
if (s) {
|
|
10
|
+
return parseFloat(s[1]) * 1000;
|
|
11
|
+
}
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
const DURATION_TOKENS = DURATION_TOKEN_NAMES.map(name => {
|
|
15
|
+
const rawValue = tokenDefaultValues[name];
|
|
16
|
+
const ms = parseDurationMs(rawValue);
|
|
17
|
+
if (ms === null) {
|
|
18
|
+
throw new Error(`use-motion-token-values: could not parse duration for token ${name}: ${rawValue}`);
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
ms,
|
|
22
|
+
token: name
|
|
23
|
+
};
|
|
24
|
+
}).sort((a, b) => a.ms - b.ms);
|
|
25
|
+
const EASING_TOKEN_NAMES = ['motion.easing.in.practical', 'motion.easing.inout.bold', 'motion.easing.out.practical', 'motion.easing.out.bold'];
|
|
26
|
+
function parseCubicBezierParams(value) {
|
|
27
|
+
const match = value.match(/^cubic-bezier\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*\)$/);
|
|
28
|
+
if (!match) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return [parseFloat(match[1]), parseFloat(match[2]), parseFloat(match[3]), parseFloat(match[4])];
|
|
32
|
+
}
|
|
33
|
+
const EASING_TOKENS = EASING_TOKEN_NAMES.map(name => {
|
|
34
|
+
const rawValue = tokenDefaultValues[name];
|
|
35
|
+
const params = parseCubicBezierParams(rawValue);
|
|
36
|
+
if (!params) {
|
|
37
|
+
throw new Error(`use-motion-token-values: could not parse cubic-bezier for token ${name}: ${rawValue}`);
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
value: rawValue,
|
|
41
|
+
token: name,
|
|
42
|
+
params
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Splits on top-level commas (outside function parens) — preserves cubic-bezier(...) commas.
|
|
47
|
+
function splitOnTopLevelCommas(value) {
|
|
48
|
+
const parts = [];
|
|
49
|
+
let depth = 0;
|
|
50
|
+
let current = '';
|
|
51
|
+
for (const ch of value) {
|
|
52
|
+
if (ch === '(') {
|
|
53
|
+
depth++;
|
|
54
|
+
current += ch;
|
|
55
|
+
} else if (ch === ')') {
|
|
56
|
+
depth--;
|
|
57
|
+
current += ch;
|
|
58
|
+
} else if (ch === ',' && depth === 0) {
|
|
59
|
+
parts.push(current.trim());
|
|
60
|
+
current = '';
|
|
61
|
+
} else {
|
|
62
|
+
current += ch;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (current.trim().length > 0) {
|
|
66
|
+
parts.push(current.trim());
|
|
67
|
+
}
|
|
68
|
+
return parts;
|
|
69
|
+
}
|
|
70
|
+
const DURATION_PROPERTIES = new Set(['transitionDuration', 'animationDuration']);
|
|
71
|
+
const EASING_PROPERTIES = new Set(['transitionTimingFunction', 'animationTimingFunction']);
|
|
72
|
+
|
|
73
|
+
// Explicit semantic mappings for CSS keyword easings to motion tokens.
|
|
74
|
+
// Pinned by design intent, confirmed with design system team (Alex + Akshay).
|
|
75
|
+
const CSS_KEYWORD_EASING_TOKEN_MAP = {
|
|
76
|
+
ease: 'motion.easing.out.practical',
|
|
77
|
+
'ease-out': 'motion.easing.out.practical',
|
|
78
|
+
'ease-in': 'motion.easing.in.practical',
|
|
79
|
+
'ease-in-out': 'motion.easing.inout.bold'
|
|
80
|
+
// linear (0,0,1,1) — warn only, no autofix (per Akshay: too generic, no good token match)
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// Non-curve easing values with no meaningful cubic-bezier representation — skip entirely
|
|
84
|
+
const SKIP_EASING_VALUES = new Set(['step-start', 'step-end', 'inherit', 'initial', 'unset', 'none']);
|
|
85
|
+
function euclideanDistance(a, b) {
|
|
86
|
+
return Math.sqrt(a.reduce((sum, val, i) => sum + Math.pow(val - b[i], 2), 0));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Maximum Euclidean distance for easing autofix — beyond this threshold, we report-only
|
|
90
|
+
const EASING_AUTOFIX_THRESHOLD = 0.5;
|
|
91
|
+
function findClosestEasingToken(params) {
|
|
92
|
+
let minDist = Infinity;
|
|
93
|
+
let closest = EASING_TOKENS[0];
|
|
94
|
+
for (const entry of EASING_TOKENS) {
|
|
95
|
+
const dist = euclideanDistance(params, entry.params);
|
|
96
|
+
if (dist < minDist) {
|
|
97
|
+
minDist = dist;
|
|
98
|
+
closest = entry;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (minDist > EASING_AUTOFIX_THRESHOLD) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
token: closest.token,
|
|
106
|
+
value: closest.value,
|
|
107
|
+
dist: minDist
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function findClosestDurationTokens(ms) {
|
|
111
|
+
const exact = DURATION_TOKENS.find(t => t.ms === ms);
|
|
112
|
+
if (exact) {
|
|
113
|
+
return [exact];
|
|
114
|
+
}
|
|
115
|
+
let minDist = Infinity;
|
|
116
|
+
for (const entry of DURATION_TOKENS) {
|
|
117
|
+
const dist = Math.abs(entry.ms - ms);
|
|
118
|
+
if (dist < minDist) {
|
|
119
|
+
minDist = dist;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const closest = DURATION_TOKENS.filter(t => Math.abs(t.ms - ms) === minDist);
|
|
123
|
+
return closest;
|
|
124
|
+
}
|
|
125
|
+
export const useMotionTokenValues = {
|
|
126
|
+
meta: {
|
|
127
|
+
type: 'suggestion',
|
|
128
|
+
fixable: 'code',
|
|
129
|
+
docs: {
|
|
130
|
+
url: 'https://bitbucket.org/atlassian/atlassian-frontend-monorepo/src/master/platform/packages/platform/eslint-plugin/src/rules/compiled/use-motion-token-values/'
|
|
131
|
+
},
|
|
132
|
+
messages: {
|
|
133
|
+
useMotionDurationToken: "Use a motion duration token instead of the hard-coded value '{{ value }}'. Replace with {{ suggestion }}.",
|
|
134
|
+
useMotionDurationTokenNearest: "No exact token match for '{{ value }}'. Nearest: {{ suggestion1 }} or {{ suggestion2 }}.",
|
|
135
|
+
useMotionEasingToken: "Use a motion easing token instead of the hard-coded value '{{ value }}'. Replace with {{ suggestion }}.",
|
|
136
|
+
useMotionEasingTokenUnknown: "Use a motion easing token from @atlaskit/tokens instead of the hard-coded value '{{ value }}'."
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
create(context) {
|
|
140
|
+
let tokensImportNode = null;
|
|
141
|
+
let hasTokenSpecifier = false;
|
|
142
|
+
function buildTokenCall(tokenName, fallback) {
|
|
143
|
+
return `token('${tokenName}', '${fallback}')`;
|
|
144
|
+
}
|
|
145
|
+
function getImportFix(fixer) {
|
|
146
|
+
var _context$sourceCode;
|
|
147
|
+
if (hasTokenSpecifier) {
|
|
148
|
+
return [];
|
|
149
|
+
}
|
|
150
|
+
if (tokensImportNode) {
|
|
151
|
+
// @atlaskit/tokens is imported but without `token` — add `token` to existing import
|
|
152
|
+
const lastSpecifier = tokensImportNode.specifiers[tokensImportNode.specifiers.length - 1];
|
|
153
|
+
if (lastSpecifier) {
|
|
154
|
+
return [fixer.insertTextAfter(lastSpecifier, ', token')];
|
|
155
|
+
}
|
|
156
|
+
// Empty import — replace the whole declaration
|
|
157
|
+
return [fixer.replaceText(tokensImportNode, `import { token } from '@atlaskit/tokens';`)];
|
|
158
|
+
}
|
|
159
|
+
const sourceCode = (_context$sourceCode = context.sourceCode) !== null && _context$sourceCode !== void 0 ? _context$sourceCode : context.getSourceCode();
|
|
160
|
+
const programBody = sourceCode.ast.body;
|
|
161
|
+
// Insert after the last existing import, or at top if no imports exist
|
|
162
|
+
const lastImport = [...programBody].reverse().find(n => n.type === 'ImportDeclaration');
|
|
163
|
+
if (lastImport) {
|
|
164
|
+
return [fixer.insertTextAfter(lastImport, `\nimport { token } from '@atlaskit/tokens';`)];
|
|
165
|
+
}
|
|
166
|
+
if (programBody.length > 0) {
|
|
167
|
+
return [fixer.insertTextBefore(programBody[0], `import { token } from '@atlaskit/tokens';\n`)];
|
|
168
|
+
}
|
|
169
|
+
return [];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Returns autofix string for a single duration value, or null if ambiguous (equidistant)
|
|
173
|
+
function resolveDurationToken(value) {
|
|
174
|
+
const ms = parseDurationMs(value);
|
|
175
|
+
if (ms === null) {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
const result = findClosestDurationTokens(ms);
|
|
179
|
+
if (result.length === 1) {
|
|
180
|
+
return buildTokenCall(result[0].token, value);
|
|
181
|
+
}
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
function handleDurationProperty(node, rawValue) {
|
|
185
|
+
const segments = splitOnTopLevelCommas(rawValue);
|
|
186
|
+
|
|
187
|
+
// Single value path keeps the existing equidistant message
|
|
188
|
+
if (segments.length === 1) {
|
|
189
|
+
const ms = parseDurationMs(rawValue);
|
|
190
|
+
if (ms === null) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const result = findClosestDurationTokens(ms);
|
|
194
|
+
if (result.length === 1) {
|
|
195
|
+
const suggestion = buildTokenCall(result[0].token, rawValue);
|
|
196
|
+
context.report({
|
|
197
|
+
node,
|
|
198
|
+
messageId: 'useMotionDurationToken',
|
|
199
|
+
data: {
|
|
200
|
+
value: rawValue,
|
|
201
|
+
suggestion
|
|
202
|
+
},
|
|
203
|
+
fix(fixer) {
|
|
204
|
+
return [...getImportFix(fixer), fixer.replaceText(node.value, suggestion)];
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
} else {
|
|
208
|
+
const suggestion1 = buildTokenCall(result[0].token, rawValue);
|
|
209
|
+
const suggestion2 = buildTokenCall(result[1].token, rawValue);
|
|
210
|
+
context.report({
|
|
211
|
+
node,
|
|
212
|
+
messageId: 'useMotionDurationTokenNearest',
|
|
213
|
+
data: {
|
|
214
|
+
value: rawValue,
|
|
215
|
+
suggestion1: `${suggestion1} (${result[0].ms}ms)`,
|
|
216
|
+
suggestion2: `${suggestion2} (${result[1].ms}ms)`
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Multi-value path: every segment must resolve to a single token for autofix
|
|
224
|
+
const resolved = segments.map(resolveDurationToken);
|
|
225
|
+
if (resolved.some(s => s === null)) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
// Build a template literal: `${token(...)}, ${token(...)}`
|
|
229
|
+
const templateLiteral = '`' + resolved.map(s => `\${${s}}`).join(', ') + '`';
|
|
230
|
+
context.report({
|
|
231
|
+
node,
|
|
232
|
+
messageId: 'useMotionDurationToken',
|
|
233
|
+
data: {
|
|
234
|
+
value: rawValue,
|
|
235
|
+
suggestion: templateLiteral
|
|
236
|
+
},
|
|
237
|
+
fix(fixer) {
|
|
238
|
+
return [...getImportFix(fixer), fixer.replaceText(node.value, templateLiteral)];
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Returns autofix string for a single easing value, or null if no token suggestion is possible
|
|
244
|
+
function resolveEasingToken(value) {
|
|
245
|
+
const trimmed = value.trim();
|
|
246
|
+
if (SKIP_EASING_VALUES.has(trimmed)) {
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
if (trimmed in CSS_KEYWORD_EASING_TOKEN_MAP) {
|
|
250
|
+
return buildTokenCall(CSS_KEYWORD_EASING_TOKEN_MAP[trimmed], trimmed);
|
|
251
|
+
}
|
|
252
|
+
// linear has no curve (0,0,1,1) — warn only, no autofix
|
|
253
|
+
if (trimmed === 'linear') {
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
if (trimmed.startsWith('linear(')) {
|
|
257
|
+
// linear() is used for spring animations — motion.easing.spring is experimental, skip
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
const params = parseCubicBezierParams(trimmed);
|
|
261
|
+
if (!params) {
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
const exact = EASING_TOKENS.find(t => t.value === trimmed);
|
|
265
|
+
if (exact) {
|
|
266
|
+
return buildTokenCall(exact.token, trimmed);
|
|
267
|
+
}
|
|
268
|
+
const closest = findClosestEasingToken(params);
|
|
269
|
+
return closest ? buildTokenCall(closest.token, trimmed) : null;
|
|
270
|
+
}
|
|
271
|
+
function handleEasingProperty(node, rawValue) {
|
|
272
|
+
const segments = splitOnTopLevelCommas(rawValue);
|
|
273
|
+
|
|
274
|
+
// Multi-value path: resolve each segment, autofix only if all resolve cleanly
|
|
275
|
+
if (segments.length > 1) {
|
|
276
|
+
const resolved = segments.map(resolveEasingToken);
|
|
277
|
+
if (resolved.some(s => s === null)) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
const templateLiteral = '`' + resolved.map(s => `\${${s}}`).join(', ') + '`';
|
|
281
|
+
context.report({
|
|
282
|
+
node,
|
|
283
|
+
messageId: 'useMotionEasingToken',
|
|
284
|
+
data: {
|
|
285
|
+
value: rawValue,
|
|
286
|
+
suggestion: templateLiteral
|
|
287
|
+
},
|
|
288
|
+
fix(fixer) {
|
|
289
|
+
return [...getImportFix(fixer), fixer.replaceText(node.value, templateLiteral)];
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
const trimmed = rawValue.trim();
|
|
295
|
+
if (SKIP_EASING_VALUES.has(trimmed)) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// CSS keyword easings: convert to cubic-bezier equivalent and find closest token
|
|
300
|
+
if (trimmed in CSS_KEYWORD_EASING_TOKEN_MAP) {
|
|
301
|
+
const suggestion = buildTokenCall(CSS_KEYWORD_EASING_TOKEN_MAP[trimmed], trimmed);
|
|
302
|
+
context.report({
|
|
303
|
+
node,
|
|
304
|
+
messageId: 'useMotionEasingToken',
|
|
305
|
+
data: {
|
|
306
|
+
value: trimmed,
|
|
307
|
+
suggestion
|
|
308
|
+
},
|
|
309
|
+
fix(fixer) {
|
|
310
|
+
return [...getImportFix(fixer), fixer.replaceText(node.value, suggestion)];
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
// linear has no curve (0,0,1,1) — warn only, no autofix
|
|
316
|
+
if (trimmed === 'linear') {
|
|
317
|
+
context.report({
|
|
318
|
+
node,
|
|
319
|
+
messageId: 'useMotionEasingTokenUnknown',
|
|
320
|
+
data: {
|
|
321
|
+
value: trimmed
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
if (trimmed.startsWith('linear(')) {
|
|
327
|
+
// linear() is used for spring animations — motion.easing.spring is experimental, skip
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
const params = parseCubicBezierParams(trimmed);
|
|
331
|
+
if (!params) {
|
|
332
|
+
context.report({
|
|
333
|
+
node,
|
|
334
|
+
messageId: 'useMotionEasingTokenUnknown',
|
|
335
|
+
data: {
|
|
336
|
+
value: rawValue
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
const exact = EASING_TOKENS.find(t => t.value === trimmed);
|
|
342
|
+
if (exact) {
|
|
343
|
+
const suggestion = buildTokenCall(exact.token, rawValue);
|
|
344
|
+
context.report({
|
|
345
|
+
node,
|
|
346
|
+
messageId: 'useMotionEasingToken',
|
|
347
|
+
data: {
|
|
348
|
+
value: rawValue,
|
|
349
|
+
suggestion
|
|
350
|
+
},
|
|
351
|
+
fix(fixer) {
|
|
352
|
+
return [...getImportFix(fixer), fixer.replaceText(node.value, suggestion)];
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
const closest = findClosestEasingToken(params);
|
|
358
|
+
if (closest) {
|
|
359
|
+
const suggestion = buildTokenCall(closest.token, rawValue);
|
|
360
|
+
context.report({
|
|
361
|
+
node,
|
|
362
|
+
messageId: 'useMotionEasingToken',
|
|
363
|
+
data: {
|
|
364
|
+
value: rawValue,
|
|
365
|
+
suggestion
|
|
366
|
+
},
|
|
367
|
+
fix(fixer) {
|
|
368
|
+
return [...getImportFix(fixer), fixer.replaceText(node.value, suggestion)];
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
} else {
|
|
372
|
+
context.report({
|
|
373
|
+
node,
|
|
374
|
+
messageId: 'useMotionEasingTokenUnknown',
|
|
375
|
+
data: {
|
|
376
|
+
value: rawValue
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
function handleProperty(node) {
|
|
382
|
+
const key = node.key;
|
|
383
|
+
if (key.type !== 'Identifier') {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
const isDuration = DURATION_PROPERTIES.has(key.name);
|
|
387
|
+
const isEasing = EASING_PROPERTIES.has(key.name);
|
|
388
|
+
if (!isDuration && !isEasing) {
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
const value = node.value;
|
|
392
|
+
if (value.type === 'TemplateLiteral') {
|
|
393
|
+
// Only handle no-interpolation template literals (e.g. `200ms`) — treat as string
|
|
394
|
+
const tl = value;
|
|
395
|
+
if (tl.expressions.length === 0 && tl.quasis.length === 1) {
|
|
396
|
+
var _tl$quasis$0$value$co;
|
|
397
|
+
const rawValue = (_tl$quasis$0$value$co = tl.quasis[0].value.cooked) !== null && _tl$quasis$0$value$co !== void 0 ? _tl$quasis$0$value$co : tl.quasis[0].value.raw;
|
|
398
|
+
if (isDuration) {
|
|
399
|
+
handleDurationProperty(node, rawValue);
|
|
400
|
+
} else {
|
|
401
|
+
handleEasingProperty(node, rawValue);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
if (value.type === 'CallExpression') {
|
|
407
|
+
const ce = value;
|
|
408
|
+
if (ce.callee.type === 'Identifier' && ce.callee.name === 'token') {
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
if (value.type === 'Literal') {
|
|
414
|
+
const lit = value;
|
|
415
|
+
let rawValue;
|
|
416
|
+
if (typeof lit.value === 'string') {
|
|
417
|
+
rawValue = lit.value;
|
|
418
|
+
} else if (typeof lit.value === 'number') {
|
|
419
|
+
// Treat bare numbers as ms
|
|
420
|
+
rawValue = `${lit.value}ms`;
|
|
421
|
+
} else {
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
if (isDuration) {
|
|
425
|
+
handleDurationProperty(node, rawValue);
|
|
426
|
+
} else {
|
|
427
|
+
handleEasingProperty(node, rawValue);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return {
|
|
432
|
+
ImportDeclaration(node) {
|
|
433
|
+
if (node.source.value === '@atlaskit/tokens') {
|
|
434
|
+
tokensImportNode = node;
|
|
435
|
+
hasTokenSpecifier = node.specifiers.some(s => s.type === 'ImportSpecifier' && s.local.name === 'token');
|
|
436
|
+
}
|
|
437
|
+
},
|
|
438
|
+
Property(node) {
|
|
439
|
+
handleProperty(node);
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
export default useMotionTokenValues;
|