From b42c7ec01d827e721c0cd3fc17bcf7afc719982d Mon Sep 17 00:00:00 2001 From: Rihan Arfan Date: Thu, 26 Feb 2026 16:48:01 +0000 Subject: [PATCH 1/4] feat(vite): auto-detect client entry --- src/build/vite/plugin.ts | 37 ++++++++++++++++++++++++++++++++++++- src/build/vite/types.ts | 1 + 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/build/vite/plugin.ts b/src/build/vite/plugin.ts index bb820a298d..230ce6dd66 100644 --- a/src/build/vite/plugin.ts +++ b/src/build/vite/plugin.ts @@ -102,6 +102,7 @@ function nitroEnv(ctx: NitroPluginContext): VitePlugin { rollupOptions: { input: userConfig.environments?.client?.build?.rollupOptions?.input ?? + ctx._clientEntry ?? useNitro(ctx).options.renderer?.template, }, }, @@ -380,7 +381,10 @@ async function setupNitroContext( ctx.nitro!.logger.info(`Using \`${prettyPath(ssrEntry)}\` as vite ssr entry.`); } } else { - let ssrEntry = getEntry(userConfig.environments.ssr.build?.rollupOptions?.input); + let ssrEntry = getEntry( + userConfig.environments.ssr.build?.rolldownOptions?.input || + userConfig.environments.ssr.build?.rollupOptions?.input + ); if (typeof ssrEntry === "string") { ssrEntry = resolveModulePath(ssrEntry, { @@ -403,6 +407,37 @@ async function setupNitroContext( ctx.nitro.options.serverEntry = false; } + // Auto-detect client entry + if (!ctx._clientEntry) { + if (userConfig.environments?.client === undefined) { + const clientEntry = resolveModulePath("./entry-client", { + from: ["app", "src", ""].flatMap((d) => + [ctx.nitro!.options.rootDir, ...ctx.nitro!.options.scanDirs].map((s) => join(s, d) + "/") + ), + extensions: DEFAULT_EXTENSIONS, + try: true, + }); + if (clientEntry) { + ctx._clientEntry = clientEntry; + ctx.nitro!.logger.info(`Using \`${prettyPath(clientEntry)}\` as vite client entry.`); + } + } else { + let clientEntry = getEntry( + userConfig.environments.client.build?.rolldownOptions?.input || + userConfig.environments.client.build?.rollupOptions?.input + ); + if (typeof clientEntry === "string") { + ctx._clientEntry = + resolveModulePath(clientEntry, { + from: [ctx.nitro.options.rootDir, ...ctx.nitro.options.scanDirs], + extensions: DEFAULT_EXTENSIONS, + suffixes: ["", "/index"], + try: true, + }) || clientEntry; + } + } + } + // Determine default Vite dist directory const publicDistDir = (ctx._publicDistDir = userConfig.build?.outDir || resolve(ctx.nitro.options.buildDir, "vite/public")); diff --git a/src/build/vite/types.ts b/src/build/vite/types.ts index d0b26869d3..7b9310324a 100644 --- a/src/build/vite/types.ts +++ b/src/build/vite/types.ts @@ -59,6 +59,7 @@ export interface NitroPluginContext { _isRolldown?: boolean; _initialized?: boolean; _envRunner?: EnvRunner; + _clientEntry?: string; _publicDistDir?: string; _entryPoints: Record; } From 162301252d96219039137a30d54a72348563c4ae Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Fri, 27 Feb 2026 18:59:57 +0100 Subject: [PATCH 2/4] update --- examples/vite-ssr-preact/vite.config.mjs | 9 --- examples/vite-ssr-react/vite.config.mjs | 5 -- examples/vite-ssr-solid/vite.config.mjs | 8 -- examples/vite-ssr-vue-router/vite.config.mjs | 4 - src/build/vite/plugin.ts | 78 +++++++++----------- src/build/vite/types.ts | 1 - 6 files changed, 36 insertions(+), 69 deletions(-) diff --git a/examples/vite-ssr-preact/vite.config.mjs b/examples/vite-ssr-preact/vite.config.mjs index 3a99a9944d..51e1c2b0c8 100644 --- a/examples/vite-ssr-preact/vite.config.mjs +++ b/examples/vite-ssr-preact/vite.config.mjs @@ -4,13 +4,4 @@ import preact from "@preact/preset-vite"; export default defineConfig({ plugins: [nitro(), preact()], - environments: { - client: { - build: { - rollupOptions: { - input: "./src/entry-client.tsx", - }, - }, - }, - }, }); diff --git a/examples/vite-ssr-react/vite.config.mjs b/examples/vite-ssr-react/vite.config.mjs index 10a770d2b5..6b1cb80cdd 100644 --- a/examples/vite-ssr-react/vite.config.mjs +++ b/examples/vite-ssr-react/vite.config.mjs @@ -4,9 +4,4 @@ import react from "@vitejs/plugin-react"; export default defineConfig({ plugins: [nitro(), react()], - environments: { - client: { - build: { rollupOptions: { input: "./src/entry-client.tsx" } }, - }, - }, }); diff --git a/examples/vite-ssr-solid/vite.config.mjs b/examples/vite-ssr-solid/vite.config.mjs index 23fdc5dbd4..752821ffb9 100644 --- a/examples/vite-ssr-solid/vite.config.mjs +++ b/examples/vite-ssr-solid/vite.config.mjs @@ -5,12 +5,4 @@ import { nitro } from "nitro/vite"; export default defineConfig({ plugins: [solid({ ssr: true }), nitro()], esbuild: { jsx: "preserve", jsxImportSource: "solid-js" }, - environments: { - ssr: { - build: { rollupOptions: { input: "./src/entry-server.tsx" } }, - }, - client: { - build: { rollupOptions: { input: "./src/entry-client.tsx" } }, - }, - }, }); diff --git a/examples/vite-ssr-vue-router/vite.config.mjs b/examples/vite-ssr-vue-router/vite.config.mjs index 577118e51f..10faaf0ad1 100644 --- a/examples/vite-ssr-vue-router/vite.config.mjs +++ b/examples/vite-ssr-vue-router/vite.config.mjs @@ -5,10 +5,6 @@ import { nitro } from "nitro/vite"; export default defineConfig((_env) => ({ plugins: [patchVueExclude(vue(), /\?assets/), devtoolsJson(), nitro()], - environments: { - client: { build: { rollupOptions: { input: "./app/entry-client.ts" } } }, - ssr: { build: { rollupOptions: { input: "./app/entry-server.ts" } } }, - }, })); // Workaround https://github.com/vitejs/vite-plugin-vue/issues/677 diff --git a/src/build/vite/plugin.ts b/src/build/vite/plugin.ts index 230ce6dd66..e49f493c5d 100644 --- a/src/build/vite/plugin.ts +++ b/src/build/vite/plugin.ts @@ -96,18 +96,43 @@ function nitroEnv(ctx: NitroPluginContext): VitePlugin { ...createServiceEnvironments(ctx), nitro: createNitroEnvironment(ctx), }; - environments.client = { - consumer: userConfig.environments?.client?.consumer ?? "client", - build: { - rollupOptions: { - input: - userConfig.environments?.client?.build?.rollupOptions?.input ?? - ctx._clientEntry ?? - useNitro(ctx).options.renderer?.template, + + let clientEntry: string | undefined = + getEntry( + userConfig.environments?.client?.build?.rolldownOptions?.input || + userConfig.environments?.client?.build?.rollupOptions?.input + ) || useNitro(ctx).options.renderer?.template; + + if (!clientEntry) { + // Auto-detect client entry + clientEntry = resolveModulePath("./entry-client", { + from: ["app", "src", ""].flatMap((d) => + [ctx.nitro!.options.rootDir, ...ctx.nitro!.options.scanDirs].map( + (s) => join(s, d) + "/" + ) + ), + extensions: DEFAULT_EXTENSIONS, + try: true, + }); + if (clientEntry) { + ctx.nitro!.logger.info(`Using \`${prettyPath(clientEntry)}\` as vite client entry.`); + } + } + if (clientEntry) { + environments.client = { + consumer: userConfig.environments?.client?.consumer ?? "client", + build: { + rollupOptions: { + input: clientEntry ? { index: clientEntry } : undefined, + }, + rolldownOptions: { + input: clientEntry ? { index: clientEntry } : undefined, + }, }, - }, - }; - debug("[env] Environments:", Object.keys(environments).join(", ")); + }; + debug("[env] Environments:", Object.keys(environments).join(", ")); + } + return { environments, }; @@ -407,37 +432,6 @@ async function setupNitroContext( ctx.nitro.options.serverEntry = false; } - // Auto-detect client entry - if (!ctx._clientEntry) { - if (userConfig.environments?.client === undefined) { - const clientEntry = resolveModulePath("./entry-client", { - from: ["app", "src", ""].flatMap((d) => - [ctx.nitro!.options.rootDir, ...ctx.nitro!.options.scanDirs].map((s) => join(s, d) + "/") - ), - extensions: DEFAULT_EXTENSIONS, - try: true, - }); - if (clientEntry) { - ctx._clientEntry = clientEntry; - ctx.nitro!.logger.info(`Using \`${prettyPath(clientEntry)}\` as vite client entry.`); - } - } else { - let clientEntry = getEntry( - userConfig.environments.client.build?.rolldownOptions?.input || - userConfig.environments.client.build?.rollupOptions?.input - ); - if (typeof clientEntry === "string") { - ctx._clientEntry = - resolveModulePath(clientEntry, { - from: [ctx.nitro.options.rootDir, ...ctx.nitro.options.scanDirs], - extensions: DEFAULT_EXTENSIONS, - suffixes: ["", "/index"], - try: true, - }) || clientEntry; - } - } - } - // Determine default Vite dist directory const publicDistDir = (ctx._publicDistDir = userConfig.build?.outDir || resolve(ctx.nitro.options.buildDir, "vite/public")); diff --git a/src/build/vite/types.ts b/src/build/vite/types.ts index 7b9310324a..d0b26869d3 100644 --- a/src/build/vite/types.ts +++ b/src/build/vite/types.ts @@ -59,7 +59,6 @@ export interface NitroPluginContext { _isRolldown?: boolean; _initialized?: boolean; _envRunner?: EnvRunner; - _clientEntry?: string; _publicDistDir?: string; _entryPoints: Record; } From c1083e2ddede5bdee397559f3412e8be837bcf44 Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Fri, 27 Feb 2026 19:09:00 +0100 Subject: [PATCH 3/4] only set client.build if not configured --- src/build/vite/plugin.ts | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/build/vite/plugin.ts b/src/build/vite/plugin.ts index e49f493c5d..ec0b331b96 100644 --- a/src/build/vite/plugin.ts +++ b/src/build/vite/plugin.ts @@ -97,22 +97,28 @@ function nitroEnv(ctx: NitroPluginContext): VitePlugin { nitro: createNitroEnvironment(ctx), }; - let clientEntry: string | undefined = - getEntry( - userConfig.environments?.client?.build?.rolldownOptions?.input || - userConfig.environments?.client?.build?.rollupOptions?.input - ) || useNitro(ctx).options.renderer?.template; + let clientEntryConfigured = false; + let clientEntry: string | undefined = getEntry( + userConfig.environments?.client?.build?.rolldownOptions?.input || + userConfig.environments?.client?.build?.rollupOptions?.input + ); + + if (clientEntry) { + clientEntryConfigured = true; + } else { + clientEntry = useNitro(ctx).options.renderer?.template; + } if (!clientEntry) { // Auto-detect client entry clientEntry = resolveModulePath("./entry-client", { + try: true, + extensions: DEFAULT_EXTENSIONS, from: ["app", "src", ""].flatMap((d) => [ctx.nitro!.options.rootDir, ...ctx.nitro!.options.scanDirs].map( (s) => join(s, d) + "/" ) ), - extensions: DEFAULT_EXTENSIONS, - try: true, }); if (clientEntry) { ctx.nitro!.logger.info(`Using \`${prettyPath(clientEntry)}\` as vite client entry.`); @@ -121,14 +127,13 @@ function nitroEnv(ctx: NitroPluginContext): VitePlugin { if (clientEntry) { environments.client = { consumer: userConfig.environments?.client?.consumer ?? "client", - build: { - rollupOptions: { - input: clientEntry ? { index: clientEntry } : undefined, - }, - rolldownOptions: { - input: clientEntry ? { index: clientEntry } : undefined, - }, - }, + build: clientEntryConfigured + ? undefined + : { + rollupOptions: { + input: clientEntry ? { index: clientEntry } : undefined, + }, + }, }; debug("[env] Environments:", Object.keys(environments).join(", ")); } From 4e8ca1c13f04ef8d2f60548433f7d53275a00082 Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Fri, 27 Feb 2026 19:13:29 +0100 Subject: [PATCH 4/4] simplify --- src/build/vite/plugin.ts | 46 +++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/build/vite/plugin.ts b/src/build/vite/plugin.ts index ec0b331b96..62803e85f0 100644 --- a/src/build/vite/plugin.ts +++ b/src/build/vite/plugin.ts @@ -97,31 +97,33 @@ function nitroEnv(ctx: NitroPluginContext): VitePlugin { nitro: createNitroEnvironment(ctx), }; - let clientEntryConfigured = false; - let clientEntry: string | undefined = getEntry( + let clientEntry: string | undefined; + let clientEntryConfigured = !!getEntry( userConfig.environments?.client?.build?.rolldownOptions?.input || userConfig.environments?.client?.build?.rollupOptions?.input ); - - if (clientEntry) { - clientEntryConfigured = true; - } else { - clientEntry = useNitro(ctx).options.renderer?.template; - } - - if (!clientEntry) { - // Auto-detect client entry - clientEntry = resolveModulePath("./entry-client", { - try: true, - extensions: DEFAULT_EXTENSIONS, - from: ["app", "src", ""].flatMap((d) => - [ctx.nitro!.options.rootDir, ...ctx.nitro!.options.scanDirs].map( - (s) => join(s, d) + "/" - ) - ), - }); - if (clientEntry) { - ctx.nitro!.logger.info(`Using \`${prettyPath(clientEntry)}\` as vite client entry.`); + if (!clientEntryConfigured) { + const rendererTemplate = useNitro(ctx).options.renderer?.template; + if (rendererTemplate) { + // Use Nitro renderer template as client entry + clientEntry = rendererTemplate; + ctx.nitro!.logger.info( + `Using Nitro renderer template \`${prettyPath(rendererTemplate)}\` as vite client entry.` + ); + } else { + // Auto-detect client entry + clientEntry = resolveModulePath("./entry-client", { + try: true, + extensions: DEFAULT_EXTENSIONS, + from: ["app", "src", ""].flatMap((d) => + [ctx.nitro!.options.rootDir, ...ctx.nitro!.options.scanDirs].map( + (s) => join(s, d) + "/" + ) + ), + }); + if (clientEntry) { + ctx.nitro!.logger.info(`Using \`${prettyPath(clientEntry)}\` as vite client entry.`); + } } } if (clientEntry) {