@atlaskit/css 0.5.0 → 0.5.2
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
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @atlaskit/css
|
|
2
2
|
|
|
3
|
+
## 0.5.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#150360](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/150360)
|
|
8
|
+
[`284490a8c1813`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/284490a8c1813) -
|
|
9
|
+
Add codemod to migrate @atlaskit/primitives from using Emotion APIs towards Compiled.
|
|
10
|
+
|
|
11
|
+
## 0.5.1
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- Updated dependencies
|
|
16
|
+
|
|
3
17
|
## 0.5.0
|
|
4
18
|
|
|
5
19
|
### Minor Changes
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
API,
|
|
3
|
+
ASTPath,
|
|
4
|
+
default as core,
|
|
5
|
+
FileInfo,
|
|
6
|
+
ImportDeclaration,
|
|
7
|
+
Options,
|
|
8
|
+
} from 'jscodeshift';
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
backgroundColorMap,
|
|
12
|
+
borderColorMap,
|
|
13
|
+
borderRadiusMap,
|
|
14
|
+
borderWidthMap,
|
|
15
|
+
dimensionMap,
|
|
16
|
+
fillMap,
|
|
17
|
+
fontFamilyMap,
|
|
18
|
+
fontMap,
|
|
19
|
+
fontWeightMap,
|
|
20
|
+
layerMap,
|
|
21
|
+
negativeSpaceMap,
|
|
22
|
+
opacityMap,
|
|
23
|
+
positiveSpaceMap,
|
|
24
|
+
shadowMap,
|
|
25
|
+
surfaceColorMap,
|
|
26
|
+
textColorMap,
|
|
27
|
+
textWeightMap,
|
|
28
|
+
} from './style-maps.partial';
|
|
29
|
+
|
|
30
|
+
const styleMaps = {
|
|
31
|
+
...backgroundColorMap,
|
|
32
|
+
...borderColorMap,
|
|
33
|
+
...borderRadiusMap,
|
|
34
|
+
...borderWidthMap,
|
|
35
|
+
...dimensionMap,
|
|
36
|
+
...fillMap,
|
|
37
|
+
...fontFamilyMap,
|
|
38
|
+
...fontMap,
|
|
39
|
+
...fontWeightMap,
|
|
40
|
+
...layerMap,
|
|
41
|
+
...negativeSpaceMap,
|
|
42
|
+
...opacityMap,
|
|
43
|
+
...positiveSpaceMap,
|
|
44
|
+
...shadowMap,
|
|
45
|
+
...surfaceColorMap,
|
|
46
|
+
...textColorMap,
|
|
47
|
+
...textWeightMap,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export default function transformer(fileInfo: FileInfo, { jscodeshift: j }: API, options: Options) {
|
|
51
|
+
const base = j(fileInfo.source);
|
|
52
|
+
|
|
53
|
+
// replace xcss with cssMap
|
|
54
|
+
const xcssSpecifier = getImportSpecifier(j, base, 'xcss');
|
|
55
|
+
if (!xcssSpecifier) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
addJsxPragma(j, base);
|
|
60
|
+
|
|
61
|
+
replaceXcssWithCssMap(j, base, xcssSpecifier);
|
|
62
|
+
|
|
63
|
+
updateImports(j, base);
|
|
64
|
+
|
|
65
|
+
return base.toSource();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function addJsxPragma(j: core.JSCodeshift, source: ReturnType<typeof j>) {
|
|
69
|
+
const jsxPragma = [j.commentBlock('*\n * @jsxRuntime classic\n * @jsx jsx\n ', true, false)];
|
|
70
|
+
|
|
71
|
+
const rootNode = source.get().node;
|
|
72
|
+
const existingComments = rootNode.comments || [];
|
|
73
|
+
|
|
74
|
+
const hasJsxPragma = existingComments.some(
|
|
75
|
+
(comment: core.Comment) =>
|
|
76
|
+
comment.value.includes('@jsxRuntime classic') && comment.value.includes('@jsx jsx'),
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
if (!hasJsxPragma) {
|
|
80
|
+
rootNode.comments = [...existingComments, ...jsxPragma];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function replaceXcssWithCssMap(
|
|
85
|
+
j: core.JSCodeshift,
|
|
86
|
+
source: ReturnType<typeof j>,
|
|
87
|
+
specifier: string,
|
|
88
|
+
) {
|
|
89
|
+
const cssMapProperties: core.ObjectProperty[] = [];
|
|
90
|
+
|
|
91
|
+
source
|
|
92
|
+
.find(j.CallExpression, {
|
|
93
|
+
// get all xcss calls
|
|
94
|
+
callee: {
|
|
95
|
+
type: 'Identifier',
|
|
96
|
+
name: specifier,
|
|
97
|
+
},
|
|
98
|
+
})
|
|
99
|
+
.forEach((path) => {
|
|
100
|
+
const args = path.node.arguments;
|
|
101
|
+
if (args.length === 1 && args[0].type === 'ObjectExpression') {
|
|
102
|
+
// get the parent variable declaration
|
|
103
|
+
// e.g. const buttonStyles = xcss({ color: 'red' });
|
|
104
|
+
const parentVariableDeclaration = path.parentPath?.parentPath?.parentPath?.node;
|
|
105
|
+
if (parentVariableDeclaration && parentVariableDeclaration.type === 'VariableDeclaration') {
|
|
106
|
+
const variableDeclarator = parentVariableDeclaration.declarations.find(
|
|
107
|
+
(declaration: core.VariableDeclarator) => declaration.init === path.node,
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
if (variableDeclarator && variableDeclarator.type === 'VariableDeclarator') {
|
|
111
|
+
const variableName = variableDeclarator.id.name; // e.g. buttonStyles
|
|
112
|
+
const key = getCssMapKey(variableName); // buttonStyles -> button to put in cssMap as the key e.g. styles = cssMap({ button: { color: 'red' } });
|
|
113
|
+
|
|
114
|
+
const cssMapObject = j.objectProperty(
|
|
115
|
+
j.identifier(key),
|
|
116
|
+
ensureSelectorAmpersand(j, args[0]),
|
|
117
|
+
);
|
|
118
|
+
cssMapProperties.push(cssMapObject);
|
|
119
|
+
|
|
120
|
+
j(path.parentPath.parentPath.parentPath).remove(); // remove original xcss object
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// create new cssMap with the combined xcss object properties
|
|
127
|
+
if (cssMapProperties.length > 0) {
|
|
128
|
+
const cssMapObject = j.objectExpression(cssMapProperties);
|
|
129
|
+
const cssMapVariableDeclaration = j.variableDeclaration('const', [
|
|
130
|
+
j.variableDeclarator(
|
|
131
|
+
j.identifier('styles'),
|
|
132
|
+
j.callExpression(j.identifier('cssMap'), [cssMapObject]),
|
|
133
|
+
),
|
|
134
|
+
]);
|
|
135
|
+
source.get().node.program.body.unshift(cssMapVariableDeclaration);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// update the xcss prop references to use the new cssMap object
|
|
139
|
+
source
|
|
140
|
+
.find(j.JSXAttribute, {
|
|
141
|
+
name: {
|
|
142
|
+
type: 'JSXIdentifier',
|
|
143
|
+
name: 'xcss',
|
|
144
|
+
},
|
|
145
|
+
})
|
|
146
|
+
.forEach((path) => {
|
|
147
|
+
const value = path.node.value;
|
|
148
|
+
// e.g. <Box xcss={buttonStyles} />
|
|
149
|
+
if (value && value.type === 'JSXExpressionContainer') {
|
|
150
|
+
const expression = value.expression;
|
|
151
|
+
if (expression.type === 'Identifier') {
|
|
152
|
+
// <Box xcss={buttonStyles} /> -> <Box xcss={styles.button} />
|
|
153
|
+
expression.name = `styles.${getCssMapKey(expression.name)}`;
|
|
154
|
+
// <Box xcss={[baseStyles, otherStyles]} /> -> <Box xcss={[styles.base, styles.otherStyles]} />
|
|
155
|
+
} else if (expression.type === 'ArrayExpression') {
|
|
156
|
+
expression.elements.forEach((element) => {
|
|
157
|
+
if (element?.type === 'Identifier') {
|
|
158
|
+
element.name = `styles.${getCssMapKey(element.name)}`;
|
|
159
|
+
// <Box xcss={condition && styles} /> -> <Box xcss={condition && styles.root} />
|
|
160
|
+
} else if (
|
|
161
|
+
element?.type === 'LogicalExpression' &&
|
|
162
|
+
element.right.type === 'Identifier'
|
|
163
|
+
) {
|
|
164
|
+
element.right.name = `styles.${getCssMapKey(element.right.name)}`;
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// replace style keys with their corresponding values from the map
|
|
172
|
+
source.find(j.ObjectProperty).forEach((path) => {
|
|
173
|
+
const key = path.node.key;
|
|
174
|
+
const value = path.node.value;
|
|
175
|
+
|
|
176
|
+
if (key.type === 'Identifier' && value.type === 'StringLiteral') {
|
|
177
|
+
const styleKey = value.value as keyof typeof styleMaps;
|
|
178
|
+
if (styleMaps[styleKey]) {
|
|
179
|
+
const mapValue = styleMaps[styleKey];
|
|
180
|
+
if (typeof mapValue === 'string' && mapValue.startsWith('var(')) {
|
|
181
|
+
// token call
|
|
182
|
+
j(path).replaceWith(
|
|
183
|
+
j.objectProperty(
|
|
184
|
+
key,
|
|
185
|
+
j.callExpression(j.identifier('token'), [j.stringLiteral(styleKey)]),
|
|
186
|
+
),
|
|
187
|
+
);
|
|
188
|
+
} else {
|
|
189
|
+
// non-token call, just use the value
|
|
190
|
+
j(path).replaceWith(j.objectProperty(key, j.literal(mapValue)));
|
|
191
|
+
}
|
|
192
|
+
} else {
|
|
193
|
+
// copy the original object property and value when not in styleMaps
|
|
194
|
+
// e.g. color: 'red'
|
|
195
|
+
j(path).replaceWith(j.objectProperty(key, value));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// this only accounts for if the selector starts with a colon
|
|
202
|
+
// e.g. { ':hover': { ... } } -> { '&:hover': { ... } }
|
|
203
|
+
function ensureSelectorAmpersand(j: core.JSCodeshift, objectExpression: core.ObjectExpression) {
|
|
204
|
+
objectExpression.properties.forEach((property) => {
|
|
205
|
+
if (property.type === 'ObjectProperty' && property.key.type === 'StringLiteral') {
|
|
206
|
+
const key = property.key.value;
|
|
207
|
+
if (key.startsWith(':')) {
|
|
208
|
+
property.key = j.stringLiteral(`&${key}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
return objectExpression;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function updateImports(j: core.JSCodeshift, source: ReturnType<typeof j>) {
|
|
216
|
+
// remove xcss import
|
|
217
|
+
source
|
|
218
|
+
.find(j.ImportDeclaration)
|
|
219
|
+
.filter((path: ASTPath<ImportDeclaration>) => path.node.source.value === '@atlaskit/primitives')
|
|
220
|
+
.forEach((path) => {
|
|
221
|
+
if (path.node.specifiers) {
|
|
222
|
+
path.node.specifiers = path.node.specifiers.filter(
|
|
223
|
+
(specifier) => specifier.local?.name !== 'xcss',
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
const existingImports = source.find(j.ImportDeclaration);
|
|
229
|
+
|
|
230
|
+
const hasCssMapImport = existingImports.some(
|
|
231
|
+
(path) => path.node.source.value === '@atlaskit/css',
|
|
232
|
+
);
|
|
233
|
+
if (!hasCssMapImport) {
|
|
234
|
+
const cssMapImport = j.importDeclaration(
|
|
235
|
+
[j.importSpecifier(j.identifier('cssMap'))],
|
|
236
|
+
j.literal('@atlaskit/css'),
|
|
237
|
+
);
|
|
238
|
+
source.get().node.program.body.unshift(cssMapImport);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const hasTokenImport = existingImports.some(
|
|
242
|
+
(path) => path.node.source.value === '@atlaskit/tokens',
|
|
243
|
+
);
|
|
244
|
+
if (!hasTokenImport) {
|
|
245
|
+
const tokenImport = j.importDeclaration(
|
|
246
|
+
[j.importSpecifier(j.identifier('token'))],
|
|
247
|
+
j.literal('@atlaskit/tokens'),
|
|
248
|
+
);
|
|
249
|
+
source.get().node.program.body.unshift(tokenImport);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// update existing @atlaskit/primitives imports to @atlaskit/primitives/compiled
|
|
253
|
+
// e.g. import { Box } from '@atlaskit/primitives' -> import { Box } from '@atlaskit/primitives/compiled'
|
|
254
|
+
source
|
|
255
|
+
.find(j.ImportDeclaration)
|
|
256
|
+
.filter((path: ASTPath<ImportDeclaration>) => path.node.source.value === '@atlaskit/primitives')
|
|
257
|
+
.forEach((path) => {
|
|
258
|
+
path.node.source.value = '@atlaskit/primitives/compiled';
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
const hasJsxImport = existingImports.some((path) => path.node.source.value === '@compiled/react');
|
|
262
|
+
if (!hasJsxImport) {
|
|
263
|
+
// check if there is `import { jsx } from '@emotion/react'`
|
|
264
|
+
// this should be replaced with `import { jsx } from '@compiled/react'`
|
|
265
|
+
const existingEmotionImport = source
|
|
266
|
+
.find(j.ImportDeclaration)
|
|
267
|
+
.filter((path: ASTPath<ImportDeclaration>) => path.node.source.value === '@emotion/react')
|
|
268
|
+
.find(j.ImportSpecifier)
|
|
269
|
+
.filter((path) => path.node.imported.name === 'jsx');
|
|
270
|
+
|
|
271
|
+
const jsxImport = j.importDeclaration(
|
|
272
|
+
[j.importSpecifier(j.identifier('jsx'))],
|
|
273
|
+
j.literal('@compiled/react'),
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
if (existingEmotionImport.size() > 0) {
|
|
277
|
+
// replace jsx import from `@emotion/react` with `@compiled/react`
|
|
278
|
+
existingEmotionImport.closest(j.ImportDeclaration).replaceWith(jsxImport);
|
|
279
|
+
} else {
|
|
280
|
+
// add the new import at the top of the file
|
|
281
|
+
source.get().node.program.body.unshift(jsxImport);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// sort import declarations alphabetically
|
|
286
|
+
// probably not necessary as we can rely on prettier on save
|
|
287
|
+
const allImports = source.find(j.ImportDeclaration).nodes();
|
|
288
|
+
allImports.sort((a, b) => {
|
|
289
|
+
if (
|
|
290
|
+
typeof a.source.value === 'undefined' ||
|
|
291
|
+
typeof b.source.value === 'undefined' ||
|
|
292
|
+
a.source.value === null ||
|
|
293
|
+
b.source.value === null
|
|
294
|
+
) {
|
|
295
|
+
return 0;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return a.source.value > b.source.value ? 1 : -1;
|
|
299
|
+
});
|
|
300
|
+
source.get().node.program.body = [
|
|
301
|
+
...allImports,
|
|
302
|
+
...source
|
|
303
|
+
.get()
|
|
304
|
+
.node.program.body.filter((node: core.Node) => node.type !== 'ImportDeclaration'),
|
|
305
|
+
];
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// look for xcss import
|
|
309
|
+
function getImportSpecifier(j: core.JSCodeshift, source: any, specifier: string) {
|
|
310
|
+
const specifiers = source
|
|
311
|
+
.find(j.ImportDeclaration)
|
|
312
|
+
.filter((path: ASTPath<ImportDeclaration>) => path.node.source.value === '@atlaskit/primitives')
|
|
313
|
+
.find(j.ImportSpecifier)
|
|
314
|
+
.filter((path: any) => path.node.imported.name === specifier);
|
|
315
|
+
|
|
316
|
+
if (!specifiers.length) {
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
return specifiers.nodes()[0]!.local!.name;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/*
|
|
323
|
+
* Logic to determine the key for the cssMap object
|
|
324
|
+
* e.g. styles -> root, buttonStyles -> button
|
|
325
|
+
* We might want nicer logic in the future with some smarts/context
|
|
326
|
+
* about the element it's being applied to
|
|
327
|
+
* e.g. if the element just has one style, we should just use the key 'root'
|
|
328
|
+
*/
|
|
329
|
+
function getCssMapKey(variableName: string): string {
|
|
330
|
+
if (variableName.toLowerCase() === 'styles') {
|
|
331
|
+
return 'root';
|
|
332
|
+
}
|
|
333
|
+
return variableName.replace(/Styles$/i, '');
|
|
334
|
+
}
|