Repository: unplugin/unplugin-vue-components (2025-07-12T20:47:00Z)

Issue:
default resolver failed to resolve components with components folder
### Describe the bug

when root directory contains "(2)", components within components folder are not auto imported.

### Reproduction

https://codesandbox.io/p/devbox/test-2-y5jhnl

### System Info

```Shell
System:
    OS: Linux 6.1 Debian GNU/Linux 12 (bookworm) 12 (bookworm)
    CPU: (2) x64 AMD EPYC
    Memory: 1.53 GB / 4.01 GB
    Container: Yes
    Shell: Unknown
  Binaries:
    Node: 20.12.0 - /usr/local/bin/node
    Yarn: 1.22.19 - /usr/local/bin/yarn
    npm: 10.5.0 - /usr/local/bin/npm
    pnpm: 8.15.6 - /usr/local/share/npm-global/bin/pnpm
```


### Used Package Manager

npm

### Validations

- [X] Follow our [Code of Conduct](https://github.com/antfu/.github/blob/main/CODE_OF_CONDUCT.md)
- [X] Read the [Contributing Guide](https://github.com/antfu/contribute).
- [X] Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
- [X] Check that this is a concrete bug. For Q&A, please open a GitHub Discussion instead.
- [X] The provided reproduction is a [minimal reproducible](https://stackoverflow.com/help/minimal-reproducible-example) of the bug.
项目目录存在英文括号()，AutoComponents本地运行异常
### Describe the bug

项目目录存在英文括号()，AutoComponents本地运行异常
```
import AutoComponents from 'unplugin-vue-components/rspack';
AutoComponents({
    // 自动加载组件的目录配置,默认的为 'src/components'
    dirs: ['src/components'],
    // 组件支持的文件后缀名
    extensions: ['vue', 'jsx', 'tsx'],
    dts: false,
    resolvers: [
      ElementPlusResolver({
        importStyle: 'scss',
      }),
    ],
  }),
```
src/components组件无法自动加载。

### Reproduction

项目(北京)/web

### System Info

```Shell
rsbuild构建vue3项目。
```

### Used Package Manager

npm

### Validations

- [x] Follow our [Code of Conduct](https://github.com/antfu/.github/blob/main/CODE_OF_CONDUCT.md)
- [x] Read the [Contributing Guide](https://github.com/antfu/contribute).
- [x] Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
- [x] Check that this is a concrete bug. For Q&A, please open a GitHub Discussion instead.
- [x] The provided reproduction is a [minimal reproducible](https://stackoverflow.com/help/minimal-reproducible-example) of the bug.


Discussion:

Patch:
diff --git a/src/core/options.ts b/src/core/options.ts
index 4ac7cd9c..699062bc 100644
--- a/src/core/options.ts
+++ b/src/core/options.ts
@@ -3,6 +3,7 @@ import { join, resolve } from 'node:path'
 import { slash, toArray } from '@antfu/utils'
 import { getPackageInfoSync, isPackageExists } from 'local-pkg'
 import { detectTypeImports } from './type-imports/detect'
+import { escapeSpecialChars } from './utils'
 
 export const defaultOptions: Omit<Required<Options>, 'include' | 'exclude' | 'excludeNames' | 'transformer' | 'globs' | 'globsExclude' | 'directives' | 'types' | 'version'> = {
   dirs: 'src/components',
@@ -63,8 +64,8 @@ export function resolveOptions(options: Options, root: string): ResolvedOptions
         i = i.slice(1)
       }
       return resolved.deep
-        ? prefix + slash(join(i, `**/*.${extsGlob}`))
-        : prefix + slash(join(i, `*.${extsGlob}`))
+        ? prefix + escapeSpecialChars(slash(join(i, `**/*.${extsGlob}`)))
+        : prefix + escapeSpecialChars(slash(join(i, `*.${extsGlob}`)))
     })
 
     if (!resolved.extensions.length)
diff --git a/src/core/utils.ts b/src/core/utils.ts
index 53e1c132..ab86231d 100644
--- a/src/core/utils.ts
+++ b/src/core/utils.ts
@@ -242,3 +242,9 @@ export function isExclude(name: string, exclude?: FilterPattern): boolean {
   }
   return false
 }
+
+const ESCAPE_PARENTHESES_REGEX = /[()]/g
+
+export function escapeSpecialChars(str: string): string {
+  return str.replace(ESCAPE_PARENTHESES_REGEX, '\\$&')
+}


Test patch:
diff --git a/test/utils.test.ts b/test/utils.test.ts
index cdabab87..a9f38bf2 100644
--- a/test/utils.test.ts
+++ b/test/utils.test.ts
@@ -1,6 +1,6 @@
 import type { ResolvedOptions } from '../src'
 import { describe, expect, it } from 'vitest'
-import { getNameFromFilePath } from '../src/core/utils'
+import { escapeSpecialChars, getNameFromFilePath } from '../src/core/utils'
 
 describe('getNameFromFilePath', () => {
   const options: Partial<ResolvedOptions> = {
@@ -20,3 +20,9 @@ describe('getNameFromFilePath', () => {
     expect(getNameFromFilePath(inComponentFilePath, options as ResolvedOptions)).toBe('a1-b2-c3-d4-ef-ghi')
   })
 })
+
+describe('escapeSpecialChars', () => {
+  it('should escape parentheses', () => {
+    expect(escapeSpecialChars('component()')).toBe('component\\(\\)')
+  })
+})