diff --git a/examples/nuxt/nuxt.config.js b/examples/nuxt/nuxt.config.js index f2d15d39..69aaf9f4 100644 --- a/examples/nuxt/nuxt.config.js +++ b/examples/nuxt/nuxt.config.js @@ -2,7 +2,7 @@ export default { buildModules: [ ['@nuxt/typescript-build', { typeCheck: false }], '@nuxtjs/composition-api/module', - 'unplugin-vue2-script-setup/nuxt', 'unplugin-icons/nuxt', ], + components: true, } diff --git a/examples/nuxt/package.json b/examples/nuxt/package.json index 531fe7e4..5e537a94 100644 --- a/examples/nuxt/package.json +++ b/examples/nuxt/package.json @@ -17,7 +17,6 @@ "@nuxt/typescript-build": "^2.1.0", "@nuxtjs/composition-api": "^0.29.0", "css-loader": "^5.2.0", - "unplugin-icons": "workspace:*", - "unplugin-vue2-script-setup": "^0.6.1" + "unplugin-icons": "workspace:*" } } diff --git a/examples/nuxt/pages/index.vue b/examples/nuxt/pages/index.vue index bfd082db..35e263cf 100644 --- a/examples/nuxt/pages/index.vue +++ b/examples/nuxt/pages/index.vue @@ -1,10 +1,10 @@ diff --git a/package.json b/package.json index 7fd4f7a3..389342b6 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "dependencies": { "@antfu/utils": "^0.3.0", "@iconify/json-tools": "^1.0.10", + "fast-glob": "^3.2.7", "has-pkg": "^0.0.1", "unplugin": "^0.2.9" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c0850e82..591b6c41 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,7 @@ importers: bumpp: ^7.1.1 eslint: ^7.32.0 esno: ^0.9.1 + fast-glob: ^3.2.7 has-pkg: ^0.0.1 rollup: ^2.56.3 tsup: ^4.14.0 @@ -23,6 +24,7 @@ importers: dependencies: '@antfu/utils': 0.3.0 '@iconify/json-tools': 1.0.10 + fast-glob: 3.2.7 has-pkg: 0.0.1 unplugin: 0.2.9_rollup@2.56.3+vite@2.5.7 devDependencies: @@ -49,7 +51,6 @@ importers: css-loader: ^5.2.0 nuxt: ^2.15.8 unplugin-icons: workspace:* - unplugin-vue2-script-setup: ^0.6.1 dependencies: core-js: 3.17.3 nuxt: 2.15.8_typescript@4.4.3 @@ -60,7 +61,6 @@ importers: '@nuxtjs/composition-api': 0.29.0_e5f33a788fb9e70523a947af9d168256 css-loader: 5.2.7 unplugin-icons: link:../.. - unplugin-vue2-script-setup: 0.6.1_rollup@2.56.3+vite@2.5.7 examples/sveltekit: specifiers: @@ -74,7 +74,7 @@ importers: unplugin-icons: workspace:* devDependencies: '@iconify/json': 1.1.401 - '@sveltejs/kit': 1.0.0-next.166_svelte@3.42.5 + '@sveltejs/kit': 1.0.0-next.168_svelte@3.42.5 svelte: 3.42.5 svelte-check: 2.2.6_svelte@3.42.5 svelte-preprocess: 4.9.4_svelte@3.42.5+typescript@4.4.3 @@ -2144,8 +2144,8 @@ packages: resolution: {integrity: sha512-T7VNNlYVM1SgQ+VsMYhnDkcGmWhQdL0bDyGm5TlQ3GBXnJscEClUUOKduWTmm2zCnvNLC1hc3JpuXjs/nFOc5w==} dev: true - /@sveltejs/kit/1.0.0-next.166_svelte@3.42.5: - resolution: {integrity: sha512-TQWtWbgyc5+eJY1/RZvCc4gHLssnW+PfKh0dGxbysR0r1vahBgLDUELNlc1dktg/jUJm4eEFl4cqDqKsvU84tQ==} + /@sveltejs/kit/1.0.0-next.168_svelte@3.42.5: + resolution: {integrity: sha512-BHVqLp3d8BCPD0kuNueoV5YxyfLfgAC9wU6PCbOGIhn1gw4mvpiVbeEn7NKeVkTkl88bu/QtAITLsIx1+4flTA==} engines: {node: ^12.20 || >=14.13} hasBin: true peerDependencies: @@ -6899,7 +6899,6 @@ packages: glob-parent: 5.1.2 merge2: 1.4.1 micromatch: 4.0.4 - dev: true /fast-json-stable-stringify/2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} diff --git a/src/nuxt.ts b/src/nuxt.ts index 38ff6a19..e6fc11df 100644 --- a/src/nuxt.ts +++ b/src/nuxt.ts @@ -1,7 +1,37 @@ +import { promises as fs } from 'fs' +import fg from 'fast-glob' import { Options } from './types' +import IconResolver, { ComponentResolverOptions } from './resolver' +import { pascalize } from './core/utils' import unplugin from '.' -export default function(this: any, options: Options) { +export interface Component { + pascalName: string + kebabName: string + import: string + asyncImport: string + export: string + filePath: string + shortPath: string + isAsync?: boolean + chunkName: string + level: number + prefetch: boolean + preload: boolean + + // await for https://github.com/nuxt/components/pull/234 + isAbsolute: boolean +} + +function hyphenate(str: string): string { + return str.replace(/\B([A-Z])/g, '-$1').toLowerCase() +} + +interface NuxtOptions extends Options { + resolver?: ComponentResolverOptions +} + +export default function(this: any, options: NuxtOptions) { // install webpack plugin this.extendBuild((config: any) => { config.plugins = config.plugins || [] @@ -13,4 +43,56 @@ export default function(this: any, options: Options) { vite.config.plugins = vite.config.plugins || [] vite.config.plugins.push(unplugin.vite(options)) }) + + if (this.nuxt.options.components === false) + return + + // Auto import for components + const resolver = IconResolver(options.resolver) + this.nuxt.hook('components:extend', async(components: Component[]) => { + const files = await fg(['**/*.vue'], { + onlyFiles: true, + cwd: this.options.rootDir, + absolute: true, + ignore: ['node_modules', '.git', 'dist', '.nuxt', '.output'], + }) + + const tags = new Set() + const tagRE = /<([a-zA-Z0-9-]+)\s/g + + await Promise.all(files.map(async(file) => { + const content = await fs.readFile(file, 'utf-8') + let m + tagRE.lastIndex = 0 + do { + m = tagRE.exec(content) + if (m && m[1]) + tags.add(pascalize(m[1])) + } while (m) + })) + + Array.from(tags) + .filter(tag => !components.find(i => i.pascalName === tag || i.kebabName === tag)) + .forEach((tag) => { + const result = resolver(tag) + if (result) { + const kebabName = hyphenate(tag) + components.push({ + pascalName: tag, + kebabName, + chunkName: `~icons/${kebabName}`, + isAsync: false, + import: `require('${result}').default`, + filePath: result, + shortPath: result, + asyncImport: `function () { return import('${result}' /* webpackChunkName: "~icons/${kebabName}" */).then(function(m) { return m['default'] || m }) }`, + export: 'default', + level: 0, + prefetch: false, + preload: false, + isAbsolute: true, + }) + } + }) + }) } diff --git a/src/resolver.ts b/src/resolver.ts index f0439d2f..02642825 100644 --- a/src/resolver.ts +++ b/src/resolver.ts @@ -4,7 +4,7 @@ import Data from '@iconify/json' import { getBuiltinIcon } from './core/loader' import { camelToKebab } from './core/utils' -export interface ComponentResolverOption { +export interface ComponentResolverOptions { /** * Prefix for resolving components name. * Set '' to disable prefix. @@ -44,7 +44,7 @@ export interface ComponentResolverOption { * * @param options */ -export default function ComponentsResolver(options: ComponentResolverOption = {}) { +export default function ComponentsResolver(options: ComponentResolverOptions = {}) { const { prefix: rawPrefix = options.componentPrefix ?? 'i', enabledCollections = Object.keys(Data.collections()),