Repository: vanilla-extract-css/vanilla-extract (ts)

Issue:
Inconsistent debug identifiers for createTheme between client and server on Next.js dev server
### Describe the bug

`@vanilla-extract/babel-plugin-debug-ids` fails to generate consistent debug identifiers for `createTheme` between server and client environments when run in `next dev`. The code generated for the server will correctly generate the debug id as `theme_createThemeClass__XXXXXXXX`. The code generated on for client however will generate a debug id of `theme_ref__XXXXXXXX`. This means that the generated style sheet has a different class than the class that is applied to the element, causing styles to fail and a Next.js client/server mismatch.

The issue involves [this section](https://github.com/vanilla-extract-css/vanilla-extract/blob/9312b66e5bd67942b7929a6b93e0ad2181b2e0ba/packages/babel-plugin-debug-ids/src/index.ts#L77-L99) in `@vanilla-extract/babel-plugin-debug-ids` that is responsible for handling a specific export format of `createTheme`. This code only handles one scenario:

```js
export const [themeClass, vars] = createTheme({});
```

when it's compiled to:

```js
var _createTheme = createTheme({}),
  _createTheme2 = _slicedToArray(_createTheme, 2),
  themeClass = _createTheme2[0],
  vars = _createTheme2[1];
```

But if you look at the generated code output by Next.js' SWC, it is actually:

```js
var ref = _slicedToArray(createTheme({}), 2);
export var themeClass = ref[0],
  vars = ref[1];
```

And if the consumer instead uses:

```js
const [themeClass, vars] = createTheme({});
export { themeClass, vars };
```

then there is yet another compiled permutation:

```js
var ref = _slicedToArray(createTheme({}), 2),
  themeClass = ref[0],
  vars = ref[1];
export { themeClass, vars };
```

---

I believe this issue arrose in `@vanilla-extract/integration@6.0.0` as part of the removal of `@vanilla-extract/babel-plugin` in [#827](https://github.com/vanilla-extract-css/vanilla-extract/pull/827). I'm not exactly sure what is causing the compiled code to change structure though, potentially something in Next.js' SWC compilation process?

TL;DR: `@vanilla-extract/babel-plugin-debug-ids` needs to be updated to handle a wider range of scenarios for `createTheme`.


### Reproduction

https://github.com/zbauman3/demos/tree/vanilla-extract-next-dev

### System Info

```shell
System:
    OS: macOS 12.6
    CPU: (10) arm64 Apple M1 Pro
    Memory: 1.58 GB / 32.00 GB
    Shell: 5.8.1 - /bin/zsh
  Binaries:
    Node: 16.13.1 - ~/.asdf/installs/nodejs/16.13.1/bin/node
    Yarn: 3.2.0 - /opt/homebrew/bin/yarn
    npm: 8.1.2 - ~/.asdf/plugins/nodejs/shims/npm
  Browsers:
    Chrome: 107.0.5304.121
    Firefox: 107.0
    Safari: 16.0
```


### Used Package Manager

yarn

### Logs

```shell
next-dev.js?3515:24 Warning: Prop `className` did not match. Server: "theme_systemStylesClass__1kzsoa60" Client: "theme_ref__1kzsoa60"
```


### Validations

- [X] Check that there isn't [already an issue](https://github.com/vanilla-extract-css/vanilla-extract/issues) that reports the same bug to avoid creating a duplicate.
- [X] The provided reproduction is a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) of the bug.

Pull request:
Handle alternative compiled forms of array destructured `createTheme`
Fixes #935.

Adds logic to handle the extra 2 cases mentioned in the issue. Tested against [the reproduction](https://github.com/zbauman3/demos/tree/vanilla-extract-next-dev).

Patch:
diff --git a/.changeset/popular-kings-rhyme.md b/.changeset/popular-kings-rhyme.md
new file mode 100644
index 0000000..a43aaed
--- /dev/null
+++ b/.changeset/popular-kings-rhyme.md
@@ -0,0 +1,5 @@
+---
+'@vanilla-extract/babel-plugin-debug-ids': patch
+---
+
+Handle alternative compiled forms of array destructured `createTheme`
diff --git a/packages/babel-plugin-debug-ids/src/index.ts b/packages/babel-plugin-debug-ids/src/index.ts
index b8cd650..9d65e7c 100644
--- a/packages/babel-plugin-debug-ids/src/index.ts
+++ b/packages/babel-plugin-debug-ids/src/index.ts
@@ -93,27 +93,72 @@ const getDebugId = (path: NodePath<t.CallExpression>) => {
     return;
   }
 
-  // Special case: Handle `export const [themeClass, vars] = createTheme({});`
-  // when it's already been compiled into this:
+  // Special case 1: Handle `export const [themeClass, vars] = createTheme({});`
+  // when it's already been compiled into one of the following forms:
   //
   // var _createTheme = createTheme({}),
   //   _createTheme2 = _slicedToArray(_createTheme, 2),
   //   themeClass = _createTheme2[0],
   //   vars = _createTheme2[1];
-  if (
-    t.isVariableDeclaration(firstRelevantParentPath.parent) &&
-    firstRelevantParentPath.parent.declarations.length === 4
-  ) {
-    const [themeDeclarator, , classNameDeclarator] =
-      firstRelevantParentPath.parent.declarations;
-
-    if (
-      t.isCallExpression(themeDeclarator.init) &&
-      t.isIdentifier(themeDeclarator.init.callee, { name: 'createTheme' }) &&
-      t.isVariableDeclarator(classNameDeclarator) &&
-      t.isIdentifier(classNameDeclarator.id)
-    ) {
-      return classNameDeclarator.id.name;
+  if (t.isVariableDeclaration(firstRelevantParentPath.parent)) {
+    if (firstRelevantParentPath.parent.declarations.length === 4) {
+      const [themeDeclarator, , classNameDeclarator] =
+        firstRelevantParentPath.parent.declarations;
+
+      if (
+        t.isCallExpression(themeDeclarator.init) &&
+        t.isIdentifier(themeDeclarator.init.callee, { name: 'createTheme' }) &&
+        t.isIdentifier(classNameDeclarator.id)
+      ) {
+        return classNameDeclarator.id.name;
+      }
+    }
+    // alternative compiled form:
+    //
+    // var ref = _slicedToArray(createTheme({}), 2);
+    // export var themeClass = ref[0],
+    //   vars = ref[1];
+    else if (firstRelevantParentPath.parent.declarations.length === 1) {
+      const [themeDeclarator] = firstRelevantParentPath.parent.declarations;
+      const nextSibling =
+        firstRelevantParentPath.parentPath?.getNextSibling().node;
+
+      if (
+        t.isCallExpression(themeDeclarator.init) &&
+        t.isCallExpression(themeDeclarator.init.arguments[0]) &&
+        t.isIdentifier(themeDeclarator.init.arguments[0].callee, {
+          name: 'createTheme',
+        }) &&
+        t.isExportNamedDeclaration(nextSibling) &&
+        t.isVariableDeclaration(nextSibling.declaration) &&
+        t.isVariableDeclarator(nextSibling.declaration.declarations[0]) &&
+        t.isIdentifier(nextSibling.declaration.declarations[0].id)
+      ) {
+        return nextSibling.declaration.declarations[0].id.name;
+      }
+    }
+    // Special case 2: Handle `const [themeClass, vars] = createTheme({});
+    //                        export { themeClass, vars };`
+    // when compiled into the following:
+    //
+    // var ref = _slicedToArray(createTheme({}), 2),
+    //   myThemeClass = ref[0],
+    //   vars = ref[1];
+    // export { themeClass, vars };
+    else if (firstRelevantParentPath.parent.declarations.length === 3) {
+      const [themeDeclarator, classNameDeclarator] =
+        firstRelevantParentPath.parent.declarations;
+
+      if (
+        t.isCallExpression(themeDeclarator.init) &&
+        t.isCallExpression(themeDeclarator.init.arguments[0]) &&
+        t.isIdentifier(themeDeclarator.init.arguments[0].callee, {
+          name: 'createTheme',
+        }) &&
+        t.isIdentifier(classNameDeclarator.id)
+      ) {
+        return classNameDeclarator.id.name;
+      }
     }
   }
 


Test patch:
diff --git a/packages/babel-plugin-debug-ids/src/index.test.ts b/packages/babel-plugin-debug-ids/src/index.test.ts
index db89088..61eb3d8 100644
--- a/packages/babel-plugin-debug-ids/src/index.test.ts
+++ b/packages/babel-plugin-debug-ids/src/index.test.ts
@@ -413,7 +413,7 @@ describe('babel plugin', () => {
     `);
   });
 
-  it('should handle createTheme using destructuring when already compiled', () => {
+  it('should handle createTheme using destructuring when already compiled scenario 1', () => {
     const source = `
       import { createTheme } from '@vanilla-extract/css';
 
@@ -432,6 +432,42 @@ describe('babel plugin', () => {
     `);
   });
 
+  it('should handle createTheme using destructuring when already compiled scenario 2', () => {
+    const source = `
+      import { createTheme } from '@vanilla-extract/css';
+
+      var ref = _slicedToArray(createTheme({}), 2);
+      export var myThemeClass = ref[0],
+        vars = ref[1];
+    `;
+
+    expect(transform(source)).toMatchInlineSnapshot(`
+      import { createTheme } from '@vanilla-extract/css';
+      var ref = _slicedToArray(createTheme({}, "myThemeClass"), 2);
+      export var myThemeClass = ref[0],
+        vars = ref[1];
+    `);
+  });
+
+  it('should handle createTheme using destructuring when already compiled scenario 3', () => {
+    const source = `
+      import { createTheme } from '@vanilla-extract/css';
+
+      var ref = _slicedToArray(createTheme({}), 2),
+        myThemeClass = ref[0],
+        vars = ref[1];
+      export { myThemeClass, vars };
+    `;
+
+    expect(transform(source)).toMatchInlineSnapshot(`
+      import { createTheme } from '@vanilla-extract/css';
+      var ref = _slicedToArray(createTheme({}, "myThemeClass"), 2),
+        myThemeClass = ref[0],
+        vars = ref[1];
+      export { myThemeClass, vars };
+    `);
+  });
+
   it('should handle createGlobalTheme', () => {
     const source = `
       import { createGlobalTheme } from '@vanilla-extract/css';