diff --git a/examples/vite-vue3/components.d.ts b/examples/vite-vue3/components.d.ts index 11ebbaea..6683211f 100644 --- a/examples/vite-vue3/components.d.ts +++ b/examples/vite-vue3/components.d.ts @@ -9,7 +9,6 @@ declare module 'vue' { export interface GlobalComponents { Avatar: typeof import('./src/components/global/avatar.vue')['default'] Book: typeof import('./src/components/book/index.vue')['default'] - CollapseCollapseFolderAndCollapseFolderAndComponentPrefixes: typeof import('./src/components/collapse/collapseFolderAnd/CollapseFolderAndComponentPrefixes.vue')['default'] CollapseCollapseFolderCollapseFolderAndComponentFromRoot: typeof import('./src/components/collapse/collapseFolder/CollapseFolderAndComponentFromRoot.vue')['default'] CollapseCollapseFolderFolderAndComponentPartially: typeof import('./src/components/collapse/collapseFolder/FolderAndComponentPartially.vue')['default'] ComponentA: typeof import('./src/components/ComponentA.vue')['default'] diff --git a/examples/vite-vue3/unplugin-vue-component-used-path_rename.json b/examples/vite-vue3/unplugin-vue-component-used-path_rename.json new file mode 100644 index 00000000..d26b2534 --- /dev/null +++ b/examples/vite-vue3/unplugin-vue-component-used-path_rename.json @@ -0,0 +1 @@ +{ "ComponentA": ["/src/App.vue", "/src/components/MarkdownB.md"], "ComponentB": ["/src/App.vue", "/src/components/ComponentD.vue"], "ComponentC": ["/src/App.vue"], "ComponentD": ["/src/App.vue"], "Recursive": ["/src/App.vue"], "Book": ["/src/App.vue"], "UiButton": ["/src/App.vue"], "UiNestedCheckbox": ["/src/App.vue"], "Avatar": ["/src/App.vue"], "MarkdownA": ["/src/App.vue"], "MarkdownB": ["/src/App.vue"], "MyCustom": ["/src/App.vue"], "IFaSolidDiceFive": ["/src/App.vue"], "IHeroiconsOutlineMenuAlt2": ["/src/App.vue"], "IRiApps2Line": ["/src/App.vue"], "IMdi:diceD12": ["/src/App.vue"], "IMdiLightAlarm": ["/src/App.vue"] } diff --git a/examples/vite-vue3/vite.config.ts b/examples/vite-vue3/vite.config.ts index 926eae87..7c4f6aae 100644 --- a/examples/vite-vue3/vite.config.ts +++ b/examples/vite-vue3/vite.config.ts @@ -37,6 +37,11 @@ const config: UserConfig = { componentPrefix: 'i', }), ], + genComponentUsedPath: { + enable: true, + exclude: [/Van\w+/], + genFilePath: './unplugin-vue-component-used-path_rename.json', + }, }), ], build: { diff --git a/src/core/declaration.ts b/src/core/declaration.ts index 2c4b633d..a431e893 100644 --- a/src/core/declaration.ts +++ b/src/core/declaration.ts @@ -139,7 +139,7 @@ declare module 'vue' {` return code } -async function writeFile(filePath: string, content: string) { +export async function writeFile(filePath: string, content: string) { await mkdir(dirname(filePath), { recursive: true }) return await writeFile_(filePath, content, 'utf-8') } diff --git a/src/core/options.ts b/src/core/options.ts index ec2b5fb0..a568a360 100644 --- a/src/core/options.ts +++ b/src/core/options.ts @@ -2,6 +2,8 @@ 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 { writeFile } from './declaration' +import { componentUsedMap } from './transforms/component' import { detectTypeImports } from './type-imports/detect' export const defaultOptions: Omit, 'include' | 'exclude' | 'excludeNames' | 'transformer' | 'globs' | 'directives' | 'types' | 'version'> = { @@ -17,7 +19,11 @@ export const defaultOptions: Omit, 'include' | 'exclude' | 'ex resolvers: [], importPathTransform: v => v, - + genComponentUsedPath: { + enable: false, + genFilePath: './unplugin-vue-component-used-path.json', + exclude: [], + }, allowOverrides: false, } @@ -93,3 +99,8 @@ function getVueVersion(root: string): 2 | 2.7 | 3 { return 2 return 3 } + +export function genComponentUsedPath(options: Options) { + Object.keys(componentUsedMap).map(key => componentUsedMap[key] = [...componentUsedMap[key]]) + writeFile(options.genComponentUsedPath?.genFilePath || './unplugin-vue-component-used-path.json', JSON.stringify(componentUsedMap)) +} diff --git a/src/core/transforms/component.ts b/src/core/transforms/component.ts index 7d2bc2ce..458fce8d 100644 --- a/src/core/transforms/component.ts +++ b/src/core/transforms/component.ts @@ -1,12 +1,27 @@ import type MagicString from 'magic-string' import type { SupportedTransformer } from '../..' +import type { IGenComponentUsedPathOnBuildEndOptions } from '../../types' import type { Context } from '../context' import type { ResolveResult } from '../transformer' +import process from 'node:process' import Debug from 'debug' -import { pascalCase, stringifyComponentImport } from '../utils' +import { isExclude, pascalCase, stringifyComponentImport } from '../utils' const debug = Debug('unplugin-vue-components:transform:component') +export const componentUsedMap: Record = {} +function collectComponentUsedPath(component: any, sfcPath: any, options: IGenComponentUsedPathOnBuildEndOptions) { + if (isExclude(component.as, options.exclude)) + return + + if (!componentUsedMap[component.as]) { + componentUsedMap[component.as] = new Set() + componentUsedMap[component.as].add(sfcPath) + } + else { + componentUsedMap[component.as].add(sfcPath) + } +} function resolveVue2(code: string, s: MagicString) { const results: ResolveResult[] = [] for (const match of code.matchAll(/\b(_c|h)\(\s*['"](.+?)["']([,)])/g)) { @@ -56,6 +71,9 @@ export default async function transformComponent(code: string, transformer: Supp ctx.updateUsageMap(sfcPath, [name]) const component = await ctx.findComponent(name, 'component', [sfcPath]) if (component) { + const genComponentUsedPath = ctx.options.genComponentUsedPath + if (genComponentUsedPath.enable) + collectComponentUsedPath(component, sfcPath.replace(process.cwd(), ''), genComponentUsedPath) const varName = `__unplugin_components_${no}` s.prepend(`${stringifyComponentImport({ ...component, as: varName }, ctx)};\n`) no += 1 diff --git a/src/core/unplugin.ts b/src/core/unplugin.ts index 14c1b9b7..f6aeacad 100644 --- a/src/core/unplugin.ts +++ b/src/core/unplugin.ts @@ -7,6 +7,7 @@ import { createFilter } from '@rollup/pluginutils' import chokidar from 'chokidar' import { createUnplugin } from 'unplugin' import { Context } from './context' +import { genComponentUsedPath } from './options' import { shouldTransform, stringifyComponentImport } from './utils' const PLUGIN_NAME = 'unplugin:webpack' @@ -36,7 +37,10 @@ export default createUnplugin((options = {}) => { transformInclude(id) { return filter(id) }, - + buildEnd() { + if (options.genComponentUsedPath?.enable) + genComponentUsedPath(options) + }, async transform(code, id) { if (!shouldTransform(code)) return null diff --git a/src/types.ts b/src/types.ts index 69dceae1..d00aee1a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -46,6 +46,14 @@ export type Transformer = (code: string, id: string, path: string, query: Record export type SupportedTransformer = 'vue3' | 'vue2' +export interface IGenComponentUsedPathOnBuildEndOptions { + /** default: false */ + enable?: boolean + exclude?: string | RegExp | (string | RegExp)[] | undefined + /** default: './unplugin-vue-component-used-path.json' */ + genFilePath?: string +} + export interface PublicPluginAPI { /** * Resolves a component using the configured resolvers. @@ -181,6 +189,12 @@ export interface Options { * Vue version of project. It will detect automatically if not specified. */ version?: 2 | 2.7 | 3 + /** + * Generate components refrence on buildEnd + * forexample {"ComponentB": ["/src/App.vue", "/src/components/ComponentD.vue" ]} + * @default {enable:false,genFilePath:'./unplugin-vue-component-used-path.json'} + */ + genComponentUsedPath?: IGenComponentUsedPathOnBuildEndOptions } export type ResolvedOptions = Omit<