diff --git a/.gitignore b/.gitignore index bbae2f96..4d637feb 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ node_modules .DS_Store dist .idea +.components-info.json components.d.ts diff --git a/examples/vite-vue2/vite.config.ts b/examples/vite-vue2/vite.config.ts index 148b1ac9..e703731e 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', + dumpComponentsInfo: true, }), ], build: { diff --git a/examples/vite-vue3/vite.config.ts b/examples/vite-vue3/vite.config.ts index 926eae87..feaee8ef 100644 --- a/examples/vite-vue3/vite.config.ts +++ b/examples/vite-vue3/vite.config.ts @@ -37,6 +37,7 @@ const config: UserConfig = { componentPrefix: 'i', }), ], + dumpComponentsInfo: true, }), ], build: { diff --git a/src/core/context.ts b/src/core/context.ts index 317d82ba..499a9a3b 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 = {} + dumpComponentsInfoPath: 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.dumpComponentsInfo) { + const dumpComponentsInfo = this.options.dumpComponentsInfo === true + ? './.components-info.json' + : this.options.dumpComponentsInfo ?? false + + this.dumpComponentsInfoPath = dumpComponentsInfo + this.generateComponentsJson = throttle(500, this._generateComponentsJson.bind(this), { noLeading: false }) + } + this.setTransformer(this.options.transformer) } @@ -169,6 +180,7 @@ export class Context { onUpdate(path: string) { this.generateDeclaration() + this.generateComponentsJson() if (!this._server) return @@ -288,7 +300,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 +308,18 @@ export class Context { this._generateDeclaration(removeUnused) } + _generateComponentsJson(removeUnused = !this._server) { + if (!Object.keys(this._componentNameMap).length) + return + + debug.components('generating components-info') + 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..4fd2c168 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.dumpComponentsInfoPath) + 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.dumpComponentsInfoPath, JSON.stringify(components, null, 2)) +} diff --git a/src/core/unplugin.ts b/src/core/unplugin.ts index e5cb119a..39616787 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.dumpComponentsInfo && ctx.dumpComponentsInfoPath) { + if (!existsSync(ctx.dumpComponentsInfoPath)) + 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..ac17d1e3 100644 --- a/src/types.ts +++ b/src/types.ts @@ -205,6 +205,16 @@ export interface Options { * @default true */ sourcemap?: boolean + + /** + * Save component information 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 `./.components-info.json` + * + * @default false + */ + dumpComponentsInfo?: boolean | string } export type ResolvedOptions = Omit<