@atlaskit/css 0.10.3 → 0.10.5

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,22 @@
1
1
  # @atlaskit/css
2
2
 
3
+ ## 0.10.5
4
+
5
+ ### Patch Changes
6
+
7
+ - [#135712](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/135712)
8
+ [`fa9caa5adb7a3`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/fa9caa5adb7a3) -
9
+ Improvements to `xcss()` -> `cssMap()` codemod.
10
+ - Updated dependencies
11
+
12
+ ## 0.10.4
13
+
14
+ ### Patch Changes
15
+
16
+ - [#133515](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/133515)
17
+ [`3e092526fea27`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/3e092526fea27) -
18
+ Fix codemod config.
19
+
3
20
  ## 0.10.3
4
21
 
5
22
  ### Patch Changes
@@ -92,6 +92,10 @@ function replaceXcssWithCssMap(
92
92
  specifier: string,
93
93
  ) {
94
94
  const cssMapProperties: core.ObjectProperty[] = [];
95
+ let firstXcssPath: ASTPath<core.CallExpression> | null = null;
96
+ let firstUsagePath: ASTPath<core.Node> | null = null;
97
+ const styleVariables = new Set<string>();
98
+ const variableDependencies = new Map<string, Set<string>>();
95
99
 
96
100
  source
97
101
  .find(j.CallExpression, {
@@ -114,15 +118,85 @@ function replaceXcssWithCssMap(
114
118
 
115
119
  if (variableDeclarator && variableDeclarator.type === 'VariableDeclarator') {
116
120
  const variableName = variableDeclarator.id.name; // e.g. buttonStyles
117
- const key = getCssMapKey(variableName); // buttonStyles -> button to put in cssMap as the key e.g. styles = cssMap({ button: { color: 'red' } });
121
+ styleVariables.add(variableName);
122
+
123
+ // find dependencies in the xcss object
124
+ const dependencies = new Set<string>();
125
+ if (args[0].type === 'ObjectExpression') {
126
+ args[0].properties.forEach((prop) => {
127
+ if (prop.type === 'ObjectProperty' && prop.value.type === 'TemplateLiteral') {
128
+ prop.value.expressions.forEach((expr) => {
129
+ if (expr.type === 'Identifier') {
130
+ dependencies.add(expr.name);
131
+ }
132
+ });
133
+ }
134
+ });
135
+ }
136
+ if (dependencies.size > 0) {
137
+ variableDependencies.set(variableName, dependencies);
138
+ }
139
+ }
140
+ }
141
+ }
142
+ });
118
143
 
144
+ // find the first usage of any style variable
145
+ source.find(j.Identifier).forEach((path) => {
146
+ if (
147
+ styleVariables.has(path.node.name) &&
148
+ (!firstUsagePath ||
149
+ (path.node.loc?.start &&
150
+ firstUsagePath.node.loc?.start &&
151
+ (path.node.loc.start.line < firstUsagePath.node.loc.start.line ||
152
+ (path.node.loc.start.line === firstUsagePath.node.loc.start.line &&
153
+ path.node.loc.start.column < firstUsagePath.node.loc.start.column))))
154
+ ) {
155
+ firstUsagePath = path;
156
+ }
157
+ });
158
+
159
+ // find all xcss function calls
160
+ source
161
+ .find(j.CallExpression, {
162
+ callee: {
163
+ type: 'Identifier',
164
+ name: specifier,
165
+ },
166
+ })
167
+ .forEach((path) => {
168
+ const args = path.node.arguments;
169
+ // only process xcss calls that have a single object argument
170
+ if (args.length === 1 && args[0].type === 'ObjectExpression') {
171
+ // get the parent variable declaration that contains this xcss call
172
+ // e.g. const buttonStyles = xcss({...})
173
+ const parentVariableDeclaration = path.parentPath?.parentPath?.parentPath?.node;
174
+ if (parentVariableDeclaration && parentVariableDeclaration.type === 'VariableDeclaration') {
175
+ // find the variable declarator that initialises with this xcss call
176
+ // e.g. const buttonStyles = xcss({ color: 'red' });
177
+ const variableDeclarator = parentVariableDeclaration.declarations.find(
178
+ (declaration: core.VariableDeclarator) => declaration.init === path.node,
179
+ );
180
+
181
+ if (variableDeclarator && variableDeclarator.type === 'VariableDeclarator') {
182
+ // convert the variable name to a cssMap key (e.g. myStyles -> root)
183
+ const variableName = variableDeclarator.id.name;
184
+ const key = getCssMapKey(variableName);
185
+
186
+ // create a new cssMap property with the key and the processed styles
119
187
  const cssMapObject = j.objectProperty(
120
188
  j.identifier(key),
121
189
  ensureSelectorAmpersand(j, args[0]),
122
190
  );
123
191
  cssMapProperties.push(cssMapObject);
124
192
 
125
- j(path.parentPath.parentPath.parentPath).remove(); // remove original xcss object
193
+ // track the first xcss call
194
+ if (!firstXcssPath) {
195
+ firstXcssPath = path;
196
+ }
197
+
198
+ // remove the original xcss variable declaration since we'll replace it with cssMap
199
+ j(path.parentPath.parentPath.parentPath).remove();
126
200
  }
127
201
  }
128
202
  }
@@ -138,11 +212,167 @@ function replaceXcssWithCssMap(
138
212
  ),
139
213
  ]);
140
214
 
141
- // insert the cssMap var after all imports
142
- const lastImportIndex = source.find(j.ImportDeclaration).size();
143
- source.get().node.program.body.splice(lastImportIndex, 0, cssMapVariableDeclaration);
215
+ // insert the cssMap declaration before its first usage
216
+ if (firstUsagePath) {
217
+ const programBody = source.get().node.program.body;
218
+ let insertIndex = 0;
219
+
220
+ // find the last variable declaration that any style depends on
221
+ let lastDependencyIndex = -1;
222
+ for (let i = 0; i < programBody.length; i++) {
223
+ const node = programBody[i];
224
+ if (node.type === 'VariableDeclaration') {
225
+ const varName = node.declarations[0]?.id.name;
226
+ if (varName) {
227
+ // check if this variable is a dependency of any style
228
+ for (const [, deps] of variableDependencies.entries()) {
229
+ if (deps.has(varName)) {
230
+ lastDependencyIndex = i;
231
+ break;
232
+ }
233
+ }
234
+ }
235
+ }
236
+ }
237
+
238
+ // insert after the last dependency
239
+ if (lastDependencyIndex !== -1) {
240
+ insertIndex = lastDependencyIndex + 1;
241
+ } else {
242
+ // if no dependencies, find the first non-import/type declaration
243
+ insertIndex = programBody.findIndex(
244
+ (node: { type: string }) =>
245
+ node.type !== 'ImportDeclaration' &&
246
+ node.type !== 'TypeAlias' &&
247
+ node.type !== 'InterfaceDeclaration',
248
+ );
249
+ }
250
+
251
+ programBody.splice(insertIndex, 0, cssMapVariableDeclaration);
252
+ }
144
253
  }
145
254
 
255
+ // First handle backgroundColor transformation for Box components
256
+ source
257
+ .find(j.JSXAttribute, {
258
+ name: {
259
+ type: 'JSXIdentifier',
260
+ name: 'xcss',
261
+ },
262
+ })
263
+ .forEach((path) => {
264
+ const value = path.node.value;
265
+ if (value && value.type === 'JSXExpressionContainer') {
266
+ const expression = value.expression;
267
+ const parentElement = path.parentPath?.node;
268
+ const isBoxComponent = parentElement?.name?.name === 'Box';
269
+
270
+ if (isBoxComponent) {
271
+ if (expression.type === 'Identifier') {
272
+ const styleKey = getCssMapKey(expression.name);
273
+ const styleObject = cssMapProperties.find(
274
+ (prop) => prop.key.type === 'Identifier' && prop.key.name === styleKey,
275
+ )?.value;
276
+ if (styleObject?.type === 'ObjectExpression') {
277
+ const backgroundColorProp = styleObject.properties.find(
278
+ (prop) =>
279
+ prop.type === 'ObjectProperty' &&
280
+ prop.key.type === 'Identifier' &&
281
+ prop.key.name === 'backgroundColor',
282
+ );
283
+ if (
284
+ backgroundColorProp?.type === 'ObjectProperty' &&
285
+ backgroundColorProp.value.type === 'StringLiteral'
286
+ ) {
287
+ // add backgroundColor prop to Box component
288
+ parentElement.attributes.push(
289
+ j.jsxAttribute(
290
+ j.jsxIdentifier('backgroundColor'),
291
+ j.stringLiteral(backgroundColorProp.value.value),
292
+ ),
293
+ );
294
+ // remove backgroundColor from cssMap
295
+ styleObject.properties = styleObject.properties.filter(
296
+ (prop) =>
297
+ !(
298
+ prop.type === 'ObjectProperty' &&
299
+ prop.key.type === 'Identifier' &&
300
+ prop.key.name === 'backgroundColor'
301
+ ),
302
+ );
303
+ }
304
+ }
305
+ } else if (expression.type === 'ArrayExpression') {
306
+ // handle array of styles
307
+ const backgroundColorValues: string[] = [];
308
+ expression.elements.forEach((element) => {
309
+ if (element?.type === 'Identifier') {
310
+ const styleKey = getCssMapKey(element.name);
311
+ const styleObject = cssMapProperties.find(
312
+ (prop) => prop.key.type === 'Identifier' && prop.key.name === styleKey,
313
+ )?.value;
314
+ if (styleObject?.type === 'ObjectExpression') {
315
+ const backgroundColorProp = styleObject.properties.find(
316
+ (prop) =>
317
+ prop.type === 'ObjectProperty' &&
318
+ prop.key.type === 'Identifier' &&
319
+ prop.key.name === 'backgroundColor',
320
+ );
321
+ if (
322
+ backgroundColorProp?.type === 'ObjectProperty' &&
323
+ backgroundColorProp.value.type === 'StringLiteral'
324
+ ) {
325
+ backgroundColorValues.push(backgroundColorProp.value.value);
326
+ // remove backgroundColor from cssMap
327
+ styleObject.properties = styleObject.properties.filter(
328
+ (prop) =>
329
+ !(
330
+ prop.type === 'ObjectProperty' &&
331
+ prop.key.type === 'Identifier' &&
332
+ prop.key.name === 'backgroundColor'
333
+ ),
334
+ );
335
+ }
336
+ }
337
+ }
338
+ });
339
+
340
+ // if we found backgroundColor values, add to Box component
341
+ if (backgroundColorValues.length > 0) {
342
+ if (backgroundColorValues.length === 1) {
343
+ // single backgroundColor value
344
+ parentElement.attributes.push(
345
+ j.jsxAttribute(
346
+ j.jsxIdentifier('backgroundColor'),
347
+ j.stringLiteral(backgroundColorValues[0]),
348
+ ),
349
+ );
350
+ } else {
351
+ // multiple backgroundColor values - use ternary for conditional
352
+ const conditions = expression.elements
353
+ .map((element, index) => {
354
+ if (
355
+ element?.type === 'LogicalExpression' &&
356
+ element.left.type === 'Identifier'
357
+ ) {
358
+ return `${element.left.name} ? "${backgroundColorValues[index]}" :`;
359
+ }
360
+ return `"${backgroundColorValues[index]}"`;
361
+ })
362
+ .join(' ');
363
+ parentElement.attributes.push(
364
+ j.jsxAttribute(
365
+ j.jsxIdentifier('backgroundColor'),
366
+ j.jsxExpressionContainer(j.identifier(conditions)),
367
+ ),
368
+ );
369
+ }
370
+ }
371
+ }
372
+ }
373
+ }
374
+ });
375
+
146
376
  // update the xcss prop references to use the new cssMap object
147
377
  source
148
378
  .find(j.JSXAttribute, {
@@ -160,7 +390,7 @@ function replaceXcssWithCssMap(
160
390
  // <Box xcss={buttonStyles} /> -> <Box xcss={styles.button} />
161
391
  expression.name = `styles.${getCssMapKey(expression.name)}`;
162
392
  } else if (expression.type === 'ArrayExpression') {
163
- // <Box xcss={[baseStyles, otherStyles]} /> -> <Box xcss={[styles.base, styles.otherStyles]} />
393
+ // <Box xcss={[baseStyles, otherStyles]} /> -> <Box xcss={[styles.base, styles.other]} />
164
394
  expression.elements.forEach((element) => {
165
395
  if (element?.type === 'Identifier') {
166
396
  element.name = `styles.${getCssMapKey(element.name)}`;
@@ -4,6 +4,7 @@ const config = {
4
4
  presets: {
5
5
  'primitives-emotion-to-compiled': primitivesEmotionToCompiled,
6
6
  },
7
+ dependencies: ['@emotion/react'],
7
8
  };
8
9
 
9
10
  export default config;
@@ -25,7 +25,7 @@ Emotion and Compiled, such as dynamic styles or imports. Please use the
25
25
  We have a codemod to assist in migrations from `xcss()` to `@atlaskit/css`.
26
26
 
27
27
  ```sh
28
- npx @hypermod/cli --packages @atlaskit/css@primitives-emotion-to-compiled ./path/to/folder
28
+ npx @hypermod/cli --packages @atlaskit/css#primitives-emotion-to-compiled ./path/to/folder
29
29
  ```
30
30
 
31
31
  The codemod should migrate something like this:
@@ -18,7 +18,7 @@ import UnboundedExample from '../../examples/constellation/unbounded';
18
18
 
19
19
  `@atlaskit/css` is the replacement for `@atlaskit/primitives.xcss`. It serves as a bounded styling
20
20
  library for use with native HTML elements and the Atlassian Design System, including
21
- [primitive components](/components/primitives).
21
+ [primitive components](/components/primitives/overview).
22
22
 
23
23
  Built on [Compiled CSS-in-JS](https://compiledcssinjs.com/), it provides a performant, static
24
24
  styling solution with some syntax changes. Notably, dynamic styles and certain imports/exports may
@@ -39,7 +39,7 @@ values, such as `color: 'rgba(123, 45, 67)'` nor `padding: 8`. Typically, only t
39
39
  allowed. Additionally, there are some restrictions, such as `zIndex`, which only supports a limited
40
40
  set of numeric values.
41
41
 
42
- ### cssMap
42
+ ### cssMap()
43
43
 
44
44
  We recommend using `cssMap` to create style maps. These maps can be applied and reused on both
45
45
  native elements and React components using `props.css` and `props.xcss` respectively.
@@ -48,6 +48,22 @@ Make sure to define the styles before using them in your components, i.e. at the
48
48
 
49
49
  <Example Component={CssMapExample} packageName="@atlaskit/css" />
50
50
 
51
+ ### css()
52
+
53
+ The `css()` function is a lower-level API that creates a single style object. Unlike `cssMap()`,
54
+ it's best suited for one-off styles. Use it with elements that accept a `css` prop. While `css()` is
55
+ simpler to use, we recommend `cssMap()` for most cases because it provides better reusability and
56
+ type safety.
57
+
58
+ ```tsx
59
+ const styles = css({
60
+ padding: token('space.100'),
61
+ color: token('color.text'),
62
+ });
63
+
64
+ <div css={styles}>Hello world!</div>;
65
+ ```
66
+
51
67
  ### Primitives
52
68
 
53
69
  When using `@atlaskit/css`, it's important to note that it's not compatible with the Emotion variant
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/css",
3
- "version": "0.10.3",
3
+ "version": "0.10.5",
4
4
  "description": "Style components backed by Atlassian Design System design tokens powered by Compiled CSS-in-JS.",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -48,7 +48,7 @@
48
48
  "./test-utils": "./src/test-utils/index.tsx"
49
49
  },
50
50
  "dependencies": {
51
- "@atlaskit/tokens": "^4.5.0",
51
+ "@atlaskit/tokens": "^4.6.0",
52
52
  "@babel/runtime": "^7.0.0",
53
53
  "@compiled/react": "^0.18.3"
54
54
  },
@@ -57,13 +57,13 @@
57
57
  },
58
58
  "devDependencies": {
59
59
  "@af/visual-regression": "^1.3.0",
60
- "@atlaskit/button": "^21.1.0",
60
+ "@atlaskit/button": "^23.0.0",
61
61
  "@atlaskit/ds-lib": "^4.0.0",
62
- "@atlaskit/primitives": "^14.2.0",
62
+ "@atlaskit/primitives": "^14.3.0",
63
63
  "@emotion/react": "^11.7.1",
64
64
  "@testing-library/react": "^13.4.0",
65
65
  "@types/jscodeshift": "^0.11.0",
66
- "jscodeshift": "^0.16.1",
66
+ "jscodeshift": "^17.0.0",
67
67
  "react-dom": "^18.2.0",
68
68
  "typescript": "~5.4.2"
69
69
  },