From 4559018e7bae2a75a2f43e268ce5ef2be1b1bf08 Mon Sep 17 00:00:00 2001 From: IlyaL Date: Sun, 13 Jul 2025 05:39:48 +0900 Subject: [PATCH 01/13] fix: escape special characters in glob patterns --- src/core/options.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/options.ts b/src/core/options.ts index 4ac7cd9c..31c71a5b 100644 --- a/src/core/options.ts +++ b/src/core/options.ts @@ -2,6 +2,7 @@ import type { ComponentResolver, ComponentResolverObject, Options, ResolvedOptio import { join, resolve } from 'node:path' import { slash, toArray } from '@antfu/utils' import { getPackageInfoSync, isPackageExists } from 'local-pkg' +import { escapePath } from 'tinyglobby' import { detectTypeImports } from './type-imports/detect' export const defaultOptions: Omit, 'include' | 'exclude' | 'excludeNames' | 'transformer' | 'globs' | 'globsExclude' | 'directives' | 'types' | 'version'> = { @@ -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 + join(escapePath(i), `**/*.${extsGlob}`) + : prefix + join(escapePath(i), `*.${extsGlob}`) }) if (!resolved.extensions.length) From 188ffcd65decd2025f497d34db56a1aac7a52b15 Mon Sep 17 00:00:00 2001 From: IlyaL Date: Sun, 13 Jul 2025 15:55:25 +0900 Subject: [PATCH 02/13] wip --- src/core/options.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/options.ts b/src/core/options.ts index 31c71a5b..30852be7 100644 --- a/src/core/options.ts +++ b/src/core/options.ts @@ -64,8 +64,8 @@ export function resolveOptions(options: Options, root: string): ResolvedOptions i = i.slice(1) } return resolved.deep - ? prefix + join(escapePath(i), `**/*.${extsGlob}`) - : prefix + join(escapePath(i), `*.${extsGlob}`) + ? prefix + join(escapePath(slash(i)), `**/*.${extsGlob}`) + : prefix + join(escapePath(slash(i)), `*.${extsGlob}`) }) if (!resolved.extensions.length) From 1a5513bd0a6dd1ec687dbc13cd956f4ec2b71d46 Mon Sep 17 00:00:00 2001 From: IlyaL Date: Sun, 13 Jul 2025 17:11:15 +0900 Subject: [PATCH 03/13] wip --- src/core/options.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/options.ts b/src/core/options.ts index 30852be7..48d08c90 100644 --- a/src/core/options.ts +++ b/src/core/options.ts @@ -64,8 +64,8 @@ export function resolveOptions(options: Options, root: string): ResolvedOptions i = i.slice(1) } return resolved.deep - ? prefix + join(escapePath(slash(i)), `**/*.${extsGlob}`) - : prefix + join(escapePath(slash(i)), `*.${extsGlob}`) + ? prefix + join(escapePath(slash(i)), slash(`**/*.${extsGlob}`)) + : prefix + join(escapePath(slash(i)), slash(`*.${extsGlob}`)) }) if (!resolved.extensions.length) From 582d71164b918ada0015c25311906160b784c6dd Mon Sep 17 00:00:00 2001 From: IlyaL Date: Sun, 13 Jul 2025 17:19:31 +0900 Subject: [PATCH 04/13] wip --- src/core/options.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/options.ts b/src/core/options.ts index 48d08c90..971be284 100644 --- a/src/core/options.ts +++ b/src/core/options.ts @@ -2,7 +2,7 @@ import type { ComponentResolver, ComponentResolverObject, Options, ResolvedOptio import { join, resolve } from 'node:path' import { slash, toArray } from '@antfu/utils' import { getPackageInfoSync, isPackageExists } from 'local-pkg' -import { escapePath } from 'tinyglobby' +import { convertPathToPattern } from 'tinyglobby' import { detectTypeImports } from './type-imports/detect' export const defaultOptions: Omit, 'include' | 'exclude' | 'excludeNames' | 'transformer' | 'globs' | 'globsExclude' | 'directives' | 'types' | 'version'> = { @@ -64,8 +64,8 @@ export function resolveOptions(options: Options, root: string): ResolvedOptions i = i.slice(1) } return resolved.deep - ? prefix + join(escapePath(slash(i)), slash(`**/*.${extsGlob}`)) - : prefix + join(escapePath(slash(i)), slash(`*.${extsGlob}`)) + ? prefix + join(convertPathToPattern(i), slash(`**/*.${extsGlob}`)) + : prefix + join(convertPathToPattern(i), slash(`*.${extsGlob}`)) }) if (!resolved.extensions.length) From 59a2a90f48592c95775b4fd5662366623e70cfcc Mon Sep 17 00:00:00 2001 From: IlyaL Date: Sun, 13 Jul 2025 23:17:43 +0900 Subject: [PATCH 05/13] rollback --- src/core/options.ts | 5 ++--- test/__snapshots__/search.test.ts.snap | 6 ++++++ test/search.test.ts | 1 + 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/core/options.ts b/src/core/options.ts index 971be284..4ac7cd9c 100644 --- a/src/core/options.ts +++ b/src/core/options.ts @@ -2,7 +2,6 @@ import type { ComponentResolver, ComponentResolverObject, Options, ResolvedOptio import { join, resolve } from 'node:path' import { slash, toArray } from '@antfu/utils' import { getPackageInfoSync, isPackageExists } from 'local-pkg' -import { convertPathToPattern } from 'tinyglobby' import { detectTypeImports } from './type-imports/detect' export const defaultOptions: Omit, 'include' | 'exclude' | 'excludeNames' | 'transformer' | 'globs' | 'globsExclude' | 'directives' | 'types' | 'version'> = { @@ -64,8 +63,8 @@ export function resolveOptions(options: Options, root: string): ResolvedOptions i = i.slice(1) } return resolved.deep - ? prefix + join(convertPathToPattern(i), slash(`**/*.${extsGlob}`)) - : prefix + join(convertPathToPattern(i), slash(`*.${extsGlob}`)) + ? prefix + slash(join(i, `**/*.${extsGlob}`)) + : prefix + slash(join(i, `*.${extsGlob}`)) }) if (!resolved.extensions.length) diff --git a/test/__snapshots__/search.test.ts.snap b/test/__snapshots__/search.test.ts.snap index 4bb99427..1f3101ed 100644 --- a/test/__snapshots__/search.test.ts.snap +++ b/test/__snapshots__/search.test.ts.snap @@ -123,6 +123,12 @@ exports[`search > should with namespace 1`] = ` `; exports[`search > should work 1`] = ` +[ + "/Users/ilyal/f/unplugin-vue-components/examples/vite-vue3/src/components/**/*.vue", +] +`; + +exports[`search > should work 2`] = ` [ { "as": "Avatar", diff --git a/test/search.test.ts b/test/search.test.ts index 3277da05..32d1ad36 100644 --- a/test/search.test.ts +++ b/test/search.test.ts @@ -18,6 +18,7 @@ describe('search', () => { ctx.setRoot(root) ctx.searchGlob() + expect(ctx.options.globs).toMatchSnapshot() expect(cleanup(ctx.componentNameMap)).toMatchSnapshot() }) From f57a50d9919416d8dbde8273a8f638767ae67aaf Mon Sep 17 00:00:00 2001 From: IlyaL Date: Sun, 13 Jul 2025 23:27:08 +0900 Subject: [PATCH 06/13] test-window-path --- src/core/options.ts | 5 +++-- test/search.test.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/core/options.ts b/src/core/options.ts index 4ac7cd9c..a9839d78 100644 --- a/src/core/options.ts +++ b/src/core/options.ts @@ -2,6 +2,7 @@ import type { ComponentResolver, ComponentResolverObject, Options, ResolvedOptio import { join, resolve } from 'node:path' import { slash, toArray } from '@antfu/utils' import { getPackageInfoSync, isPackageExists } from 'local-pkg' +import { escapePath } from 'tinyglobby' import { detectTypeImports } from './type-imports/detect' export const defaultOptions: Omit, 'include' | 'exclude' | 'excludeNames' | 'transformer' | 'globs' | 'globsExclude' | 'directives' | 'types' | 'version'> = { @@ -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 + (join(escapePath(slash(i)), `**/*.${extsGlob}`)) + : prefix + (join(escapePath(slash(i)), `*.${extsGlob}`)) }) if (!resolved.extensions.length) diff --git a/test/search.test.ts b/test/search.test.ts index 32d1ad36..cd2dc1c1 100644 --- a/test/search.test.ts +++ b/test/search.test.ts @@ -18,7 +18,7 @@ describe('search', () => { ctx.setRoot(root) ctx.searchGlob() - expect(ctx.options.globs).toMatchSnapshot() + expect(ctx.options.globs).toMatch('D:/a/unplugin-vue-components/unplugin-vue-components/examples/vite-vue3/src/components/**/*.vue') expect(cleanup(ctx.componentNameMap)).toMatchSnapshot() }) From 384429f911c3bd2809abeafc159325897f9dffe9 Mon Sep 17 00:00:00 2001 From: IlyaL Date: Sun, 13 Jul 2025 23:32:09 +0900 Subject: [PATCH 07/13] test --- test/__snapshots__/search.test.ts.snap | 2 +- test/search.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/__snapshots__/search.test.ts.snap b/test/__snapshots__/search.test.ts.snap index 1f3101ed..2154841d 100644 --- a/test/__snapshots__/search.test.ts.snap +++ b/test/__snapshots__/search.test.ts.snap @@ -124,7 +124,7 @@ exports[`search > should with namespace 1`] = ` exports[`search > should work 1`] = ` [ - "/Users/ilyal/f/unplugin-vue-components/examples/vite-vue3/src/components/**/*.vue", + "D:/a/unplugin-vue-components/unplugin-vue-components/examples/vite-vue3/src/components/**/*.vue", ] `; diff --git a/test/search.test.ts b/test/search.test.ts index cd2dc1c1..32d1ad36 100644 --- a/test/search.test.ts +++ b/test/search.test.ts @@ -18,7 +18,7 @@ describe('search', () => { ctx.setRoot(root) ctx.searchGlob() - expect(ctx.options.globs).toMatch('D:/a/unplugin-vue-components/unplugin-vue-components/examples/vite-vue3/src/components/**/*.vue') + expect(ctx.options.globs).toMatchSnapshot() expect(cleanup(ctx.componentNameMap)).toMatchSnapshot() }) From e428f2cd115b7d9f57d9a6e7fb61b358462e86a2 Mon Sep 17 00:00:00 2001 From: IlyaL Date: Mon, 14 Jul 2025 00:00:27 +0900 Subject: [PATCH 08/13] wip --- src/core/options.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/core/options.ts b/src/core/options.ts index a9839d78..db1b7ca3 100644 --- a/src/core/options.ts +++ b/src/core/options.ts @@ -2,7 +2,6 @@ import type { ComponentResolver, ComponentResolverObject, Options, ResolvedOptio import { join, resolve } from 'node:path' import { slash, toArray } from '@antfu/utils' import { getPackageInfoSync, isPackageExists } from 'local-pkg' -import { escapePath } from 'tinyglobby' import { detectTypeImports } from './type-imports/detect' export const defaultOptions: Omit, 'include' | 'exclude' | 'excludeNames' | 'transformer' | 'globs' | 'globsExclude' | 'directives' | 'types' | 'version'> = { @@ -64,8 +63,8 @@ export function resolveOptions(options: Options, root: string): ResolvedOptions i = i.slice(1) } return resolved.deep - ? prefix + (join(escapePath(slash(i)), `**/*.${extsGlob}`)) - : prefix + (join(escapePath(slash(i)), `*.${extsGlob}`)) + ? prefix + (join(slash(i), slash(`**/*.${extsGlob}`))) + : prefix + (join(slash(i), slash(`*.${extsGlob}`))) }) if (!resolved.extensions.length) From 393cb1e70a34b4320f81ea703437f0061fc487b7 Mon Sep 17 00:00:00 2001 From: IlyaL Date: Mon, 14 Jul 2025 00:14:09 +0900 Subject: [PATCH 09/13] rollback --- src/core/options.ts | 4 ++-- test/__snapshots__/search.test.ts.snap | 6 ------ test/search.test.ts | 1 - 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/core/options.ts b/src/core/options.ts index db1b7ca3..8a39fc6b 100644 --- a/src/core/options.ts +++ b/src/core/options.ts @@ -63,8 +63,8 @@ export function resolveOptions(options: Options, root: string): ResolvedOptions i = i.slice(1) } return resolved.deep - ? prefix + (join(slash(i), slash(`**/*.${extsGlob}`))) - : prefix + (join(slash(i), slash(`*.${extsGlob}`))) + ? prefix + slash(join((i), `**/*.${extsGlob}`)) + : prefix + slash(join((i), `*.${extsGlob}`)) }) if (!resolved.extensions.length) diff --git a/test/__snapshots__/search.test.ts.snap b/test/__snapshots__/search.test.ts.snap index 2154841d..4bb99427 100644 --- a/test/__snapshots__/search.test.ts.snap +++ b/test/__snapshots__/search.test.ts.snap @@ -123,12 +123,6 @@ exports[`search > should with namespace 1`] = ` `; exports[`search > should work 1`] = ` -[ - "D:/a/unplugin-vue-components/unplugin-vue-components/examples/vite-vue3/src/components/**/*.vue", -] -`; - -exports[`search > should work 2`] = ` [ { "as": "Avatar", diff --git a/test/search.test.ts b/test/search.test.ts index 32d1ad36..3277da05 100644 --- a/test/search.test.ts +++ b/test/search.test.ts @@ -18,7 +18,6 @@ describe('search', () => { ctx.setRoot(root) ctx.searchGlob() - expect(ctx.options.globs).toMatchSnapshot() expect(cleanup(ctx.componentNameMap)).toMatchSnapshot() }) From 029273fae25e9086fdaadbb6f89e7cf7cf8bbb21 Mon Sep 17 00:00:00 2001 From: IlyaL Date: Mon, 14 Jul 2025 01:25:54 +0900 Subject: [PATCH 10/13] feat: introduce `escapeSpecialChars` --- src/core/options.ts | 5 +++-- src/core/utils.ts | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/core/options.ts b/src/core/options.ts index 8a39fc6b..811980ef 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, '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((join(i, `**/*.${extsGlob}`))) + : prefix + escapeSpecialChars((join(i, `*.${extsGlob}`))) }) if (!resolved.extensions.length) diff --git a/src/core/utils.ts b/src/core/utils.ts index 53e1c132..08f342db 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, '\\$&') +} From 21e517fd95c6632b6848c492703bb59bdaf3772d Mon Sep 17 00:00:00 2001 From: IlyaL Date: Mon, 14 Jul 2025 01:30:57 +0900 Subject: [PATCH 11/13] fix: add missing `slash` --- src/core/options.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/options.ts b/src/core/options.ts index 811980ef..699062bc 100644 --- a/src/core/options.ts +++ b/src/core/options.ts @@ -64,8 +64,8 @@ export function resolveOptions(options: Options, root: string): ResolvedOptions i = i.slice(1) } return resolved.deep - ? prefix + escapeSpecialChars((join(i, `**/*.${extsGlob}`))) - : prefix + escapeSpecialChars((join(i, `*.${extsGlob}`))) + ? prefix + escapeSpecialChars(slash(join(i, `**/*.${extsGlob}`))) + : prefix + escapeSpecialChars(slash(join(i, `*.${extsGlob}`))) }) if (!resolved.extensions.length) From 126f1b9c35a8ca58c764df268ea92da1c91910c4 Mon Sep 17 00:00:00 2001 From: IlyaL Date: Thu, 17 Jul 2025 04:09:33 +0800 Subject: [PATCH 12/13] fix: correct regex --- src/core/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/utils.ts b/src/core/utils.ts index 08f342db..ab86231d 100644 --- a/src/core/utils.ts +++ b/src/core/utils.ts @@ -243,7 +243,7 @@ export function isExclude(name: string, exclude?: FilterPattern): boolean { return false } -const ESCAPE_PARENTHESES_REGEX = /[(?:)]/g +const ESCAPE_PARENTHESES_REGEX = /[()]/g export function escapeSpecialChars(str: string): string { return str.replace(ESCAPE_PARENTHESES_REGEX, '\\$&') From 3f8f6ec3afb3b2a71d3f6226cf4783122fad273b Mon Sep 17 00:00:00 2001 From: IlyaL Date: Thu, 17 Jul 2025 04:15:13 +0800 Subject: [PATCH 13/13] test: add unit --- test/utils.test.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 = { @@ -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\\(\\)') + }) +})