Repository: facebook/stylex (js)

Issue:
Using dynamic styles with StyleX constants from defineConsts
### Describe the issue

When using a dynamic function as a style value in `stylex.create` together with a constants from a StyleX constants file (`defineConsts`), the following error occurs:

```bash
/stylex/packages/@stylexjs/babel-plugin/__tests__/__fixtures__/main.stylex.js: Invalid pseudo or at-rule.

      68 |       )
      69 |     ) {
    > 70 |       throw new Error(messages.INVALID_PSEUDO_OR_AT_RULE);
         |             ^
      71 |     }
      72 |     if (conditions.includes(key)) {
      73 |       throw new Error(messages.DUPLICATE_CONDITIONAL);

      at validateConditionalStyles (src/shared/preprocess-rules/basic-validation.js:70:13)
      at validateConditionalStyles (src/shared/preprocess-rules/basic-validation.js:42:9)
      at styleXCreateSet (src/shared/stylex-create.js:57:22)
      at transformStyleXCreate (src/visitors/stylex-create.js:169:19)
      at PluginPass.CallExpression (src/index.js:311:30)
      at call (../../../node_modules/@babel/traverse/src/visitors.ts:303:14)

```

### Expected behavior

Correct style transformation using constants as dynamic styles

### Steps to reproduce

Add styles as in `test case` section

### Test case

```js
// packages/@stylexjs/babel-plugin/__tests__/transform-stylex-defineConsts-test.js

test('dynamic styles with StyleX constants', () => {
      const { code, } = transformWithInlineConsts(`
        import * as stylex from '@stylexjs/stylex';
        import { mediaQueries } from './constants.stylex';

        export const styles = stylex.create({
          nodeEnd: (animationDuration) => ({
            transition: {
              [mediaQueries.reduceMotion]: 'none',
              default: \`transform \${animationDuration}ms ease-in-out\`,
            },
          }),
        });
      `);

      expect(code).toMatchInlineSnapshot(`
        "
          // Transformation result
        "
      `);
    });
```

### Additional comments

Thanks to @martinbooth for discovering and [reporting](https://github.com/Dwlad90/stylex-swc-plugin/issues/504) this bug 


Patch:
diff --git a/packages/@stylexjs/babel-plugin/src/visitors/parse-stylex-create-arg.js b/packages/@stylexjs/babel-plugin/src/visitors/parse-stylex-create-arg.js
index 701d9112c..e2fc5c145 100644
--- a/packages/@stylexjs/babel-plugin/src/visitors/parse-stylex-create-arg.js
+++ b/packages/@stylexjs/babel-plugin/src/visitors/parse-stylex-create-arg.js
@@ -150,13 +150,18 @@ function evaluatePartialObjectRecursively(
         return { confident: false, deopt: keyResult.deopt, value: null };
       }
       let key = keyResult.value;
-      if (key.startsWith('var(') && key.endsWith(')')) {
-        key = key.slice(4, -1);
-      }
 
       const valuePath: NodePath<t.Expression | t.PatternLike> =
         prop.get('value');
 
+      if (key.startsWith('var(') && key.endsWith(')')) {
+        const inner = key.slice(4, -1);
+        // When the `keyPath` is not empty, the var(--hash) is a `defineConsts` at-rule placeholder and must be kept intact.
+        if (keyPath.length === 0) {
+          key = inner;
+        }
+      }
+
       if (valuePath.isObjectExpression()) {
         const result = evaluatePartialObjectRecursively(
           valuePath,


Test patch:
diff --git a/packages/@stylexjs/babel-plugin/__tests__/transform-stylex-defineConsts-test.js b/packages/@stylexjs/babel-plugin/__tests__/transform-stylex-defineConsts-test.js
index 0e3ef6cac..a6a121d84 100644
--- a/packages/@stylexjs/babel-plugin/__tests__/transform-stylex-defineConsts-test.js
+++ b/packages/@stylexjs/babel-plugin/__tests__/transform-stylex-defineConsts-test.js
@@ -373,25 +373,128 @@ describe('@stylexjs/babel-plugin', () => {
       `);
 
       expect(code).toMatchInlineSnapshot(`
+            `);
+    });
+
+    test('works with dynamic styles constants', () => {
+      const { code, metadata } = transformWithInlineConsts(`
+        import * as stylex from '@stylexjs/stylex';
+        import { colors } from './constants.stylex';
+
+        export const styles = stylex.create({
+          node: (padding) => ({
+            padding: padding,
+            color: colors.background,
+          }),
+        });
+      `);
+
+      expect(code).toMatchInlineSnapshot(`
+        "import * as stylex from '@stylexjs/stylex';
+        import { colors } from './constants.stylex';
+        const _temp = {
+          kMwMTN: "xy1iwrb",
+          "$$css": true
+        };
+        export const styles = {
+          node: padding => [_temp, {
+            kmVPX3: padding != null ? "x1fozly0" : padding,
+            $$css: true
+          }, {
+            "--x-padding": (val => typeof val === "number" ? val + "px" : val != null ? val : undefined)(padding)
+          }]
+        };"
+      `);
+
+      expect(metadata).toMatchInlineSnapshot(`
+        {
+          "stylex": [
+            [
+              "x1fozly0",
+              {
+                "ltr": ".x1fozly0{padding:var(--x-padding)}",
+                "rtl": null,
+              },
+              1000,
+            ],
+            [
+              "xy1iwrb",
+              {
+                "ltr": ".xy1iwrb{color:var(--x180gk19)}",
+                "rtl": null,
+              },
+              3000,
+            ],
+            [
+              "--x-padding",
+              {
+                "ltr": "@property --x-padding { syntax: "*"; inherits: false;}",
+                "rtl": null,
+              },
+              0,
+            ],
+          ],
+        }
       `);
     });
 
-    test.skip('works with dynamic styles', () => {
-      const { code } = transformWithInlineConsts(`
+    test('works with dynamic styles at-rules', () => {
+      const { code, metadata } = transformWithInlineConsts(`
         import * as stylex from '@stylexjs/stylex';
         import { breakpoints } from './constants.stylex';
 
         export const styles = stylex.create({
-          nodeEnd: (animationDuration) => ({
-            transition: {
-              [breakpoints.small]: 'none',
-              default: \`transform \${animationDuration}ms ease-in-out\`,
+          node: (color) => ({
+            color: {
+              [breakpoints.small]: 'blue',
+              default: color,
             },
           }),
         });
       `);
 
       expect(code).toMatchInlineSnapshot(`
+        "import * as stylex from '@stylexjs/stylex';
+        import { breakpoints } from './constants.stylex';
+        export const styles = {
+          node: color => [{
+            kMwMTN: "xbs0o1n " + (color != null ? "x3d248p" : color),
+            $$css: true
+          }, {
+            "--x-4xs81a": color != null ? color : undefined
+          }]
+        };"
+      `);
+
+      expect(metadata).toMatchInlineSnapshot(`
+        {
+          "stylex": [
+            [
+              "xbs0o1n",
+              {
+                "ltr": "var(--x1r2wpmh){.xbs0o1n.xbs0o1n{color:blue}}",
+                "rtl": null,
+              },
+              6000,
+            ],
+            [
+              "x3d248p",
+              {
+                "ltr": ".x3d248p{color:var(--x-4xs81a)}",
+                "rtl": null,
+              },
+              3000,
+            ],
+            [
+              "--x-4xs81a",
+              {
+                "ltr": "@property --x-4xs81a { syntax: "*"; inherits: false;}",
+                "rtl": null,
+              },
+              0,
+            ],
+          ],
+        }
       `);
     });