From eed512d5b73bae830d70730e8505f6c8c5127fc6 Mon Sep 17 00:00:00 2001 From: IlyaL Date: Wed, 25 Jun 2025 22:30:51 +0800 Subject: [PATCH 1/3] feat: add components prefix Co-authored-by: winches --- src/core/declaration.ts | 16 +++++++++++++--- src/core/options.ts | 2 ++ src/types.ts | 5 +++++ test/__snapshots__/dts.test.ts.snap | 22 ++++++++++++++++++++++ test/dts.test.ts | 15 +++++++++++++++ 5 files changed, 57 insertions(+), 3 deletions(-) diff --git a/src/core/declaration.ts b/src/core/declaration.ts index 7259be71..13230ef6 100644 --- a/src/core/declaration.ts +++ b/src/core/declaration.ts @@ -38,6 +38,16 @@ export function parseDeclaration(code: string): DeclarationImports | undefined { return imports } +function addComponentPrefix(component: ComponentInfo, prefix?: string) { + if (!component.as || !prefix) + return component + + return { + ...component, + as: `${prefix}${component.as}`, + } +} + /** * Converts `ComponentInfo` to an array * @@ -59,9 +69,9 @@ function stringifyComponentInfo(filepath: string, { from: path, as: name, name: * * `{ name: "typeof import(path)[importName]", ... }` */ -export function stringifyComponentsInfo(filepath: string, components: ComponentInfo[], importPathTransform?: Options['importPathTransform']): Record { +export function stringifyComponentsInfo(filepath: string, components: ComponentInfo[], importPathTransform?: Options['importPathTransform'], prefix?: string): Record { return Object.fromEntries( - components.map(info => stringifyComponentInfo(filepath, info, importPathTransform)) + components.map(info => stringifyComponentInfo(filepath, addComponentPrefix(info, prefix), importPathTransform)) .filter(notNullish), ) } @@ -78,7 +88,7 @@ export function getDeclarationImports(ctx: Context, filepath: string): Declarati ...ctx.componentCustomMap, }), ...resolveTypeImports(ctx.options.types), - ], ctx.options.importPathTransform) + ], ctx.options.importPathTransform, ctx.options.prefix) const directive = stringifyComponentsInfo( filepath, diff --git a/src/core/options.ts b/src/core/options.ts index 58ba6239..0ea67fb0 100644 --- a/src/core/options.ts +++ b/src/core/options.ts @@ -21,6 +21,8 @@ export const defaultOptions: Omit, 'include' | 'exclude' | 'ex importPathTransform: v => v, allowOverrides: false, + + prefix: '', } function normalizeResolvers(resolvers: (ComponentResolver | ComponentResolver[])[]): ComponentResolverObject[] { diff --git a/src/types.ts b/src/types.ts index ac17d1e3..bce2a70c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -114,6 +114,11 @@ export interface Options { */ directoryAsNamespace?: boolean + /** + * Generate components with prefix. + */ + prefix?: string + /** * Collapse same prefixes (camel-sensitive) of folders and components * to prevent duplication inside namespaced component name. diff --git a/test/__snapshots__/dts.test.ts.snap b/test/__snapshots__/dts.test.ts.snap index c1d09dda..ba2e5764 100644 --- a/test/__snapshots__/dts.test.ts.snap +++ b/test/__snapshots__/dts.test.ts.snap @@ -36,6 +36,28 @@ declare module 'vue' { " `; +exports[`dts > generate components with prefix 1`] = ` +"/* eslint-disable */ +// @ts-nocheck +// Generated by unplugin-vue-components +// Read more: https://github.com/vuejs/core/pull/3399 +// biome-ignore lint: disable +export {} + +/* prettier-ignore */ +declare module 'vue' { + export interface GlobalComponents { + CustomPrefixRouterLink: typeof import('vue-router')['RouterLink'] + CustomPrefixRouterView: typeof import('vue-router')['RouterView'] + CustomPrefixTestComp: typeof import('test/component/TestComp')['default'] + } + export interface GlobalDirectives { + vLoading: typeof import('test/directive/Loading')['default'] + } +} +" +`; + exports[`dts > getDeclaration 1`] = ` "/* eslint-disable */ // @ts-nocheck diff --git a/test/dts.test.ts b/test/dts.test.ts index 9ddf0f5d..45d14df2 100644 --- a/test/dts.test.ts +++ b/test/dts.test.ts @@ -189,4 +189,19 @@ declare module 'vue' { const imports = parseDeclaration(code) expect(imports).matchSnapshot() }) + + it.only('generate components with prefix', async () => { + const ctx = new Context({ + resolvers: resolver, + directives: true, + prefix: 'CustomPrefix', + }) + const code = ` +const _component_test_comp = _resolveComponent("test-comp") +const _directive_loading = _resolveDirective("loading")` + await ctx.transform(code, '') + + const declarations = getDeclaration(ctx, 'test.d.ts') + expect(declarations).toMatchSnapshot() + }) }) From 4751b4a720e878e75ad63e509125fe881e06120c Mon Sep 17 00:00:00 2001 From: IlyaL Date: Wed, 25 Jun 2025 22:40:36 +0800 Subject: [PATCH 2/3] fix: remove `.only` --- test/dts.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/dts.test.ts b/test/dts.test.ts index 45d14df2..6d871263 100644 --- a/test/dts.test.ts +++ b/test/dts.test.ts @@ -190,7 +190,7 @@ declare module 'vue' { expect(imports).matchSnapshot() }) - it.only('generate components with prefix', async () => { + it('generate components with prefix', async () => { const ctx = new Context({ resolvers: resolver, directives: true, From 922958755cff4581e4438b8ca44b00d70fb615a3 Mon Sep 17 00:00:00 2001 From: IlyaL Date: Wed, 25 Jun 2025 23:22:48 +0800 Subject: [PATCH 3/3] feat: only add prefix for specific components --- src/core/declaration.ts | 13 ++++++------- test/__snapshots__/dts.test.ts.snap | 20 +++++++++++++++++--- test/dts.test.ts | 3 +++ 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/core/declaration.ts b/src/core/declaration.ts index 13230ef6..cc2e35fa 100644 --- a/src/core/declaration.ts +++ b/src/core/declaration.ts @@ -69,9 +69,9 @@ function stringifyComponentInfo(filepath: string, { from: path, as: name, name: * * `{ name: "typeof import(path)[importName]", ... }` */ -export function stringifyComponentsInfo(filepath: string, components: ComponentInfo[], importPathTransform?: Options['importPathTransform'], prefix?: string): Record { +export function stringifyComponentsInfo(filepath: string, components: ComponentInfo[], importPathTransform?: Options['importPathTransform']): Record { return Object.fromEntries( - components.map(info => stringifyComponentInfo(filepath, addComponentPrefix(info, prefix), importPathTransform)) + components.map(info => stringifyComponentInfo(filepath, info, importPathTransform)) .filter(notNullish), ) } @@ -82,13 +82,12 @@ export interface DeclarationImports { } export function getDeclarationImports(ctx: Context, filepath: string): DeclarationImports | undefined { + const prefixComponentNameMap = Object.values(ctx.componentNameMap).map(info => addComponentPrefix(info, ctx.options.prefix)) const component = stringifyComponentsInfo(filepath, [ - ...Object.values({ - ...ctx.componentNameMap, - ...ctx.componentCustomMap, - }), + ...Object.values(ctx.componentCustomMap), + ...prefixComponentNameMap, ...resolveTypeImports(ctx.options.types), - ], ctx.options.importPathTransform, ctx.options.prefix) + ], ctx.options.importPathTransform) const directive = stringifyComponentsInfo( filepath, diff --git a/test/__snapshots__/dts.test.ts.snap b/test/__snapshots__/dts.test.ts.snap index ba2e5764..1359bad0 100644 --- a/test/__snapshots__/dts.test.ts.snap +++ b/test/__snapshots__/dts.test.ts.snap @@ -47,9 +47,23 @@ export {} /* prettier-ignore */ declare module 'vue' { export interface GlobalComponents { - CustomPrefixRouterLink: typeof import('vue-router')['RouterLink'] - CustomPrefixRouterView: typeof import('vue-router')['RouterView'] - CustomPrefixTestComp: typeof import('test/component/TestComp')['default'] + CustomPrefixAvatar: typeof import('./examples/vite-vue3/src/components/global/avatar.vue')['default'] + CustomPrefixBook: typeof import('./examples/vite-vue3/src/components/book/index.vue')['default'] + CustomPrefixButton: typeof import('./examples/vite-vue3/src/components/ui/button.vue')['default'] + CustomPrefixCheckbox: typeof import('./examples/vite-vue3/src/components/ui/nested/checkbox.vue')['default'] + CustomPrefixCollapseFolderAndComponentFromRoot: typeof import('./examples/vite-vue3/src/components/collapse/collapseFolder/CollapseFolderAndComponentFromRoot.vue')['default'] + CustomPrefixComponentA: typeof import('./examples/vite-vue3/src/components/ComponentA.vue')['default'] + CustomPrefixComponentAsync: typeof import('./examples/vite-vue3/src/components/ComponentAsync.vue')['default'] + CustomPrefixComponentB: typeof import('./examples/vite-vue3/src/components/ComponentB.vue')['default'] + CustomPrefixComponentC: typeof import('./examples/vite-vue3/src/components/component-c.vue')['default'] + CustomPrefixComponentD: typeof import('./examples/vite-vue3/src/components/ComponentD.vue')['default'] + CustomPrefixFolderAndComponentPartially: typeof import('./examples/vite-vue3/src/components/collapse/collapseFolder/FolderAndComponentPartially.vue')['default'] + CustomPrefixKebabCaseCollapseFile: typeof import('./examples/vite-vue3/src/components/kebab-case/kebab-case-collapse/KebabCaseCollapseFile.vue')['default'] + CustomPrefixKebabCaseFile: typeof import('./examples/vite-vue3/src/components/kebab-case/KebabCaseFile.vue')['default'] + CustomPrefixRecursive: typeof import('./examples/vite-vue3/src/components/Recursive.vue')['default'] + RouterLink: typeof import('vue-router')['RouterLink'] + RouterView: typeof import('vue-router')['RouterView'] + TestComp: typeof import('test/component/TestComp')['default'] } export interface GlobalDirectives { vLoading: typeof import('test/directive/Loading')['default'] diff --git a/test/dts.test.ts b/test/dts.test.ts index 6d871263..aa8715d1 100644 --- a/test/dts.test.ts +++ b/test/dts.test.ts @@ -5,6 +5,7 @@ import { describe, expect, it } from 'vitest' import { Context } from '../src/core/context' import { getDeclaration, parseDeclaration } from '../src/core/declaration' +const root = path.resolve(__dirname, '../examples/vite-vue3') const resolver: ComponentResolver[] = [ { type: 'component', @@ -195,7 +196,9 @@ declare module 'vue' { resolvers: resolver, directives: true, prefix: 'CustomPrefix', + dirs: ['src/components'], }) + ctx.setRoot(root) const code = ` const _component_test_comp = _resolveComponent("test-comp") const _directive_loading = _resolveDirective("loading")` pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy