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

Issue:
Custom identifier doesn't allow hyphens inside a css class
### Describe the bug

Custom identifier function throws an error, when trying to use css class with hyphens inside.

```javascript
const withVanillaExtract = createVanillaExtractPlugin({
    identifiers: ({ hash }) => `prefix-${hash}` //will throw an Error
});
```

It seems the regex detects the false negative result.
```typescript
// packages\css\src\identifier.ts
if (!identifier.match(/^[A-Z_][0-9A-Z_]+$/i)) {
  throw new Error(
    `Identifier function returned invalid indentifier: "${identifier}"`,
  );
}
``` 



### Reproduction

https://github.com/EvgenNoskov/vanilla-css-issue

### System Info

```shell
System:
    OS: Windows 10 10.0.19045
    CPU: (8) x64 Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz
    Memory: 7.59 GB / 31.94 GB
  Binaries:
    Node: 20.10.0 - C:\Program Files\nodejs\node.EXE
    Yarn: 1.22.10 - ~\AppData\Roaming\npm\yarn.CMD
    npm: 10.2.3 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Chrome: 120.0.6099.71
    Edge: Spartan (44.19041.3636.0), Chromium (120.0.2210.61)
    Internet Explorer: 11.0.19041.3636
  npmPackages:
    @vanilla-extract/css: ^1.14.0 => 1.14.0
    @vanilla-extract/next-plugin: ^2.3.2 => 2.3.2
```


### Used Package Manager

npm

### Logs

```shell
Error: Identifier function returned invalid indentifier: "prefix-1gezp2d0"
Import trace for requested module:
./src/components/MyComponent.css.ts
./src/components/MyComponent.tsx
./src/app/page.tsx
```


### 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:
Allow hyphens inside css classes
Fixes #1253

Patch:
diff --git a/.changeset/strong-ducks-dream.md b/.changeset/strong-ducks-dream.md
new file mode 100644
index 0000000..4c8efd5
--- /dev/null
+++ b/.changeset/strong-ducks-dream.md
@@ -0,0 +1,10 @@
+---
+'@vanilla-extract/css': patch
+'@vanilla-extract/esbuild-plugin': patch
+'@vanilla-extract/next-plugin': patch
+'@vanilla-extract/rollup-plugin': patch
+'@vanilla-extract/vite-plugin': patch
+'@vanilla-extract/webpack-plugin': patch
+---
+
+Allow hyphens in class names when using a custom identifier
diff --git a/packages/css/src/identifier.ts b/packages/css/src/identifier.ts
index 175f6a0..1209cdc 100644
--- a/packages/css/src/identifier.ts
+++ b/packages/css/src/identifier.ts
@@ -75,7 +75,7 @@ export function generateIdentifier(
       packageName,
     });
 
-    if (!identifier.match(/^[A-Z_][0-9A-Z_]+$/i)) {
+    if (!identifier.match(/^[A-Z_][0-9A-Z_-]+$/i)) {
       throw new Error(
         `Identifier function returned invalid indentifier: "${identifier}"`,
       );
diff --git a/site/docs/integrations/webpack.md b/site/docs/integrations/webpack.md
index 6665aa1..39e0a00 100644
--- a/site/docs/integrations/webpack.md
+++ b/site/docs/integrations/webpack.md
@@ -94,7 +94,7 @@ Different formatting of identifiers (e.g. class names, keyframes, CSS Vars, etc)
 - A custom identifier function takes an object parameter with properties `hash`, `filePath`, `debugId`, and `packageName`, and returns a customized identifier. e.g.
 
 ```ts
-VanillaExtractPlugin({
+new VanillaExtractPlugin({
   identifiers: ({ hash }) => `prefix_${hash}`
 });
 ```


Test patch:
diff --git a/packages/css/src/identifier.test.ts b/packages/css/src/identifier.test.ts
index 88a6e88..0eebc62 100644
--- a/packages/css/src/identifier.test.ts
+++ b/packages/css/src/identifier.test.ts
@@ -91,6 +91,15 @@ describe('identifier', () => {
       );
     });
 
+    it('should allow hyphens in a class name', () => {
+      expect(generateIdentifier('a-b')).toMatchInlineSnapshot(
+        `"abc_a-b_s0xkdr1_packagetest_file"`,
+      );
+      expect(generateIdentifier('a-b-c')).toMatchInlineSnapshot(
+        `"abc_a-b-c_s0xkdr2_packagetest_file"`,
+      );
+    });
+
     it('rejects invalid identifiers', () => {
       // getIdentOption() does not remove spaces from the debug info so the
       // resulting identifier should be invalid here.