diff --git a/src/core/context.ts b/src/core/context.ts index 7bdadd91..79a25767 100644 --- a/src/core/context.ts +++ b/src/core/context.ts @@ -8,6 +8,7 @@ import { pascalCase, getNameFromFilePath, resolveAlias, matchGlobs, parseId } fr import { resolveOptions } from './options' import { searchComponents } from './fs/glob' import { generateDeclaration } from './declaration' +import { generateIdeHelper } from './ideHelper' import transformer from './transformer' const debug = { @@ -15,6 +16,7 @@ const debug = { search: Debug('unplugin-vue-components:context:search'), hmr: Debug('unplugin-vue-components:context:hmr'), decleration: Debug('unplugin-vue-components:decleration'), + ideHelper: Debug('unplugin-vue-components:ideHelper'), env: Debug('unplugin-vue-components:env'), } @@ -37,6 +39,7 @@ export class Context { ) { this.options = resolveOptions(rawOptions, this.root) this.generateDeclaration = throttle(500, false, this.generateDeclaration.bind(this)) + this.generateIdeHelper = throttle(500, false, this.generateIdeHelper.bind(this)) this.setTransformer(this.options.transformer) } @@ -130,6 +133,7 @@ export class Context { onUpdate(path: string) { this.generateDeclaration() + this.generateIdeHelper() if (!this._server) return @@ -251,6 +255,14 @@ export class Context { generateDeclaration(this, this.options.root, this.options.dts) } + generateIdeHelper() { + if (!this.options.generateIdeHelper) + return + + debug.ideHelper('generating') + generateIdeHelper(this, this.options.root, this.options.generateIdeHelper) + } + get componentNameMap() { return this._componentNameMap } diff --git a/src/core/ideHelper.ts b/src/core/ideHelper.ts new file mode 100644 index 00000000..433b6c45 --- /dev/null +++ b/src/core/ideHelper.ts @@ -0,0 +1,74 @@ +import { dirname, relative, isAbsolute } from 'path' +import { promises as fs, existsSync } from 'fs' +import { notNullish, slash } from '@antfu/utils' +import { Context } from './context' +import { getVueVersion } from './options' +import { getTransformedPath } from './utils' + +export async function generateIdeHelper(ctx: Context, root: string, filepath: string) { + const imports: Record = Object.fromEntries( + Object.values({ + ...ctx.componentNameMap, + ...ctx.componentCustomMap, + }) + .map(({ path, name, importName }) => { + if (!name) + return undefined + path = getTransformedPath(path, ctx) + const related = isAbsolute(path) + ? `./${relative(dirname(filepath), path)}` + : path + + let entry = 'import ' + if (importName) + entry += `{ ${importName} as ${name} }` + else + entry += name + entry += ` from '${slash(related)}';` + return [name, entry] + }) + .filter(notNullish), + ) + + if (!Object.keys(imports).length) + return + + const originalContent = existsSync(filepath) ? await fs.readFile(filepath, 'utf-8') : '' + + const lines = Object.entries(imports) + .sort((a, b) => a[0].localeCompare(b[0])) + + let code = `// generated by unplugin-vue-components +// We suggest you to NOT commit this file into source control +// Read more: https://github.com/antfu/unplugin-vue-components/issues/135 +` + + if (getVueVersion() === 'vue3') { + // @see https://youtrack.jetbrains.com/issue/WEB-48239 + code += `import { createApp } from "vue"; + +${lines.map(line => line[1]).join('\n')} + +const app = createApp({}); + +const Vue = app + +${lines.map(line => `Vue.component('${line[0]}', ${line[0]});`).join('\n')} + +app.mount("body"); + +` + } + else { + code += `import Vue from "vue"; + +${lines.map(line => line[1]).join('\n')} + +${lines.map(line => `Vue.component('${line[0]}', ${line[0]});\nVue.component('Lazy${line[0]}', ${line[0]});`).join('\n')} + +` + } + + if (code !== originalContent) + await fs.writeFile(filepath, code, 'utf-8') +} diff --git a/src/core/options.ts b/src/core/options.ts index e1a78faa..9841468d 100644 --- a/src/core/options.ts +++ b/src/core/options.ts @@ -19,6 +19,8 @@ export const defaultOptions: Omit, 'include' | 'exclude' | 'tr importPathTransform: v => v, allowOverrides: false, + + generateIdeHelper: false, } function normalizeResolvers(resolvers: (ComponentResolver | ComponentResolver[])[]): ComponentResolverObject[] { @@ -60,6 +62,16 @@ export function resolveOptions(options: Options, root: string): ResolvedOptions ? options.dts : 'components.d.ts', ) + + resolved.generateIdeHelper = !options.generateIdeHelper + ? false + : resolve( + root, + typeof options.generateIdeHelper === 'string' + ? options.generateIdeHelper + : '.components.gen.js', + ) + resolved.root = root resolved.transformer = options.transformer || getVueVersion() || 'vue3' resolved.directives = (typeof options.directives === 'boolean') @@ -71,7 +83,7 @@ export function resolveOptions(options: Options, root: string): ResolvedOptions return resolved } -function getVueVersion() { +export function getVueVersion() { try { // eslint-disable-next-line @typescript-eslint/no-var-requires const vue = require('vue') diff --git a/src/core/transformer.ts b/src/core/transformer.ts index ee53c58a..bbc87506 100644 --- a/src/core/transformer.ts +++ b/src/core/transformer.ts @@ -15,7 +15,7 @@ export interface ResolveResult { replace: (resolved: string) => void } -export default function tranformer(ctx: Context, transformer: SupportedTransformer): Transformer { +export default function transformer(ctx: Context, transformer: SupportedTransformer): Transformer { return async(code, id, path) => { ctx.searchGlob() diff --git a/src/core/unplugin.ts b/src/core/unplugin.ts index 32365858..8f01fca3 100644 --- a/src/core/unplugin.ts +++ b/src/core/unplugin.ts @@ -26,6 +26,7 @@ export default createUnplugin((options = {}) => { try { const result = await ctx.transform(code, id) ctx.generateDeclaration() + ctx.generateIdeHelper() return result } catch (e) { @@ -44,6 +45,7 @@ export default createUnplugin((options = {}) => { if (options.dts) { ctx.searchGlob() ctx.generateDeclaration() + ctx.generateIdeHelper() } if (config.build.watch && config.command === 'build') diff --git a/src/types.ts b/src/types.ts index 9c9045b1..6250117c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -138,6 +138,15 @@ export interface Options { * @default undefined */ directives?: boolean + + /** + * Generate components.js helper for IntelliJ IDEs. + * + * Accept boolean or a path related to project root. + * + * @default false + */ + generateIdeHelper?: boolean | string } export type ResolvedOptions = Omit< @@ -151,6 +160,7 @@ Required, resolvedDirs: string[] globs: string[] dts: string | false + generateIdeHelper: string | false root: string } 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