Skip to content

Commit 0ebbfc1

Browse files
Fix broken css prop ternaries when referencing cssMap objects. (#1767)
* Fix ternaries by extracting nested member expressions inside of conditional expressions. Fixes #1750 We simply copy the direct `buildCss > MemberExpression > …` extraction into a new path through `buildCss > ConditionalExpression > extractConditionalExpression(…) > MemberExpression > …`. * Fixup recursive scenario * Revert unintended minor testing change
1 parent fc4c47a commit 0ebbfc1

File tree

3 files changed

+96
-12
lines changed

3 files changed

+96
-12
lines changed

.changeset/cyan-waves-rest.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@compiled/babel-plugin': minor
3+
---
4+
5+
Fix supporting ternaries referencing cssMap style objects when extracting styles.

packages/babel-plugin/src/css-map/__tests__/index.test.ts

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,18 @@ describe('css map basic functionality', () => {
2222
import { cssMap } from '@compiled/react';
2323
2424
const styles = cssMap(${styles});
25+
26+
const Component = () => <div>
27+
<span css={styles.danger} />
28+
<span css={styles.success} />
29+
</div>
2530
`);
2631

27-
expect(actual).toInclude(
28-
'const styles={danger:"_syaz5scu _bfhk5scu",success:"_syazbf54 _bfhkbf54"};'
29-
);
32+
expect(actual).toIncludeMultiple([
33+
'const styles={danger:"_syaz5scu _bfhk5scu",success:"_syazbf54 _bfhkbf54"};',
34+
'<span className={ax([styles.danger])}/>',
35+
'<span className={ax([styles.success])}/>',
36+
]);
3037
});
3138

3239
it('should transform css map even with an empty object', () => {
@@ -45,6 +52,42 @@ describe('css map basic functionality', () => {
4552
expect(actual).toInclude('const styles={danger:"",success:"_syazbf54 _bfhkbf54"};');
4653
});
4754

55+
it('should transform ternary-based conditional referencing cssMap declarations', () => {
56+
const actual = transform(`
57+
import { cssMap } from '@compiled/react';
58+
59+
const styles = cssMap({
60+
root: { display: 'block' },
61+
positive: { background: 'white', color: 'black' },
62+
negative: { background: 'green', color: 'red' },
63+
bold: { fontWeight: 'bold' },
64+
normal: { fontWeight: 'normal' },
65+
});
66+
67+
const Component = ({ isPrimary, weight }) => (
68+
<div
69+
css={[
70+
styles.root,
71+
weight in styles ? styles[weight] : styles.normal,
72+
isPrimary ? styles.positive : styles.negative,
73+
]}
74+
/>
75+
);
76+
`);
77+
78+
expect(actual).toIncludeMultiple([
79+
'._1e0c1ule{display:block}',
80+
'._bfhk1x77{background-color:white}',
81+
'._syaz11x8{color:black}',
82+
'._bfhkbf54{background-color:green}',
83+
'._syaz5scu{color:red}',
84+
'._k48p8n31{font-weight:bold}',
85+
'._k48p4jg8{font-weight:normal}',
86+
'const styles={root:"_1e0c1ule",positive:"_bfhk1x77 _syaz11x8",negative:"_bfhkbf54 _syaz5scu",bold:"_k48p8n31",normal:"_k48p4jg8"}',
87+
'<div className={ax([styles.root,weight in styles?styles[weight]:styles.normal,isPrimary?styles.positive:styles.negative])}/>',
88+
]);
89+
});
90+
4891
it('should error out if variants are not defined at the top-most scope of the module.', () => {
4992
expect(() => {
5093
transform(`

packages/babel-plugin/src/utils/css-builders.ts

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,8 @@ const extractConditionalExpression = (node: t.ConditionalExpression, meta: Metad
381381
}
382382
} else if (t.isConditionalExpression(pathNode)) {
383383
cssOutput = extractConditionalExpression(pathNode, meta);
384+
} else if (t.isMemberExpression(pathNode)) {
385+
cssOutput = extractMemberExpression(pathNode, meta, false);
384386
}
385387

386388
if (cssOutput) {
@@ -663,6 +665,44 @@ const extractObjectExpression = (node: t.ObjectExpression, meta: Metadata): CSSO
663665
return { css: mergeSubsequentUnconditionalCssItems(css), variables };
664666
};
665667

668+
/**
669+
* Extracts CSS data from a member expression node (eg. `styles.primary`)
670+
*
671+
* @param node Node we're interested in extracting CSS from.
672+
* @param meta {Metadata} Useful metadata that can be used during the transformation
673+
* @param fallbackToEvaluate {Boolean} Whether to fallback to re-evaluating the expression if it's not a cssMap identifier
674+
*/
675+
function extractMemberExpression(
676+
node: t.MemberExpression,
677+
meta: Metadata,
678+
fallbackToEvaluate?: true
679+
): CSSOutput;
680+
function extractMemberExpression(
681+
node: t.MemberExpression,
682+
meta: Metadata,
683+
fallbackToEvaluate: false
684+
): CSSOutput | undefined;
685+
function extractMemberExpression(
686+
node: t.MemberExpression,
687+
meta: Metadata,
688+
fallbackToEvaluate = true
689+
): CSSOutput | undefined {
690+
const bindingIdentifier = findBindingIdentifier(node);
691+
if (bindingIdentifier && meta.state.cssMap[bindingIdentifier.name]) {
692+
return {
693+
css: [{ type: 'map', expression: node, name: bindingIdentifier.name, css: '' }],
694+
variables: [],
695+
};
696+
}
697+
698+
if (fallbackToEvaluate) {
699+
const { value, meta: updatedMeta } = evaluateExpression(node, meta);
700+
return buildCss(value, updatedMeta);
701+
}
702+
703+
return undefined;
704+
}
705+
666706
/**
667707
* Extracts CSS data from a template literal node.
668708
*
@@ -880,15 +920,7 @@ export const buildCss = (node: t.Expression | t.Expression[], meta: Metadata): C
880920
}
881921

882922
if (t.isMemberExpression(node)) {
883-
const bindingIdentifier = findBindingIdentifier(node);
884-
if (bindingIdentifier && meta.state.cssMap[bindingIdentifier.name]) {
885-
return {
886-
css: [{ type: 'map', expression: node, name: bindingIdentifier.name, css: '' }],
887-
variables: [],
888-
};
889-
}
890-
const { value, meta: updatedMeta } = evaluateExpression(node, meta);
891-
return buildCss(value, updatedMeta);
923+
return extractMemberExpression(node, meta);
892924
}
893925

894926
if (t.isArrowFunctionExpression(node)) {
@@ -903,6 +935,10 @@ export const buildCss = (node: t.Expression | t.Expression[], meta: Metadata): C
903935
if (t.isConditionalExpression(node.body)) {
904936
return extractConditionalExpression(node.body, meta);
905937
}
938+
939+
if (t.isMemberExpression(node.body)) {
940+
return extractMemberExpression(node.body, meta);
941+
}
906942
}
907943

908944
if (t.isIdentifier(node)) {

0 commit comments

Comments
 (0)