Repository: styled-components/babel-plugin-styled-components
Pull request #335 (2021-06-29 16:10:31)

Teach plugin about interpolated object keys


Patch:
diff --git a/package.json b/package.json
index 4f36b8b..7d07855 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,5 @@
 {
-  "version": "1.12.1",
+  "version": "1.13.0-test",
   "name": "babel-plugin-styled-components",
   "description": "Improve the debugging experience and add server-side rendering support to styled-components",
   "repository": "styled-components/babel-plugin-styled-components",
diff --git a/src/visitors/transpileCssProp.js b/src/visitors/transpileCssProp.js
index 1b88130..d694523 100644
--- a/src/visitors/transpileCssProp.js
+++ b/src/visitors/transpileCssProp.js
@@ -19,7 +19,10 @@ const getName = (node, t) => {
 const getNameExpression = (node, t) => {
   if (typeof node.name === 'string') return t.identifier(node.name)
   if (t.isJSXMemberExpression(node)) {
-    return t.memberExpression(getNameExpression(node.object, t), t.identifier(node.property.name))
+    return t.memberExpression(
+      getNameExpression(node.object, t),
+      t.identifier(node.property.name)
+    )
   }
   throw path.buildCodeFrameError(
     `Cannot infer name expression from node with type "${node.type}". Please submit an issue at github.com/styled-components/babel-plugin-styled-components with your code so we can take a look at your use case!`
@@ -44,14 +47,16 @@ export default t => (path, state) => {
       nameHint: 'styled',
     })
 
-    importName = t.identifier(importLocalName('default', state, { bypassCache: true }))
+    importName = t.identifier(
+      importLocalName('default', state, { bypassCache: true })
+    )
   }
 
   if (!t.isIdentifier(importName)) importName = t.identifier(importName)
 
   const elem = path.parentPath
   const name = getName(elem.node.name, t)
-  const nameExpression = getNameExpression(elem.node.name, t);
+  const nameExpression = getNameExpression(elem.node.name, t)
   const id = path.scope.generateUidIdentifier(
     'Styled' + name.replace(/^([a-z])/, (match, p1) => p1.toUpperCase())
   )
@@ -119,7 +124,7 @@ export default t => (path, state) => {
   if (t.isObjectExpression(css)) {
     /**
      * for objects as CSS props, we have to recurse through the object and replace any
-     * object value scope references with generated props similar to how the template
+     * object key/value scope references with generated props similar to how the template
      * literal transform above creates dynamic interpolations
      */
     const p = t.identifier('p')
@@ -129,6 +134,25 @@ export default t => (path, state) => {
       acc,
       property
     ) {
+      /**
+       * handle potential object key interpolations
+       */
+      if (
+        t.isMemberExpression(property.key) ||
+        t.isCallExpression(property.key)
+      ) {
+        const name = path.scope.generateUidIdentifier('css')
+
+        elem.node.attributes.push(
+          t.jSXAttribute(
+            t.jSXIdentifier(name.name),
+            t.jSXExpressionContainer(property.key)
+          )
+        )
+
+        property.key = t.memberExpression(p, name)
+      }
+
       if (t.isObjectExpression(property.value)) {
         // recurse for objects within objects (e.g. {'::before': { content: x }})
         property.value.properties = property.value.properties.reduce(


Test patch:
diff --git a/test/fixtures/transpile-css-prop/code.js b/test/fixtures/transpile-css-prop/code.js
index d8b6051..6795efb 100644
--- a/test/fixtures/transpile-css-prop/code.js
+++ b/test/fixtures/transpile-css-prop/code.js
@@ -111,24 +111,58 @@ const ObjectInterpolation = p => {
   const theme = useTheme()
 
   return (
-  <p
-    css={{
-      color: theme.colors.red,
-    }}
-  >
-    H
-  </p>
-)}
+    <p
+      css={{
+        color: theme.colors.red,
+      }}
+    >
+      H
+    </p>
+  )
+}
 
 const ObjectInterpolationCustomComponent = p => {
   const theme = useTheme()
 
   return (
-  <Thing3
-    css={{
-      color: theme.colors.red,
-    }}
-  >
-    H
-  </Thing3>
-)}
+    <Thing3
+      css={{
+        color: theme.colors.red,
+      }}
+    >
+      H
+    </Thing3>
+  )
+}
+
+const ObjectInterpolationInKey = p => {
+  const theme = useTheme()
+
+  return (
+    <Thing3
+      css={{
+        [theme.breakpoints.md]: {
+          color: 'red',
+        },
+      }}
+    >
+      H
+    </Thing3>
+  )
+}
+
+const ObjectFnInterpolationInKey = p => {
+  const theme = useTheme()
+
+  return (
+    <Thing3
+      css={{
+        [theme.breakpoints.md()]: {
+          color: 'red',
+        },
+      }}
+    >
+      H
+    </Thing3>
+  )
+}
diff --git a/test/fixtures/transpile-css-prop/output.js b/test/fixtures/transpile-css-prop/output.js
index aed872e..1bd042f 100644
--- a/test/fixtures/transpile-css-prop/output.js
+++ b/test/fixtures/transpile-css-prop/output.js
@@ -6,6 +6,8 @@ var _templateObject, _templateObject2, _templateObject3, _templateObject4, _temp
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
 
+function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
+
 function _taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(0); } return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }
 
 /*
@@ -103,6 +105,14 @@ var EarlyUsageComponent = function EarlyUsageComponent(p) {
 
 var Thing3 = styled.div(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n  color: blue;\n"])));
 
+var _StyledThing4 = (0, _styledComponents["default"])(Thing3)(_defineProperty({}, p._css7, {
+  color: 'red'
+}));
+
+var _StyledThing3 = (0, _styledComponents["default"])(Thing3)(_defineProperty({}, p._css6, {
+  color: 'red'
+}));
+
 var _StyledThing2 = (0, _styledComponents["default"])(Thing3)(function (p) {
   return {
     color: p._css5
@@ -114,15 +124,29 @@ var _StyledThing = (0, _styledComponents["default"])(Thing3)(_templateObject2 ||
 var ObjectInterpolation = function ObjectInterpolation(p) {
   var theme = useTheme();
   return <_StyledP12 _css4={theme.colors.red}>
-    H
-  </_StyledP12>;
+      H
+    </_StyledP12>;
 };
 
 var ObjectInterpolationCustomComponent = function ObjectInterpolationCustomComponent(p) {
   var theme = useTheme();
   return <_StyledThing2 _css5={theme.colors.red}>
-    H
-  </_StyledThing2>;
+      H
+    </_StyledThing2>;
+};
+
+var ObjectInterpolationInKey = function ObjectInterpolationInKey(p) {
+  var theme = useTheme();
+  return <_StyledThing3 _css6={theme.breakpoints.md}>
+      H
+    </_StyledThing3>;
+};
+
+var ObjectFnInterpolationInKey = function ObjectFnInterpolationInKey(p) {
+  var theme = useTheme();
+  return <_StyledThing4 _css7={theme.breakpoints.md()}>
+      H
+    </_StyledThing4>;
 };
 
 var _StyledP = (0, _styledComponents["default"])("p")(_templateObject3 || (_templateObject3 = _taggedTemplateLiteral(["flex: 1;"])));