From 24e955b9a6f517f7769a489320d456f61a0e24b0 Mon Sep 17 00:00:00 2001 From: Kylor Hall Date: Tue, 17 Dec 2024 18:46:47 +0900 Subject: [PATCH 1/3] Fix ternaries by extracting nested member expressions inside of conditional expressions. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes https://github.com/atlassian-labs/compiled/issues/1750 We simply copy the direct `buildCss > MemberExpression > …` extraction into a new path through `buildCss > ConditionalExpression > extractConditionalExpression(…) > MemberExpression > …`. --- .changeset/cyan-waves-rest.md | 5 ++ .../src/css-map/__tests__/index.test.ts | 49 +++++++++++++++++-- .../babel-plugin/src/utils/css-builders.ts | 30 ++++++++---- 3 files changed, 72 insertions(+), 12 deletions(-) create mode 100644 .changeset/cyan-waves-rest.md diff --git a/.changeset/cyan-waves-rest.md b/.changeset/cyan-waves-rest.md new file mode 100644 index 000000000..bd9188dd9 --- /dev/null +++ b/.changeset/cyan-waves-rest.md @@ -0,0 +1,5 @@ +--- +'@compiled/babel-plugin': minor +--- + +Fix supporting ternaries referencing cssMap style objects when extracting styles. diff --git a/packages/babel-plugin/src/css-map/__tests__/index.test.ts b/packages/babel-plugin/src/css-map/__tests__/index.test.ts index 95c8d9185..7036eecc4 100644 --- a/packages/babel-plugin/src/css-map/__tests__/index.test.ts +++ b/packages/babel-plugin/src/css-map/__tests__/index.test.ts @@ -22,11 +22,18 @@ describe('css map basic functionality', () => { import { cssMap } from '@compiled/react'; const styles = cssMap(${styles}); + + const Component = () =>
+ + +
`); - expect(actual).toInclude( - 'const styles={danger:"_syaz5scu _bfhk5scu",success:"_syazbf54 _bfhkbf54"};' - ); + expect(actual).toIncludeMultiple([ + 'const styles={danger:"_syaz5scu _bfhk5scu",success:"_syazbf54 _bfhkbf54"};', + '', + '', + ]); }); it('should transform css map even with an empty object', () => { @@ -45,6 +52,42 @@ describe('css map basic functionality', () => { expect(actual).toInclude('const styles={danger:"",success:"_syazbf54 _bfhkbf54"};'); }); + it('should transform ternary-based conditional referencing cssMap declarations', () => { + const actual = transform(` + import { cssMap } from '@compiled/react'; + + const styles = cssMap({ + root: { display: 'block' }, + positive: { background: 'white', color: 'black' }, + negative: { background: 'green', color: 'red' }, + bold: { fontWeight: 'bold' }, + normal: { fontWeight: 'normal' }, + }); + + const Component = ({ isPrimary, weight }) => ( +
+ ); + `); + + expect(actual).toIncludeMultiple([ + '._1e0c1ule{display:block}', + '._bfhk1x77{background-color:white}', + '._syaz11x8{color:black}', + '._bfhkbf54{background-color:green}', + '._syaz5scu{color:red}', + '._k48p8n31{font-weight:bold}', + '._k48p4jg8{font-weight:normal}', + 'const styles={root:"_1e0c1ule",positive:"_bfhk1x77 _syaz11x8",negative:"_bfhkbf54 _syaz5scu",bold:"_k48p8n31",normal:"_k48p4jg8"}', + '
', + ]); + }); + it('should error out if variants are not defined at the top-most scope of the module.', () => { expect(() => { transform(` diff --git a/packages/babel-plugin/src/utils/css-builders.ts b/packages/babel-plugin/src/utils/css-builders.ts index d35a9b293..cd2b2ea1d 100644 --- a/packages/babel-plugin/src/utils/css-builders.ts +++ b/packages/babel-plugin/src/utils/css-builders.ts @@ -381,6 +381,8 @@ const extractConditionalExpression = (node: t.ConditionalExpression, meta: Metad } } else if (t.isConditionalExpression(pathNode)) { cssOutput = extractConditionalExpression(pathNode, meta); + } else if (t.isMemberExpression(pathNode)) { + cssOutput = extractMemberExpression(pathNode, meta); } if (cssOutput) { @@ -663,6 +665,24 @@ const extractObjectExpression = (node: t.ObjectExpression, meta: Metadata): CSSO return { css: mergeSubsequentUnconditionalCssItems(css), variables }; }; +/** + * Extracts CSS data from a member expression node (eg. `styles.primary`) + * + * @param node Node we're interested in extracting CSS from. + * @param meta {Metadata} Useful metadata that can be used during the transformation + */ +const extractMemberExpression = (node: t.MemberExpression, meta: Metadata): CSSOutput => { + const bindingIdentifier = findBindingIdentifier(node); + if (bindingIdentifier && meta.state.cssMap[bindingIdentifier.name]) { + return { + css: [{ type: 'map', expression: node, name: bindingIdentifier.name, css: '' }], + variables: [], + }; + } + const { value, meta: updatedMeta } = evaluateExpression(node, meta); + return buildCss(value, updatedMeta); +}; + /** * Extracts CSS data from a template literal node. * @@ -880,15 +900,7 @@ export const buildCss = (node: t.Expression | t.Expression[], meta: Metadata): C } if (t.isMemberExpression(node)) { - const bindingIdentifier = findBindingIdentifier(node); - if (bindingIdentifier && meta.state.cssMap[bindingIdentifier.name]) { - return { - css: [{ type: 'map', expression: node, name: bindingIdentifier.name, css: '' }], - variables: [], - }; - } - const { value, meta: updatedMeta } = evaluateExpression(node, meta); - return buildCss(value, updatedMeta); + return extractMemberExpression(node, meta); } if (t.isArrowFunctionExpression(node)) { From 3f04d6d6e228bbee7a80a03762833cfc9301dd9c Mon Sep 17 00:00:00 2001 From: Kylor Hall Date: Wed, 18 Dec 2024 10:34:52 +0900 Subject: [PATCH 2/3] Fixup recursive scenario --- .../tagged-template-expression.test.ts | 2 +- .../babel-plugin/src/utils/css-builders.ts | 34 ++++++++++++++++--- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/packages/babel-plugin/src/styled/__tests__/tagged-template-expression.test.ts b/packages/babel-plugin/src/styled/__tests__/tagged-template-expression.test.ts index 60f7eedd3..59d291773 100644 --- a/packages/babel-plugin/src/styled/__tests__/tagged-template-expression.test.ts +++ b/packages/babel-plugin/src/styled/__tests__/tagged-template-expression.test.ts @@ -908,7 +908,7 @@ describe('styled tagged template expression', () => { props.isPrimary ? \` background-color: \${({ isLoading }) => (isLoading ? colors.N20 : colors.N40)}; - color: \${({ loading: l }) => (l ? colors.N50 : colors.N10)}; + color: \${({ loading }) => (loading ? colors.N50 : colors.N10)}; border-color: \${(propz) => (propz.loading ? colors.N100 : colors.N200)}; \` : 'color: black' }; diff --git a/packages/babel-plugin/src/utils/css-builders.ts b/packages/babel-plugin/src/utils/css-builders.ts index cd2b2ea1d..f0d6d6f0b 100644 --- a/packages/babel-plugin/src/utils/css-builders.ts +++ b/packages/babel-plugin/src/utils/css-builders.ts @@ -382,7 +382,7 @@ const extractConditionalExpression = (node: t.ConditionalExpression, meta: Metad } else if (t.isConditionalExpression(pathNode)) { cssOutput = extractConditionalExpression(pathNode, meta); } else if (t.isMemberExpression(pathNode)) { - cssOutput = extractMemberExpression(pathNode, meta); + cssOutput = extractMemberExpression(pathNode, meta, false); } if (cssOutput) { @@ -670,8 +670,23 @@ const extractObjectExpression = (node: t.ObjectExpression, meta: Metadata): CSSO * * @param node Node we're interested in extracting CSS from. * @param meta {Metadata} Useful metadata that can be used during the transformation + * @param fallbackToEvaluate {Boolean} Whether to fallback to re-evaluating the expression if it's not a cssMap identifier */ -const extractMemberExpression = (node: t.MemberExpression, meta: Metadata): CSSOutput => { +function extractMemberExpression( + node: t.MemberExpression, + meta: Metadata, + fallbackToEvaluate?: true +): CSSOutput; +function extractMemberExpression( + node: t.MemberExpression, + meta: Metadata, + fallbackToEvaluate: false +): CSSOutput | undefined; +function extractMemberExpression( + node: t.MemberExpression, + meta: Metadata, + fallbackToEvaluate = true +): CSSOutput | undefined { const bindingIdentifier = findBindingIdentifier(node); if (bindingIdentifier && meta.state.cssMap[bindingIdentifier.name]) { return { @@ -679,9 +694,14 @@ const extractMemberExpression = (node: t.MemberExpression, meta: Metadata): CSSO variables: [], }; } - const { value, meta: updatedMeta } = evaluateExpression(node, meta); - return buildCss(value, updatedMeta); -}; + + if (fallbackToEvaluate) { + const { value, meta: updatedMeta } = evaluateExpression(node, meta); + return buildCss(value, updatedMeta); + } + + return undefined; +} /** * Extracts CSS data from a template literal node. @@ -915,6 +935,10 @@ export const buildCss = (node: t.Expression | t.Expression[], meta: Metadata): C if (t.isConditionalExpression(node.body)) { return extractConditionalExpression(node.body, meta); } + + if (t.isMemberExpression(node.body)) { + return extractMemberExpression(node.body, meta); + } } if (t.isIdentifier(node)) { From 5bcb62a2d5b3ecab39e1178ff7b6cc4d49bc7642 Mon Sep 17 00:00:00 2001 From: Kylor Hall Date: Wed, 18 Dec 2024 17:44:29 +0900 Subject: [PATCH 3/3] Revert unintended minor testing change --- .../src/styled/__tests__/tagged-template-expression.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-plugin/src/styled/__tests__/tagged-template-expression.test.ts b/packages/babel-plugin/src/styled/__tests__/tagged-template-expression.test.ts index 59d291773..60f7eedd3 100644 --- a/packages/babel-plugin/src/styled/__tests__/tagged-template-expression.test.ts +++ b/packages/babel-plugin/src/styled/__tests__/tagged-template-expression.test.ts @@ -908,7 +908,7 @@ describe('styled tagged template expression', () => { props.isPrimary ? \` background-color: \${({ isLoading }) => (isLoading ? colors.N20 : colors.N40)}; - color: \${({ loading }) => (loading ? colors.N50 : colors.N10)}; + color: \${({ loading: l }) => (l ? colors.N50 : colors.N10)}; border-color: \${(propz) => (propz.loading ? colors.N100 : colors.N200)}; \` : 'color: black' };