From 478dd5de79c93a57fed118e376684c9a5383a58a Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Thu, 11 Nov 2021 16:53:25 +0800 Subject: [PATCH 01/19] feat: client only component --- packages/nuxt3/src/app/plugins/client-only.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 packages/nuxt3/src/app/plugins/client-only.ts diff --git a/packages/nuxt3/src/app/plugins/client-only.ts b/packages/nuxt3/src/app/plugins/client-only.ts new file mode 100644 index 00000000000..f16cc2c5906 --- /dev/null +++ b/packages/nuxt3/src/app/plugins/client-only.ts @@ -0,0 +1,16 @@ +import { ref, onMounted, defineComponent } from 'vue' +import { defineNuxtPlugin } from '#app' + +export const ClientOnly = defineComponent({ + setup (_, { slots }) { + const show = ref(false) + onMounted(() => { + show.value = true + }) + return () => (show.value && slots.default ? slots.default() : null) + } +}) + +export default defineNuxtPlugin((nuxtApp) => { + nuxtApp.vueApp.component('ClientOnly', ClientOnly) +}) From aac32eaaf240733eea9d8d780362803445c4bbf5 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Thu, 11 Nov 2021 17:11:16 +0800 Subject: [PATCH 02/19] chore: add plugin --- packages/nuxt3/src/core/templates.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/nuxt3/src/core/templates.ts b/packages/nuxt3/src/core/templates.ts index d1d7613b3af..66f3498d28e 100644 --- a/packages/nuxt3/src/core/templates.ts +++ b/packages/nuxt3/src/core/templates.ts @@ -35,8 +35,10 @@ export const clientPluginTemplate = { getContents (ctx: TemplateContext) { const clientPlugins = ctx.app.plugins.filter(p => !p.mode || p.mode !== 'server') return [ + "import clientOnly from '#app/plugins/client-only'", importSources(clientPlugins.map(p => p.src)), 'export default [', + ' clientOnly,', clientPlugins.map(p => importName(p.src)).join(',\n '), ']' ].join('\n') @@ -49,9 +51,11 @@ export const serverPluginTemplate = { const serverPlugins = ctx.app.plugins.filter(p => !p.mode || p.mode !== 'client') return [ "import preload from '#app/plugins/preload.server'", + "import clientOnly from '#app/plugins/client-only'", importSources(serverPlugins.map(p => p.src)), 'export default [', ' preload,', + ' clientOnly,', serverPlugins.map(p => importName(p.src)).join(',\n '), ']' ].join('\n') From a11dc8fb7bdb26982e2b19d7c37d693a55653fc5 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Thu, 11 Nov 2021 18:30:00 +0800 Subject: [PATCH 03/19] chore: update --- .../nuxt3/src/app/{plugins => components}/client-only.ts | 7 ++----- packages/nuxt3/src/core/nuxt.ts | 6 ++++++ packages/nuxt3/src/core/templates.ts | 4 ---- 3 files changed, 8 insertions(+), 9 deletions(-) rename packages/nuxt3/src/app/{plugins => components}/client-only.ts (56%) diff --git a/packages/nuxt3/src/app/plugins/client-only.ts b/packages/nuxt3/src/app/components/client-only.ts similarity index 56% rename from packages/nuxt3/src/app/plugins/client-only.ts rename to packages/nuxt3/src/app/components/client-only.ts index f16cc2c5906..54a89f4568d 100644 --- a/packages/nuxt3/src/app/plugins/client-only.ts +++ b/packages/nuxt3/src/app/components/client-only.ts @@ -1,7 +1,6 @@ import { ref, onMounted, defineComponent } from 'vue' -import { defineNuxtPlugin } from '#app' -export const ClientOnly = defineComponent({ +const ClientOnly = defineComponent({ setup (_, { slots }) { const show = ref(false) onMounted(() => { @@ -11,6 +10,4 @@ export const ClientOnly = defineComponent({ } }) -export default defineNuxtPlugin((nuxtApp) => { - nuxtApp.vueApp.component('ClientOnly', ClientOnly) -}) +export default ClientOnly diff --git a/packages/nuxt3/src/core/nuxt.ts b/packages/nuxt3/src/core/nuxt.ts index 70fcb2d3122..09e6ffb0e80 100644 --- a/packages/nuxt3/src/core/nuxt.ts +++ b/packages/nuxt3/src/core/nuxt.ts @@ -58,6 +58,12 @@ async function initNuxt (nuxt: Nuxt) { filePath: resolve(nuxt.options.appDir, 'components/nuxt-welcome.vue') }) + // Add + addComponent({ + name: 'clientOnly', + filePath: resolve(nuxt.options.appDir, 'components/client-only.ts') + }) + for (const m of modulesToInstall) { await installModule(nuxt, m) } diff --git a/packages/nuxt3/src/core/templates.ts b/packages/nuxt3/src/core/templates.ts index 66f3498d28e..d1d7613b3af 100644 --- a/packages/nuxt3/src/core/templates.ts +++ b/packages/nuxt3/src/core/templates.ts @@ -35,10 +35,8 @@ export const clientPluginTemplate = { getContents (ctx: TemplateContext) { const clientPlugins = ctx.app.plugins.filter(p => !p.mode || p.mode !== 'server') return [ - "import clientOnly from '#app/plugins/client-only'", importSources(clientPlugins.map(p => p.src)), 'export default [', - ' clientOnly,', clientPlugins.map(p => importName(p.src)).join(',\n '), ']' ].join('\n') @@ -51,11 +49,9 @@ export const serverPluginTemplate = { const serverPlugins = ctx.app.plugins.filter(p => !p.mode || p.mode !== 'client') return [ "import preload from '#app/plugins/preload.server'", - "import clientOnly from '#app/plugins/client-only'", importSources(serverPlugins.map(p => p.src)), 'export default [', ' preload,', - ' clientOnly,', serverPlugins.map(p => importName(p.src)).join(',\n '), ']' ].join('\n') From 49f6e76b7dd80f52497eaf447e4014f78cef5967 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Thu, 11 Nov 2021 18:44:55 +0800 Subject: [PATCH 04/19] chore: update --- .../src/app/components/{client-only.ts => client-only.mjs} | 6 +++++- packages/nuxt3/src/core/nuxt.ts | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) rename packages/nuxt3/src/app/components/{client-only.ts => client-only.mjs} (70%) diff --git a/packages/nuxt3/src/app/components/client-only.ts b/packages/nuxt3/src/app/components/client-only.mjs similarity index 70% rename from packages/nuxt3/src/app/components/client-only.ts rename to packages/nuxt3/src/app/components/client-only.mjs index 54a89f4568d..e5f49b217f8 100644 --- a/packages/nuxt3/src/app/components/client-only.ts +++ b/packages/nuxt3/src/app/components/client-only.mjs @@ -6,7 +6,11 @@ const ClientOnly = defineComponent({ onMounted(() => { show.value = true }) - return () => (show.value && slots.default ? slots.default() : null) + return () => ( + show.value + ? slots.default?.() + : slots.fallback?.() + ) } }) diff --git a/packages/nuxt3/src/core/nuxt.ts b/packages/nuxt3/src/core/nuxt.ts index 09e6ffb0e80..9dd5196aec7 100644 --- a/packages/nuxt3/src/core/nuxt.ts +++ b/packages/nuxt3/src/core/nuxt.ts @@ -61,7 +61,7 @@ async function initNuxt (nuxt: Nuxt) { // Add addComponent({ name: 'clientOnly', - filePath: resolve(nuxt.options.appDir, 'components/client-only.ts') + filePath: resolve(nuxt.options.appDir, 'components/client-only') }) for (const m of modulesToInstall) { From 09d91ba84e5d40346e939f2033a8024bc25aca34 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Thu, 11 Nov 2021 19:15:07 +0800 Subject: [PATCH 05/19] chore: update --- packages/nuxt3/src/app/components/client-only.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nuxt3/src/app/components/client-only.mjs b/packages/nuxt3/src/app/components/client-only.mjs index e5f49b217f8..c810e5ba257 100644 --- a/packages/nuxt3/src/app/components/client-only.mjs +++ b/packages/nuxt3/src/app/components/client-only.mjs @@ -9,7 +9,7 @@ const ClientOnly = defineComponent({ return () => ( show.value ? slots.default?.() - : slots.fallback?.() + : (slots.fallback ?? slots.placeholder)?.() ) } }) From 809047632d4d4be13055e10a34cb2270c82efa38 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Sun, 14 Nov 2021 18:04:49 +0800 Subject: [PATCH 06/19] wip: update --- examples/with-components/app.vue | 1 + .../with-components/components/Foo.client.vue | 9 ++++++ .../with-components/components/Foo.server.vue | 5 +++ packages/kit/src/types/components.ts | 6 ++++ packages/nuxt3/src/components/scan.ts | 31 ++++++++++++++++++- 5 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 examples/with-components/components/Foo.client.vue create mode 100644 examples/with-components/components/Foo.server.vue diff --git a/examples/with-components/app.vue b/examples/with-components/app.vue index 96d95aad4a3..80ab5cb3ff2 100644 --- a/examples/with-components/app.vue +++ b/examples/with-components/app.vue @@ -2,5 +2,6 @@
+
diff --git a/examples/with-components/components/Foo.client.vue b/examples/with-components/components/Foo.client.vue new file mode 100644 index 00000000000..7302c2f2a5d --- /dev/null +++ b/examples/with-components/components/Foo.client.vue @@ -0,0 +1,9 @@ + + + diff --git a/examples/with-components/components/Foo.server.vue b/examples/with-components/components/Foo.server.vue new file mode 100644 index 00000000000..cf3e866be67 --- /dev/null +++ b/examples/with-components/components/Foo.server.vue @@ -0,0 +1,5 @@ + diff --git a/packages/kit/src/types/components.ts b/packages/kit/src/types/components.ts index d3584241bd6..51e0e0ac2bb 100644 --- a/packages/kit/src/types/components.ts +++ b/packages/kit/src/types/components.ts @@ -1,3 +1,5 @@ +export type ComponentEnv = 'client' | 'server' + export interface Component { pascalName: string kebabName: string @@ -9,6 +11,10 @@ export interface Component { prefetch: boolean preload: boolean global?: boolean + envPaths?: { + server?: string + client?: string + } /** @deprecated */ import?: string diff --git a/packages/nuxt3/src/components/scan.ts b/packages/nuxt3/src/components/scan.ts index 23988911cec..c6d9fdb3687 100644 --- a/packages/nuxt3/src/components/scan.ts +++ b/packages/nuxt3/src/components/scan.ts @@ -1,7 +1,7 @@ import { basename, extname, join, dirname, relative } from 'pathe' import globby from 'globby' import { pascalCase, splitByCase } from 'scule' -import type { ScanDir, Component } from '@nuxt/kit' +import type { ScanDir, Component, ComponentEnv } from '@nuxt/kit' export function sortDirsByPathLength ({ path: pathA }: ScanDir, { path: pathB }: ScanDir): number { return pathB.split(/[\\/]/).filter(Boolean).length - pathA.split(/[\\/]/).filter(Boolean).length @@ -13,6 +13,21 @@ function hyphenate (str: string):string { return str.replace(/\B([A-Z])/g, '-$1').toLowerCase() } +function resolveEnvComponent (fileName:string) { + const match = fileName.match(/^(.+)\.(client|server)$/) + if (match) { + return { + fileName: match[1], + env: match[2] as ComponentEnv + } + } else { + return { + fileName, + env: undefined + } + } +} + export async function scanComponents (dirs: ScanDir[], srcDir: string): Promise { const components: Component[] = [] const filePaths = new Set() @@ -36,10 +51,14 @@ export async function scanComponents (dirs: ScanDir[], srcDir: string): Promise< dir.prefix ? splitByCase(dir.prefix) : [], (dir.pathPrefix !== false) ? splitByCase(relative(dir.path, dirname(filePath))) : [] ) + let env: ComponentEnv | undefined let fileName = basename(filePath, extname(filePath)) if (fileName.toLowerCase() === 'index') { fileName = dir.pathPrefix === false ? basename(dirname(filePath)) : '' /* inherits from path */ } + // eslint-disable-next-line prefer-const + ({ env, fileName } = resolveEnvComponent(fileName)) + const fileNameParts = splitByCase(fileName) const componentNameParts: string[] = [] @@ -80,6 +99,12 @@ export async function scanComponents (dirs: ScanDir[], srcDir: string): Promise< preload: Boolean(dir.preload) } + if (env) { + component.envPaths = { + [env]: filePath + } + } + if (typeof dir.extendComponent === 'function') { component = (await dir.extendComponent(component)) || component } @@ -88,6 +113,8 @@ export async function scanComponents (dirs: ScanDir[], srcDir: string): Promise< const definedComponent = components.find(c => c.pascalName === component.pascalName) if (definedComponent && component.level < definedComponent.level) { Object.assign(definedComponent, component) + } else if (definedComponent?.envPaths && component.envPaths) { + Object.assign(definedComponent.envPaths, component.envPaths) } else if (!definedComponent) { components.push(component) } @@ -96,5 +123,7 @@ export async function scanComponents (dirs: ScanDir[], srcDir: string): Promise< scannedPaths.push(dir.path) } + console.log({ components }) + return components } From bb94b8868d893bd25cf80f11cc8b2b6c1b4e0dc6 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Sun, 14 Nov 2021 19:27:57 +0800 Subject: [PATCH 07/19] feat: working! --- packages/nuxt3/src/components/module.ts | 11 ++++++++++- packages/nuxt3/src/components/scan.ts | 21 ++++++--------------- packages/nuxt3/src/components/templates.ts | 14 ++++++++++++++ 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/packages/nuxt3/src/components/module.ts b/packages/nuxt3/src/components/module.ts index 67e941c4bc5..65940e073b8 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 } from 'pathe' import { defineNuxtModule, resolveAlias, addVitePlugin, addWebpackPlugin } from '@nuxt/kit' import type { Component, ComponentsDir } from '@nuxt/kit' -import { componentsTemplate, componentsTypeTemplate } from './templates' +import { componentsTemplate, componentsTypeTemplate, getEnvComponentTemplate } from './templates' import { scanComponents } from './scan' import { loaderPlugin } from './loader' @@ -65,6 +65,15 @@ export default defineNuxtModule({ return } + components.filter(i => i.envPaths).forEach((component) => { + component.filePath = `#build/env-components-${component.pascalName}.vue` + app.templates.push({ + filename: `env-components-${component.pascalName}.vue`, + write: true, + getContents: () => getEnvComponentTemplate(component) + }) + }) + app.templates.push({ ...componentsTemplate, options: { components } diff --git a/packages/nuxt3/src/components/scan.ts b/packages/nuxt3/src/components/scan.ts index c6d9fdb3687..b8d638bae1a 100644 --- a/packages/nuxt3/src/components/scan.ts +++ b/packages/nuxt3/src/components/scan.ts @@ -34,8 +34,6 @@ export async function scanComponents (dirs: ScanDir[], srcDir: string): Promise< const scannedPaths: string[] = [] for (const dir of dirs.sort(sortDirsByPathLength)) { - const resolvedNames = new Map() - for (const _file of await globby(dir.pattern!, { cwd: dir.path, ignore: dir.ignore })) { const filePath = join(dir.path, _file) @@ -70,17 +68,6 @@ export async function scanComponents (dirs: ScanDir[], srcDir: string): Promise< } const componentName = pascalCase(componentNameParts) + pascalCase(fileNameParts) - - if (resolvedNames.has(componentName)) { - // eslint-disable-next-line no-console - console.warn(`Two component files resolving to the same name \`${componentName}\`:\n` + - `\n - ${filePath}` + - `\n - ${resolvedNames.get(componentName)}` - ) - continue - } - resolvedNames.set(componentName, filePath) - const pascalName = pascalCase(componentName).replace(/["']/g, '') const kebabName = hyphenate(componentName) const shortPath = relative(srcDir, filePath) @@ -114,16 +101,20 @@ export async function scanComponents (dirs: ScanDir[], srcDir: string): Promise< if (definedComponent && component.level < definedComponent.level) { Object.assign(definedComponent, component) } else if (definedComponent?.envPaths && component.envPaths) { + // merge client and server component path Object.assign(definedComponent.envPaths, component.envPaths) } else if (!definedComponent) { components.push(component) + } else { + console.warn(`Two component files resolving to the same name \`${componentName}\`:\n` + + `\n - ${filePath}` + + `\n - ${definedComponent.filePath}` + ) } } scannedPaths.push(dir.path) } - console.log({ components }) - return components } diff --git a/packages/nuxt3/src/components/templates.ts b/packages/nuxt3/src/components/templates.ts index f19b5356be5..3d72642b593 100644 --- a/packages/nuxt3/src/components/templates.ts +++ b/packages/nuxt3/src/components/templates.ts @@ -22,6 +22,20 @@ const createImportMagicComments = (options: ImportMagicCommentsOptions) => { ].filter(Boolean).join(', ') } +export const getEnvComponentTemplate = (component: Component) => ` + + + +` + export const componentsTemplate = { filename: 'components.mjs', getContents ({ options }: { options: ComponentsTemplateOptions }) { From b5ba76be2f04abbfb830c10f6690c970da5af2b5 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Sun, 14 Nov 2021 19:44:06 +0800 Subject: [PATCH 08/19] feat: passing slots and attrs --- examples/with-components/app.vue | 12 ++++++++++-- examples/with-components/components/Foo.client.vue | 1 + examples/with-components/components/Foo.server.vue | 1 + packages/nuxt3/src/components/templates.ts | 6 +++--- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/examples/with-components/app.vue b/examples/with-components/app.vue index 80ab5cb3ff2..e2988ff96cb 100644 --- a/examples/with-components/app.vue +++ b/examples/with-components/app.vue @@ -1,7 +1,15 @@ + + diff --git a/examples/with-components/components/Foo.client.vue b/examples/with-components/components/Foo.client.vue index 7302c2f2a5d..3e055a3d66b 100644 --- a/examples/with-components/components/Foo.client.vue +++ b/examples/with-components/components/Foo.client.vue @@ -5,5 +5,6 @@ const width = window.innerWidth diff --git a/examples/with-components/components/Foo.server.vue b/examples/with-components/components/Foo.server.vue index cf3e866be67..2e297d465fa 100644 --- a/examples/with-components/components/Foo.server.vue +++ b/examples/with-components/components/Foo.server.vue @@ -1,5 +1,6 @@ diff --git a/packages/nuxt3/src/components/templates.ts b/packages/nuxt3/src/components/templates.ts index 3d72642b593..01d67276967 100644 --- a/packages/nuxt3/src/components/templates.ts +++ b/packages/nuxt3/src/components/templates.ts @@ -30,8 +30,8 @@ ${component.envPaths.server ? `import Server from '${component.envPaths.server}' ` @@ -66,7 +66,7 @@ export const componentsTypeTemplate = { getContents: ({ options }: { options: ComponentsTemplateOptions }) => `// Generated by components discovery declare module 'vue' { export interface GlobalComponents { -${options.components.map(c => ` '${c.pascalName}': typeof import('${relative(options.buildDir, c.filePath)}')['${c.export}']`).join(',\n')} +${options.components.map(c => ` '${c.pascalName}': typeof import('${relative(options.buildDir, c.envPaths?.client || c.filePath)}')['${c.export}']`).join(',\n')} } } export {} From 0155358c8833b7f1b744fdda28968f56f9f37c56 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Sun, 14 Nov 2021 19:54:16 +0800 Subject: [PATCH 09/19] chore: update example --- examples/with-components/app.vue | 15 +++++---------- ...{Foo.client.vue => ClientAndServer.client.vue} | 0 ...{Foo.server.vue => ClientAndServer.server.vue} | 2 +- .../components/JustClient.client.vue | 10 ++++++++++ packages/nuxt3/src/components/module.ts | 4 ++-- 5 files changed, 18 insertions(+), 13 deletions(-) rename examples/with-components/components/{Foo.client.vue => ClientAndServer.client.vue} (100%) rename examples/with-components/components/{Foo.server.vue => ClientAndServer.server.vue} (57%) create mode 100644 examples/with-components/components/JustClient.client.vue diff --git a/examples/with-components/app.vue b/examples/with-components/app.vue index e2988ff96cb..84d7dbd0d7d 100644 --- a/examples/with-components/app.vue +++ b/examples/with-components/app.vue @@ -1,15 +1,10 @@ - - diff --git a/examples/with-components/components/Foo.client.vue b/examples/with-components/components/ClientAndServer.client.vue similarity index 100% rename from examples/with-components/components/Foo.client.vue rename to examples/with-components/components/ClientAndServer.client.vue diff --git a/examples/with-components/components/Foo.server.vue b/examples/with-components/components/ClientAndServer.server.vue similarity index 57% rename from examples/with-components/components/Foo.server.vue rename to examples/with-components/components/ClientAndServer.server.vue index 2e297d465fa..a0f83bfbc72 100644 --- a/examples/with-components/components/Foo.server.vue +++ b/examples/with-components/components/ClientAndServer.server.vue @@ -1,6 +1,6 @@ diff --git a/examples/with-components/components/JustClient.client.vue b/examples/with-components/components/JustClient.client.vue new file mode 100644 index 00000000000..32f0be86ed1 --- /dev/null +++ b/examples/with-components/components/JustClient.client.vue @@ -0,0 +1,10 @@ + + + diff --git a/packages/nuxt3/src/components/module.ts b/packages/nuxt3/src/components/module.ts index 65940e073b8..d54405e0661 100644 --- a/packages/nuxt3/src/components/module.ts +++ b/packages/nuxt3/src/components/module.ts @@ -66,9 +66,9 @@ export default defineNuxtModule({ } components.filter(i => i.envPaths).forEach((component) => { - component.filePath = `#build/env-components-${component.pascalName}.vue` + component.filePath = `#build/c-${component.pascalName}.vue` app.templates.push({ - filename: `env-components-${component.pascalName}.vue`, + filename: `c-${component.pascalName}.vue`, write: true, getContents: () => getEnvComponentTemplate(component) }) From 5463607f292a77ee072036eb53b5e382dbbff42e Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Thu, 18 Nov 2021 21:03:31 +0800 Subject: [PATCH 10/19] docs: add more comments --- packages/kit/src/types/components.ts | 4 ++++ packages/nuxt3/src/components/scan.ts | 15 +++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/kit/src/types/components.ts b/packages/kit/src/types/components.ts index 91d0f438002..c213f9a5f4b 100644 --- a/packages/kit/src/types/components.ts +++ b/packages/kit/src/types/components.ts @@ -11,6 +11,10 @@ export interface Component { prefetch: boolean preload: boolean global?: boolean + + /** + * Environment-specific components (`.client.vue` and `.server.vue`) real paths + */ envPaths?: { server?: string client?: string diff --git a/packages/nuxt3/src/components/scan.ts b/packages/nuxt3/src/components/scan.ts index bdc041f1d7f..2f38b53bdd6 100644 --- a/packages/nuxt3/src/components/scan.ts +++ b/packages/nuxt3/src/components/scan.ts @@ -132,16 +132,19 @@ export async function scanComponents (dirs: ComponentsDir[], srcDir: string): Pr component = (await dir.extendComponent(component)) || component } - // Check if component is already defined, used to overwite if level is inferiour + // Check if component is already defined const definedComponent = components.find(c => c.pascalName === component.pascalName) - if (definedComponent && component.level < definedComponent.level) { + if (!definedComponent) { + // Not defined, add component + components.push(component) + } else if (component.level < definedComponent.level) { + // Overwite if level is inferiour Object.assign(definedComponent, component) - } else if (definedComponent?.envPaths && component.envPaths) { - // merge client and server component path + } else if (definedComponent.envPaths && component.envPaths) { + // Merge client and server component path Object.assign(definedComponent.envPaths, component.envPaths) - } else if (!definedComponent) { - components.push(component) } else { + // Naming conflict warning, ignore the later one console.warn(`Two component files resolving to the same name \`${componentName}\`:\n` + `\n - ${filePath}` + `\n - ${definedComponent.filePath}` From 413de897f60a27d68827e13c28c8ff11f2d36e54 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Thu, 18 Nov 2021 21:20:17 +0800 Subject: [PATCH 11/19] chore: update --- packages/nuxt3/src/components/module.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/nuxt3/src/components/module.ts b/packages/nuxt3/src/components/module.ts index d54405e0661..5be1b25f078 100644 --- a/packages/nuxt3/src/components/module.ts +++ b/packages/nuxt3/src/components/module.ts @@ -65,10 +65,13 @@ export default defineNuxtModule({ return } + // Handle environment-specify components components.filter(i => i.envPaths).forEach((component) => { - component.filePath = `#build/c-${component.pascalName}.vue` + const filename = `env-${component.pascalName}.vue` + component.filePath = `#build/${filename}` + component.shortPath = `#build/${filename}` app.templates.push({ - filename: `c-${component.pascalName}.vue`, + filename, write: true, getContents: () => getEnvComponentTemplate(component) }) From adfaef85338e6a1179ae3976a98cf390314e054c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Chopin?= Date: Mon, 22 Nov 2021 12:49:19 +0100 Subject: [PATCH 12/19] chore: typo --- examples/with-components/components/JustClient.client.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/with-components/components/JustClient.client.vue b/examples/with-components/components/JustClient.client.vue index 32f0be86ed1..68549aef07a 100644 --- a/examples/with-components/components/JustClient.client.vue +++ b/examples/with-components/components/JustClient.client.vue @@ -5,6 +5,6 @@ const height = window.innerHeight From aebd09d68e7eee93d619b5519f7e4dab1099bb50 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Thu, 2 Dec 2021 13:15:01 +0800 Subject: [PATCH 13/19] wip: use redirecting for client/server --- examples/with-components/nuxt.config.ts | 2 +- packages/nuxt3/src/components/loader.ts | 8 +-- packages/nuxt3/src/components/module.ts | 62 ++++++++++++++++------ packages/nuxt3/src/components/templates.ts | 58 +++++++++++++------- 4 files changed, 90 insertions(+), 40 deletions(-) diff --git a/examples/with-components/nuxt.config.ts b/examples/with-components/nuxt.config.ts index 01b4f600a9c..5ce38fd3dc7 100644 --- a/examples/with-components/nuxt.config.ts +++ b/examples/with-components/nuxt.config.ts @@ -1,7 +1,7 @@ import { defineNuxtConfig } from 'nuxt3' export default defineNuxtConfig({ - vite: false, + vite: true, components: { dirs: [ '~/components', diff --git a/packages/nuxt3/src/components/loader.ts b/packages/nuxt3/src/components/loader.ts index 7477d178552..f9cb4c7c00d 100644 --- a/packages/nuxt3/src/components/loader.ts +++ b/packages/nuxt3/src/components/loader.ts @@ -1,9 +1,11 @@ import { createUnplugin } from 'unplugin' import { parseQuery, parseURL } from 'ufo' import { Component } from '@nuxt/schema' +import { getComponentPath } from './templates' interface LoaderOptions { getComponents(): Component[] + mode: 'server' | 'client' } export const loaderPlugin = createUnplugin((options: LoaderOptions) => ({ @@ -17,7 +19,7 @@ export const loaderPlugin = createUnplugin((options: LoaderOptions) => ({ return pathname.endsWith('.vue') && (query.type === 'template' || !search) }, transform (code) { - return transform(code, options.getComponents()) + return transform(code, options.getComponents(), options.mode) } })) @@ -25,7 +27,7 @@ function findComponent (components: Component[], name:string) { return components.find(({ pascalName, kebabName }) => [pascalName, kebabName].includes(name)) } -function transform (content: string, components: Component[]) { +function transform (content: string, components: Component[], mode: 'server' | 'client') { let num = 0 let imports = '' const map = new Map() @@ -36,7 +38,7 @@ function transform (content: string, components: Component[]) { if (component) { const identifier = map.get(component) || `__nuxt_component_${num++}` map.set(component, identifier) - imports += `import ${identifier} from "${component.filePath}";` + imports += `import ${identifier} from "${getComponentPath(component, mode)}";` return ` ${identifier}` } // no matched diff --git a/packages/nuxt3/src/components/module.ts b/packages/nuxt3/src/components/module.ts index f41940defeb..70a064fb254 100644 --- a/packages/nuxt3/src/components/module.ts +++ b/packages/nuxt3/src/components/module.ts @@ -1,8 +1,8 @@ import { statSync } from 'fs' import { resolve } from 'pathe' -import { defineNuxtModule, resolveAlias, addVitePlugin, addWebpackPlugin } from '@nuxt/kit' +import { defineNuxtModule, resolveAlias } from '@nuxt/kit' import type { Component, ComponentsDir, ComponentsOptions } from '@nuxt/schema' -import { componentsTemplate, getEnvComponentTemplate, componentsTypeTemplate } from './templates' +import { componentsTypeTemplate, componentsClientTemplate, componentsServerTemplate } from './templates' import { scanComponents } from './scan' import { loaderPlugin } from './loader' @@ -66,20 +66,31 @@ export default defineNuxtModule({ return } - // Handle environment-specify components - components.filter(i => i.envPaths).forEach((component) => { - const filename = `env-${component.pascalName}.vue` - component.filePath = `#build/${filename}` - component.shortPath = `#build/${filename}` - app.templates.push({ - filename, - write: true, - getContents: () => getEnvComponentTemplate(component) - }) + // // Handle environment-specify components + // components.filter(i => i.envPaths).forEach((component) => { + // const filename = `env-${component.pascalName}.vue` + // component.filePath = `#build/${filename}` + // component.shortPath = `#build/${filename}` + // app.templates.push({ + // filename, + // write: true, + // getContents: () => getEnvComponentTemplate(component) + // }) + // }) + + app.templates.push({ + filename: 'Empty.vue', + write: true, + getContents: () => '' + }) + + app.templates.push({ + ...componentsClientTemplate, + options: { components } }) app.templates.push({ - ...componentsTemplate, + ...componentsServerTemplate, options: { components } }) @@ -88,7 +99,8 @@ export default defineNuxtModule({ options: { components, buildDir: nuxt.options.buildDir } }) - app.plugins.push({ src: '#build/components' }) + app.plugins.push({ src: '#build/components-client', mode: 'client' }) + app.plugins.push({ src: '#build/components-server', mode: 'server' }) }) nuxt.hook('prepare:types', ({ references }) => { @@ -108,8 +120,24 @@ export default defineNuxtModule({ } }) - const loaderOptions = { getComponents: () => components } - addWebpackPlugin(loaderPlugin.webpack(loaderOptions)) - addVitePlugin(loaderPlugin.vite(loaderOptions)) + const getComponents = () => components + + nuxt.hook('vite:extendConfig', (config, { isClient }) => { + config.plugins = config.plugins || [] + config.plugins.push(loaderPlugin.vite({ + getComponents, + mode: isClient ? 'client' : 'server' + })) + }) + + // TODO: webpack + nuxt.hook('webpack:config', (configs) => { + configs.forEach((config) => { + console.log(config) + }) + }) + + // addWebpackPlugin(loaderPlugin.webpack(loaderOptions)) + // addVitePlugin(loaderPlugin.vite(loaderOptions)) } }) diff --git a/packages/nuxt3/src/components/templates.ts b/packages/nuxt3/src/components/templates.ts index fe4b77cfad5..a6e859ac17e 100644 --- a/packages/nuxt3/src/components/templates.ts +++ b/packages/nuxt3/src/components/templates.ts @@ -1,6 +1,6 @@ import { relative } from 'pathe' -import type { Component } from '@nuxt/schema' +import type { Component, NuxtPlugin } from '@nuxt/schema' export type ComponentsTemplateOptions = { buildDir?: string @@ -22,31 +22,38 @@ const createImportMagicComments = (options: ImportMagicCommentsOptions) => { ].filter(Boolean).join(', ') } -export const getEnvComponentTemplate = (component: Component) => ` - +// export const getEnvComponentTemplate = (component: Component) => ` +// - -` +// +// ` -export const componentsTemplate = { - filename: 'components.mjs', - getContents ({ options }: { options: ComponentsTemplateOptions }) { - return `import { defineAsyncComponent } from 'vue' +export function getComponentPath (component: Component, mode?: NuxtPlugin['mode']) { + if (!component.envPaths || !mode || mode === 'all') { + return component.filePath + } + return mode === 'client' + ? component.envPaths.client + : component.envPaths.server || '#build/Empty.vue' +} + +function getComponentTemplate (components: Component[], mode: NuxtPlugin['mode']) { + return `import { defineAsyncComponent } from 'vue' const components = { -${options.components.filter(c => c.global !== false).map((c) => { +${components.filter(c => c.global !== false).map((c) => { const exp = c.export === 'default' ? 'c.default || c' : `c['${c.export}']` const magicComments = createImportMagicComments(c) - return ` '${c.pascalName}': defineAsyncComponent(() => import('${c.filePath}' /* ${magicComments} */).then(c => ${exp}))` + return ` '${c.pascalName}': defineAsyncComponent(() => import('${getComponentPath(c, mode)}' /* ${magicComments} */).then(c => ${exp}))` }).join(',\n')} } @@ -57,6 +64,19 @@ export default function (nuxtApp) { } } ` +} + +export const componentsClientTemplate = { + filename: 'components-client.mjs', + getContents ({ options }: { options: ComponentsTemplateOptions }) { + return getComponentTemplate(options.components, 'client') + } +} + +export const componentsServerTemplate = { + filename: 'components-server.mjs', + getContents ({ options }: { options: ComponentsTemplateOptions }) { + return getComponentTemplate(options.components, 'server') } } From d093fa92b917beecc0a5df1121bd2f5db27bc01b Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Thu, 2 Dec 2021 14:21:25 +0800 Subject: [PATCH 14/19] feat: it works! --- .../components/ClientAndServer.server.vue | 4 +++ examples/with-components/nuxt.config.ts | 2 +- .../nuxt3/src/app/components/client-only.mjs | 22 ++++++++++++-- .../nuxt3/src/app/components/nuxt-empty.mjs | 8 +++++ packages/nuxt3/src/components/loader.ts | 9 ++++++ packages/nuxt3/src/components/module.ts | 28 ++++------------- packages/nuxt3/src/components/templates.ts | 30 +++++++------------ 7 files changed, 58 insertions(+), 45 deletions(-) create mode 100644 packages/nuxt3/src/app/components/nuxt-empty.mjs diff --git a/examples/with-components/components/ClientAndServer.server.vue b/examples/with-components/components/ClientAndServer.server.vue index a0f83bfbc72..8e1af3bf585 100644 --- a/examples/with-components/components/ClientAndServer.server.vue +++ b/examples/with-components/components/ClientAndServer.server.vue @@ -4,3 +4,7 @@ + + diff --git a/examples/with-components/nuxt.config.ts b/examples/with-components/nuxt.config.ts index 5ce38fd3dc7..01b4f600a9c 100644 --- a/examples/with-components/nuxt.config.ts +++ b/examples/with-components/nuxt.config.ts @@ -1,7 +1,7 @@ import { defineNuxtConfig } from 'nuxt3' export default defineNuxtConfig({ - vite: true, + vite: false, components: { dirs: [ '~/components', diff --git a/packages/nuxt3/src/app/components/client-only.mjs b/packages/nuxt3/src/app/components/client-only.mjs index 45054c56b30..deb200c63b9 100644 --- a/packages/nuxt3/src/app/components/client-only.mjs +++ b/packages/nuxt3/src/app/components/client-only.mjs @@ -1,6 +1,6 @@ -import { ref, onMounted, defineComponent, createElementBlock } from 'vue' +import { ref, onMounted, defineComponent, createElementBlock, h } from 'vue' -export default defineComponent({ +const ClientOnly = defineComponent({ name: 'ClientOnly', // eslint-disable-next-line vue/require-prop-types props: ['fallback', 'placeholder', 'placeholderTag', 'fallbackTag'], @@ -17,3 +17,21 @@ export default defineComponent({ } } }) + +export function wrapClientOnly (component, mode) { + return defineComponent({ + name: 'ClientOnlyWarpper', + setup (props, { attrs, slots }) { + const mounted = ref(false) + onMounted(() => { mounted.value = true }) + return () => { + if (mounted.value === (mode !== 'server')) { + return h(component, { props, attrs }, slots) + } + return h('div') + } + } + }) +} + +export default ClientOnly diff --git a/packages/nuxt3/src/app/components/nuxt-empty.mjs b/packages/nuxt3/src/app/components/nuxt-empty.mjs new file mode 100644 index 00000000000..986666d7a55 --- /dev/null +++ b/packages/nuxt3/src/app/components/nuxt-empty.mjs @@ -0,0 +1,8 @@ +import { defineComponent, createElementBlock } from 'vue' + +export default defineComponent({ + name: 'Empty', + render () { + return createElementBlock('div') + } +}) diff --git a/packages/nuxt3/src/components/loader.ts b/packages/nuxt3/src/components/loader.ts index f9cb4c7c00d..4d59b8b953a 100644 --- a/packages/nuxt3/src/components/loader.ts +++ b/packages/nuxt3/src/components/loader.ts @@ -31,6 +31,7 @@ function transform (content: string, components: Component[], mode: 'server' | ' let num = 0 let imports = '' const map = new Map() + let hasEnvComponents = false // replace `_resolveComponent("...")` to direct import const newContent = content.replace(/ _resolveComponent\("(.*?)"\)/g, (full, name) => { @@ -39,11 +40,19 @@ function transform (content: string, components: Component[], mode: 'server' | ' const identifier = map.get(component) || `__nuxt_component_${num++}` map.set(component, identifier) imports += `import ${identifier} from "${getComponentPath(component, mode)}";` + if (component.envPaths) { + hasEnvComponents = true + return ` wrapClientOnly(${identifier}, '${mode}')` + } return ` ${identifier}` } // no matched return full }) + if (hasEnvComponents) { + imports = 'import { wrapClientOnly } from \'#app/components/client-only\';' + imports + } + return `${imports}\n${newContent}` } diff --git a/packages/nuxt3/src/components/module.ts b/packages/nuxt3/src/components/module.ts index 70a064fb254..f5a3c5b7888 100644 --- a/packages/nuxt3/src/components/module.ts +++ b/packages/nuxt3/src/components/module.ts @@ -66,24 +66,6 @@ export default defineNuxtModule({ return } - // // Handle environment-specify components - // components.filter(i => i.envPaths).forEach((component) => { - // const filename = `env-${component.pascalName}.vue` - // component.filePath = `#build/${filename}` - // component.shortPath = `#build/${filename}` - // app.templates.push({ - // filename, - // write: true, - // getContents: () => getEnvComponentTemplate(component) - // }) - // }) - - app.templates.push({ - filename: 'Empty.vue', - write: true, - getContents: () => '' - }) - app.templates.push({ ...componentsClientTemplate, options: { components } @@ -130,14 +112,14 @@ export default defineNuxtModule({ })) }) - // TODO: webpack nuxt.hook('webpack:config', (configs) => { configs.forEach((config) => { - console.log(config) + config.plugins = config.plugins || [] + config.plugins.push(loaderPlugin.webpack({ + getComponents, + mode: config.name === 'client' ? 'client' : 'server' + })) }) }) - - // addWebpackPlugin(loaderPlugin.webpack(loaderOptions)) - // addVitePlugin(loaderPlugin.vite(loaderOptions)) } }) diff --git a/packages/nuxt3/src/components/templates.ts b/packages/nuxt3/src/components/templates.ts index a6e859ac17e..fb8e1b35a35 100644 --- a/packages/nuxt3/src/components/templates.ts +++ b/packages/nuxt3/src/components/templates.ts @@ -22,38 +22,30 @@ const createImportMagicComments = (options: ImportMagicCommentsOptions) => { ].filter(Boolean).join(', ') } -// export const getEnvComponentTemplate = (component: Component) => ` -// - -// -// ` - export function getComponentPath (component: Component, mode?: NuxtPlugin['mode']) { if (!component.envPaths || !mode || mode === 'all') { return component.filePath } - return mode === 'client' + const envPath = mode === 'client' ? component.envPaths.client - : component.envPaths.server || '#build/Empty.vue' + : component.envPaths.server + return envPath || '#app/components/nuxt-empty' } function getComponentTemplate (components: Component[], mode: NuxtPlugin['mode']) { - return `import { defineAsyncComponent } from 'vue' + return `import { defineAsyncComponent, h as __h } from 'vue' +import { wrapClientOnly } from '#app/components/client-only' const components = { ${components.filter(c => c.global !== false).map((c) => { - const exp = c.export === 'default' ? 'c.default || c' : `c['${c.export}']` + let exp = c.export === 'default' ? 'c.default || c' : `c['${c.export}']` const magicComments = createImportMagicComments(c) + const path = getComponentPath(c, mode) + if (c.envPaths) { + exp = `wrapClientOnly(${exp}, '${mode}')` + } - return ` '${c.pascalName}': defineAsyncComponent(() => import('${getComponentPath(c, mode)}' /* ${magicComments} */).then(c => ${exp}))` + return ` '${c.pascalName}': defineAsyncComponent(() => import('${path}' /* ${magicComments} */).then(c => ${exp}))` }).join(',\n')} } From 1531aa4a7cefc8a7f4c2d16dce32638ccf39ee74 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Thu, 2 Dec 2021 14:26:15 +0800 Subject: [PATCH 15/19] chore: clean up --- packages/nuxt3/src/app/components/client-only.mjs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/nuxt3/src/app/components/client-only.mjs b/packages/nuxt3/src/app/components/client-only.mjs index deb200c63b9..16ef245f3e5 100644 --- a/packages/nuxt3/src/app/components/client-only.mjs +++ b/packages/nuxt3/src/app/components/client-only.mjs @@ -1,6 +1,6 @@ import { ref, onMounted, defineComponent, createElementBlock, h } from 'vue' -const ClientOnly = defineComponent({ +export default defineComponent({ name: 'ClientOnly', // eslint-disable-next-line vue/require-prop-types props: ['fallback', 'placeholder', 'placeholderTag', 'fallbackTag'], @@ -33,5 +33,3 @@ export function wrapClientOnly (component, mode) { } }) } - -export default ClientOnly From 040e8f93dabc171afcd7b50772f9b7bd81546aa3 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Thu, 23 Dec 2021 04:02:59 +0800 Subject: [PATCH 16/19] Update packages/nuxt3/src/app/components/client-only.mjs Co-authored-by: Wolfgang Hobmaier --- packages/nuxt3/src/app/components/client-only.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nuxt3/src/app/components/client-only.mjs b/packages/nuxt3/src/app/components/client-only.mjs index 16ef245f3e5..514146397b8 100644 --- a/packages/nuxt3/src/app/components/client-only.mjs +++ b/packages/nuxt3/src/app/components/client-only.mjs @@ -20,7 +20,7 @@ export default defineComponent({ export function wrapClientOnly (component, mode) { return defineComponent({ - name: 'ClientOnlyWarpper', + name: 'ClientOnlyWrapper', setup (props, { attrs, slots }) { const mounted = ref(false) onMounted(() => { mounted.value = true }) From 6a54ea6821d5b4ec14e993094e084f6f754a4a46 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Mon, 7 Feb 2022 10:38:03 +0800 Subject: [PATCH 17/19] chore: update --- examples/with-components/app.vue | 4 ---- 1 file changed, 4 deletions(-) diff --git a/examples/with-components/app.vue b/examples/with-components/app.vue index f296fe11e23..bf4840cf20a 100644 --- a/examples/with-components/app.vue +++ b/examples/with-components/app.vue @@ -7,10 +7,6 @@ - -
[Slot]
-
-
[Slot]
From 98d48596b6ae9b209782f1d8234aea9184742e47 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Mon, 7 Feb 2022 10:39:42 +0800 Subject: [PATCH 18/19] chore: cleanup --- packages/nuxt3/src/components/module.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/nuxt3/src/components/module.ts b/packages/nuxt3/src/components/module.ts index 6680931052b..29a58f73ca7 100644 --- a/packages/nuxt3/src/components/module.ts +++ b/packages/nuxt3/src/components/module.ts @@ -85,11 +85,6 @@ export default defineNuxtModule({ options: { components } }) - app.templates.push({ - ...componentsTypeTemplate, - options: { components, buildDir: nuxt.options.buildDir } - }) - app.plugins.push({ src: '#build/components-client', mode: 'client' }) app.plugins.push({ src: '#build/components-server', mode: 'server' }) }) From 3e78d4fdf1c65f33645517b2c47502050948332d Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Thu, 10 Mar 2022 17:54:38 +0800 Subject: [PATCH 19/19] chore: update --- examples/with-components/app.vue | 2 +- packages/nuxt3/src/components/loader.ts | 2 +- packages/nuxt3/src/components/module.ts | 5 +---- packages/nuxt3/src/components/templates.ts | 2 ++ 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/examples/with-components/app.vue b/examples/with-components/app.vue index bf4840cf20a..75fe8e0cec3 100644 --- a/examples/with-components/app.vue +++ b/examples/with-components/app.vue @@ -7,7 +7,7 @@ - +
[Slot]
diff --git a/packages/nuxt3/src/components/loader.ts b/packages/nuxt3/src/components/loader.ts index 564f1bd760b..527a1827b8e 100644 --- a/packages/nuxt3/src/components/loader.ts +++ b/packages/nuxt3/src/components/loader.ts @@ -42,7 +42,7 @@ function transform (code: string, id: string, components: Component[], mode: 'se if (component) { const identifier = map.get(component) || `__nuxt_component_${num++}` map.set(component, identifier) - imports += genImport(getComponentPath(component), [{ name: component.export, as: identifier }]) + imports += genImport(getComponentPath(component, mode), [{ name: component.export, as: identifier }]) if (component.envPaths) { hasEnvComponents = true return ` wrapClientOnly(${identifier}, '${mode}')` diff --git a/packages/nuxt3/src/components/module.ts b/packages/nuxt3/src/components/module.ts index 0f810f4f1c1..e54618e5045 100644 --- a/packages/nuxt3/src/components/module.ts +++ b/packages/nuxt3/src/components/module.ts @@ -1,6 +1,6 @@ import { statSync } from 'fs' import { resolve, basename } from 'pathe' -import { defineNuxtModule, resolveAlias, addTemplate, addPluginTemplate, addPlugin } from '@nuxt/kit' +import { defineNuxtModule, resolveAlias, addTemplate, addPluginTemplate } from '@nuxt/kit' import type { Component, ComponentsDir, ComponentsOptions } from '@nuxt/schema' import { componentsTypeTemplate, componentsClientTemplate, componentsServerTemplate } from './templates' import { scanComponents } from './scan' @@ -111,9 +111,6 @@ export default defineNuxtModule({ options }) - addPlugin({ src: '#build/components-client', mode: 'client' }) - addPlugin({ src: '#build/components-server', mode: 'server' }) - // Scan components and add to plugin nuxt.hook('app:templates', async () => { options.components = await scanComponents(componentDirs, nuxt.options.srcDir!) diff --git a/packages/nuxt3/src/components/templates.ts b/packages/nuxt3/src/components/templates.ts index 8a6046776bd..dd9d0c24ffd 100644 --- a/packages/nuxt3/src/components/templates.ts +++ b/packages/nuxt3/src/components/templates.ts @@ -59,6 +59,7 @@ export default function (nuxtApp) { export const componentsClientTemplate = { filename: 'components-client.mjs', + mode: 'client' as const, getContents ({ options }: { options: ComponentsTemplateOptions }) { return getComponentTemplate(options.components, 'client') } @@ -66,6 +67,7 @@ export const componentsClientTemplate = { export const componentsServerTemplate = { filename: 'components-server.mjs', + mode: 'server' as const, getContents ({ options }: { options: ComponentsTemplateOptions }) { return getComponentTemplate(options.components, 'server') }