From 368610e8a76198a543bdef431e2fd0882def3c0d Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Thu, 7 Apr 2022 12:03:37 +0100 Subject: [PATCH] feat(nuxt3): allow explicitly importing components (#4150) --- .../3.directory-structure/4.components.md | 20 ++++++++++ packages/nuxt3/src/components/module.ts | 10 ++++- packages/nuxt3/src/components/templates.ts | 37 +++++++++++++++---- 3 files changed, 57 insertions(+), 10 deletions(-) diff --git a/docs/content/2.guide/3.directory-structure/4.components.md b/docs/content/2.guide/3.directory-structure/4.components.md index 0cdf463ab9..5eed31732b 100644 --- a/docs/content/2.guide/3.directory-structure/4.components.md +++ b/docs/content/2.guide/3.directory-structure/4.components.md @@ -116,6 +116,26 @@ export default { ``` +## Direct imports + +You can also explicitly import components from `#components` if you want or need to bypass Nuxt's auto-importing functionality. + +```html{}[pages/index.vue] + + + +``` + ## `` Component Nuxt provides the `` component for purposely rendering a component only on client side. To import a component only on the client, register the component in a client-side only plugin. diff --git a/packages/nuxt3/src/components/module.ts b/packages/nuxt3/src/components/module.ts index 33c6ec2e66..611481a6b7 100644 --- a/packages/nuxt3/src/components/module.ts +++ b/packages/nuxt3/src/components/module.ts @@ -2,7 +2,7 @@ import { statSync } from 'fs' import { resolve, basename } from 'pathe' import { defineNuxtModule, resolveAlias, addVitePlugin, addWebpackPlugin, addTemplate, addPluginTemplate } from '@nuxt/kit' import type { Component, ComponentsDir, ComponentsOptions } from '@nuxt/schema' -import { componentsTemplate, componentsTypeTemplate } from './templates' +import { componentsPluginTemplate, componentsTemplate, componentsTypeTemplate } from './templates' import { scanComponents } from './scan' import { loaderPlugin } from './loader' @@ -97,6 +97,12 @@ export default defineNuxtModule({ }) addPluginTemplate({ + ...componentsPluginTemplate, + options + }) + + nuxt.options.alias['#components'] = resolve(nuxt.options.buildDir, componentsTemplate.filename) + addTemplate({ ...componentsTemplate, options }) @@ -108,7 +114,7 @@ export default defineNuxtModule({ }) nuxt.hook('prepare:types', ({ references }) => { - references.push({ path: resolve(nuxt.options.buildDir, 'types/components.d.ts') }) + references.push({ path: resolve(nuxt.options.buildDir, 'components.d.ts') }) }) // Watch for changes diff --git a/packages/nuxt3/src/components/templates.ts b/packages/nuxt3/src/components/templates.ts index c329b74eff..26429e014e 100644 --- a/packages/nuxt3/src/components/templates.ts +++ b/packages/nuxt3/src/components/templates.ts @@ -1,7 +1,7 @@ -import { isAbsolute, join, relative } from 'pathe' +import { isAbsolute, relative } from 'pathe' import type { Component } from '@nuxt/schema' -import { genDynamicImport, genObjectFromRawEntries } from 'knitwork' +import { genDynamicImport, genExport, genObjectFromRawEntries } from 'knitwork' export type ComponentsTemplateOptions = { buildDir?: string @@ -23,8 +23,8 @@ const createImportMagicComments = (options: ImportMagicCommentsOptions) => { ].filter(Boolean).join(', ') } -export const componentsTemplate = { - filename: 'components.mjs', +export const componentsPluginTemplate = { + filename: 'components.plugin.mjs', getContents ({ options }: { options: ComponentsTemplateOptions }) { return `import { defineAsyncComponent } from 'vue' @@ -45,15 +45,36 @@ export default function (nuxtApp) { } } +export const componentsTemplate = { + filename: 'components.mjs', + getContents ({ options }: { options: ComponentsTemplateOptions }) { + return [ + 'import { defineAsyncComponent } from \'vue\'', + ...options.components.flatMap((c) => { + const exp = c.export === 'default' ? 'c.default || c' : `c['${c.export}']` + const comment = createImportMagicComments(c) + + return [ + genExport(c.filePath, [{ name: c.export, as: c.pascalName }]), + `export const Lazy${c.pascalName} = defineAsyncComponent(${genDynamicImport(c.filePath, { comment })}.then(c => ${exp}))` + ] + }), + `export const componentNames = ${JSON.stringify(options.components.map(c => c.pascalName))}` + ].join('\n') + } +} + export const componentsTypeTemplate = { - filename: 'types/components.d.ts', + filename: 'components.d.ts', getContents: ({ options }: { options: ComponentsTemplateOptions }) => `// Generated by components discovery declare module 'vue' { export interface GlobalComponents { -${options.components.map(c => ` '${c.pascalName}': typeof ${genDynamicImport(isAbsolute(c.filePath) ? relative(join(options.buildDir, 'types'), c.filePath) : c.filePath, { wrapper: false })}['${c.export}']`).join(',\n')} -${options.components.map(c => ` 'Lazy${c.pascalName}': typeof ${genDynamicImport(isAbsolute(c.filePath) ? relative(join(options.buildDir, 'types'), c.filePath) : c.filePath, { wrapper: false })}['${c.export}']`).join(',\n')} +${options.components.map(c => ` '${c.pascalName}': typeof ${genDynamicImport(isAbsolute(c.filePath) ? relative(options.buildDir, c.filePath) : c.filePath, { wrapper: false })}['${c.export}']`).join(',\n')} +${options.components.map(c => ` 'Lazy${c.pascalName}': typeof ${genDynamicImport(isAbsolute(c.filePath) ? relative(options.buildDir, c.filePath) : c.filePath, { wrapper: false })}['${c.export}']`).join(',\n')} } } -export {} +${options.components.map(c => `export const ${c.pascalName}: typeof ${genDynamicImport(isAbsolute(c.filePath) ? relative(options.buildDir, c.filePath) : c.filePath, { wrapper: false })}['${c.export}']`).join('\n')} +${options.components.map(c => `export const Lazy${c.pascalName}: typeof ${genDynamicImport(isAbsolute(c.filePath) ? relative(options.buildDir, c.filePath) : c.filePath, { wrapper: false })}['${c.export}']`).join('\n')} +export const componentNames: string[] ` }