diff --git a/.gitignore b/.gitignore index bbae2f96..bd89a7d7 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ node_modules .DS_Store dist .idea +.unimport-components.json components.d.ts diff --git a/examples/vite-vue2/vite.config.ts b/examples/vite-vue2/vite.config.ts index 148b1ac9..24e876ee 100644 --- a/examples/vite-vue2/vite.config.ts +++ b/examples/vite-vue2/vite.config.ts @@ -8,6 +8,7 @@ const config: UserConfig = { Components({ transformer: 'vue2', dts: 'src/components.d.ts', + dumpUnimportComponents: true, }), ], build: { diff --git a/examples/vite-vue3/vite.config.ts b/examples/vite-vue3/vite.config.ts index 926eae87..478ae759 100644 --- a/examples/vite-vue3/vite.config.ts +++ b/examples/vite-vue3/vite.config.ts @@ -37,6 +37,7 @@ const config: UserConfig = { componentPrefix: 'i', }), ], + dumpUnimportComponents: true, }), ], build: { diff --git a/src/core/context.ts b/src/core/context.ts index 317d82ba..e2f26138 100644 --- a/src/core/context.ts +++ b/src/core/context.ts @@ -6,7 +6,7 @@ import process from 'node:process' import { slash, throttle, toArray } from '@antfu/utils' import Debug from 'debug' import { DIRECTIVE_IMPORT_PREFIX } from './constants' -import { writeDeclaration } from './declaration' +import { writeComponentsJson, writeDeclaration } from './declaration' import { searchComponents } from './fs/glob' import { resolveOptions } from './options' import transformer from './transformer' @@ -34,6 +34,7 @@ export class Context { root = process.cwd() sourcemap: string | boolean = true alias: Record = {} + dumpUnimportComponentsPath: string | undefined constructor( private rawOptions: Options, @@ -41,6 +42,16 @@ export class Context { this.options = resolveOptions(rawOptions, this.root) this.sourcemap = rawOptions.sourcemap ?? true this.generateDeclaration = throttle(500, this._generateDeclaration.bind(this), { noLeading: false }) + + if (this.options.dumpUnimportComponents) { + const dumpUnimportComponents = this.options.dumpUnimportComponents === true + ? './.unimport-components.json' + : this.options.dumpUnimportComponents ?? false + + this.dumpUnimportComponentsPath = dumpUnimportComponents + this.generateComponentsJson = throttle(500, this._generateComponentsJson.bind(this), { noLeading: false }) + } + this.setTransformer(this.options.transformer) } @@ -288,7 +299,7 @@ export class Context { if (!this.options.dts) return - debug.declaration('generating') + debug.declaration('generating dts') return writeDeclaration(this, this.options.dts, removeUnused) } @@ -296,6 +307,18 @@ export class Context { this._generateDeclaration(removeUnused) } + _generateComponentsJson(removeUnused = !this._server) { + if (!Object.keys(this._componentNameMap).length) + return + + debug.components('generating components.json') + return writeComponentsJson(this, removeUnused) + } + + generateComponentsJson(removeUnused = !this._server): void { + this._generateComponentsJson(removeUnused) + } + get componentNameMap() { return this._componentNameMap } diff --git a/src/core/declaration.ts b/src/core/declaration.ts index 99a5945e..0d5c0d03 100644 --- a/src/core/declaration.ts +++ b/src/core/declaration.ts @@ -156,3 +156,22 @@ export async function writeDeclaration(ctx: Context, filepath: string, removeUnu if (code !== originalContent) await writeFile(filepath, code) } + +export async function writeComponentsJson(ctx: Context, _removeUnused = false) { + if (!ctx.dumpUnimportComponentsPath) + return + + const components = [ + ...Object.entries({ + ...ctx.componentNameMap, + ...ctx.componentCustomMap, + }).map(([_, { name, as, from }]) => ({ + name: name || 'default', + as, + from, + })), + ...resolveTypeImports(ctx.options.types), + ] + + await writeFile(ctx.dumpUnimportComponentsPath, JSON.stringify(components, null, 2)) +} diff --git a/src/core/unplugin.ts b/src/core/unplugin.ts index e5cb119a..d82663fb 100644 --- a/src/core/unplugin.ts +++ b/src/core/unplugin.ts @@ -48,6 +48,7 @@ export default createUnplugin((options = {}) => { try { const result = await ctx.transform(code, id) ctx.generateDeclaration() + ctx.generateComponentsJson() return result } catch (e) { @@ -69,6 +70,11 @@ export default createUnplugin((options = {}) => { ctx.generateDeclaration() } + if (ctx.options.dumpUnimportComponents && ctx.dumpUnimportComponentsPath) { + if (!existsSync(ctx.dumpUnimportComponentsPath)) + ctx.generateComponentsJson() + } + if (config.build.watch && config.command === 'build') ctx.setupWatcher(chokidar.watch(ctx.options.globs)) }, diff --git a/src/types.ts b/src/types.ts index cb43086f..db7f7012 100644 --- a/src/types.ts +++ b/src/types.ts @@ -205,6 +205,16 @@ export interface Options { * @default true */ sourcemap?: boolean + + /** + * Save unimport components into a JSON file for other tools to consume. + * Provide a filepath to save the JSON file. + * + * When set to `true`, it will save to `./.unimport-components.json` + * + * @default false + */ + dumpUnimportComponents?: boolean | string } export type ResolvedOptions = Omit<