diff --git a/playground-vue/vite.config.ts b/playground-vue/vite.config.ts index 78ba9fa0..0f5d8552 100644 --- a/playground-vue/vite.config.ts +++ b/playground-vue/vite.config.ts @@ -1,7 +1,5 @@ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' -import AutoImports from 'unplugin-auto-import/vite' -import VueComponents from 'unplugin-vue-components' import ui from '../src/vite' @@ -20,12 +18,13 @@ export default defineConfig({ primary: 'green', neutral: 'slate' } + }, + autoImport: { + imports: ['vue'] + }, + components: { + dirs: ['../playground/app/components'] } - }), - // these are required as we share the component pages with the Nuxt playground - AutoImports({ imports: ['vue'] }), - VueComponents.vite({ - dirs: ['../playground/app/components'] }) ], optimizeDeps: { diff --git a/src/plugins/auto-import.ts b/src/plugins/auto-import.ts new file mode 100644 index 00000000..fb658aae --- /dev/null +++ b/src/plugins/auto-import.ts @@ -0,0 +1,20 @@ +import { join } from 'pathe' +import type { UnpluginContextMeta, UnpluginOptions } from 'unplugin' +import { defu } from 'defu' + +import { runtimeDir } from '../unplugin' +import type { NuxtUIOptions } from '../unplugin' +import AutoImport from 'unplugin-auto-import' +import type { Options as AutoImportOptions } from 'unplugin-auto-import/types' + +/** + * This plugin adds all the Nuxt UI composables as auto-imports. + */ +export default function AutoImportPlugin(options: NuxtUIOptions, meta: UnpluginContextMeta): UnpluginOptions { + const pluginOptions = defu(options.autoImport, { + dts: options.dts ?? true, + dirs: [join(runtimeDir, 'composables')] + }) + + return AutoImport.raw(pluginOptions, meta) as UnpluginOptions +} diff --git a/src/plugins/components.ts b/src/plugins/components.ts index 43575853..de69e9b3 100644 --- a/src/plugins/components.ts +++ b/src/plugins/components.ts @@ -2,20 +2,35 @@ import { join, normalize } from 'pathe' import type { UnpluginContextMeta, UnpluginOptions } from 'unplugin' import { globSync } from 'tinyglobby' import AutoImportComponents from 'unplugin-vue-components' +import type { Options as ComponentsOptions } from 'unplugin-vue-components/types' import { runtimeDir } from '../unplugin' import type { NuxtUIOptions } from '../unplugin' +import { defu } from 'defu' /** * This plugin adds all the Nuxt UI components as auto-imports. */ -export default function ComponentImportPlugin(framework: UnpluginContextMeta['framework'], options: NuxtUIOptions & { prefix: NonNullable }) { +export default function ComponentImportPlugin(options: NuxtUIOptions & { prefix: NonNullable }, meta: UnpluginContextMeta) { const components = globSync('**/*.vue', { cwd: join(runtimeDir, 'components') }) const componentNames = new Set(components.map(c => `${options.prefix}${c.replace(/\.vue$/, '')}`)) const overrides = globSync('**/*.vue', { cwd: join(runtimeDir, 'vue/components') }) const overrideNames = new Set(overrides.map(c => `${options.prefix}${c.replace(/\.vue$/, '')}`)) + const pluginOptions = defu(options.components, { + dts: options.dts ?? true, + exclude: [/[\\/]node_modules[\\/](?!\.pnpm|@nuxt\/ui)/, /[\\/]\.git[\\/]/, /[\\/]\.nuxt[\\/]/], + resolvers: [ + (componentName) => { + if (overrideNames.has(componentName)) + return { name: 'default', from: join(runtimeDir, 'vue/components', `${componentName.slice(options.prefix.length)}.vue`) } + if (componentNames.has(componentName)) + return { name: 'default', from: join(runtimeDir, 'components', `${componentName.slice(options.prefix.length)}.vue`) } + } + ] + }) + return [ /** * This plugin aims to ensure we override certain components with Vue-compatible versions: @@ -41,18 +56,7 @@ export default function ComponentImportPlugin(framework: UnpluginContextMeta['fr } } }, - AutoImportComponents[framework]({ - dts: options.dts ?? true, - exclude: [/[\\/]node_modules[\\/](?!\.pnpm|@nuxt\/ui)/, /[\\/]\.git[\\/]/, /[\\/]\.nuxt[\\/]/], - resolvers: [ - (componentName) => { - if (overrideNames.has(componentName)) - return { name: 'default', from: join(runtimeDir, 'vue/components', `${componentName.slice(options.prefix.length)}.vue`) } - if (componentNames.has(componentName)) - return { name: 'default', from: join(runtimeDir, 'components', `${componentName.slice(options.prefix.length)}.vue`) } - } - ] - }) + AutoImportComponents.raw(pluginOptions, meta) as UnpluginOptions ] satisfies UnpluginOptions[] } diff --git a/src/unplugin.ts b/src/unplugin.ts index e684df6d..7bb6e2eb 100644 --- a/src/unplugin.ts +++ b/src/unplugin.ts @@ -1,8 +1,10 @@ import { fileURLToPath } from 'node:url' -import { join, normalize } from 'pathe' +import { normalize } from 'pathe' +import type { UnpluginOptions } from 'unplugin' import { createUnplugin } from 'unplugin' -import AutoImport from 'unplugin-auto-import' +import type { Options as AutoImportOptions } from 'unplugin-auto-import/types' +import type { Options as ComponentsOptions } from 'unplugin-vue-components/types' import { defu } from 'defu' import tailwind from '@tailwindcss/vite' import type colors from 'tailwindcss/colors' @@ -20,6 +22,7 @@ import ComponentImportPlugin from './plugins/components' import NuxtEnvironmentPlugin from './plugins/nuxt-environment' import type { DeepPartial } from './runtime/types/utils' +import AutoImportPlugin from './plugins/auto-import' type NeutralColor = 'slate' | 'gray' | 'zinc' | 'neutral' | 'stone' type Color = Exclude | (string & {}) @@ -39,6 +42,14 @@ export interface NuxtUIOptions extends Omit + /** + * Override options for `unplugin-vue-components` + */ + components?: Partial } export const runtimeDir = normalize(fileURLToPath(new URL('./runtime', import.meta.url))) @@ -53,11 +64,26 @@ export const NuxtUIPlugin = createUnplugin((_options return [ NuxtEnvironmentPlugin(), - ...ComponentImportPlugin(meta.framework, options), - AutoImport[meta.framework]({ dts: options.dts ?? true, dirs: [join(runtimeDir, 'composables')] }), + ComponentImportPlugin(options, meta), + AutoImportPlugin(options, meta), tailwind(), PluginsPlugin(options), TemplatePlugin(options, appConfig), - AppConfigPlugin(options, appConfig) - ] + AppConfigPlugin(options, appConfig), + { + name: 'nuxt:ui:plugins-duplication-detection', + vite: { + configResolved(config) { + const plugins = config.plugins || [] + + if (plugins.filter(i => i.name === 'unplugin-auto-import').length > 1) { + throw new Error('[Nuxt UI] Multiple instances of `unplugin-auto-import` detected. Nuxt UI includes `unplugin-auto-import` already, and you can configure it using `autoImport` option in Nuxt UI module options.') + } + if (plugins.filter(i => i.name === 'unplugin-vue-components').length > 1) { + throw new Error('[Nuxt UI] Multiple instances of `unplugin-vue-components` detected. Nuxt UI includes `unplugin-vue-components` already, and you can configure it using `components` option in Nuxt UI module options.') + } + } + } + } + ].flat(1) as UnpluginOptions[] })