diff --git a/packages/schema/src/config/experimental.ts b/packages/schema/src/config/experimental.ts index d59eca09ac5..835f76a2c8a 100644 --- a/packages/schema/src/config/experimental.ts +++ b/packages/schema/src/config/experimental.ts @@ -5,16 +5,11 @@ export default { * Set to true to generate an async entry point for the Vue bundle (for module federation support). */ asyncEntry: { - $resolve: (val, get) => val ?? (get('dev') && get('experimental.viteNode')) ?? false + $resolve: (val, get) => val ?? false }, /** - * Use `vite-node` for on-demand server chunk loading. - */ - viteNode: process.env.EXPERIMENTAL_VITE_NODE ? true : false, - - /** - * Enable Vue's reactivity transform. + * Enable Vue's reactivity transform * @see https://vuejs.org/guide/extras/reactivity-transform.html */ reactivityTransform: false, @@ -31,6 +26,23 @@ export default { */ treeshakeClientOnly: false, + /** + * Use vite-node for on-demand server chunk loading + * + * @deprecated use `vite.devBundler: 'vite-node'` + */ + viteNode: { + $resolve: (val) => { + val = process.env.EXPERIMENTAL_VITE_NODE ? true : val + if (val === true) { + console.warn('`vite-node` is now enabled by default. You can safely remove `experimental.viteNode` from your config.') + } else if (val === false) { + console.warn('`vite-node` is now enabled by default. To disable it, set `vite.devBundler` to `legacy` instead.') + } + return val ?? true + } + }, + /** * Split server bundle into multiple chunks and dynamically import them. * diff --git a/packages/schema/src/types/config.ts b/packages/schema/src/types/config.ts index 3207e7f87ae..6e201627130 100644 --- a/packages/schema/src/types/config.ts +++ b/packages/schema/src/types/config.ts @@ -45,4 +45,9 @@ export interface ViteConfig extends ViteUserConfig { * @see https://github.com/vitejs/vite/tree/main/packages/plugin-vue */ vue?: VuePluginOptions + /** + * Bundler for dev time server-side rendering. + * @default 'vite-node' + */ + devBundler?: 'vite-node' | 'legacy' } diff --git a/packages/vite/src/client.ts b/packages/vite/src/client.ts index d42c293739d..1ee964cc8fe 100644 --- a/packages/vite/src/client.ts +++ b/packages/vite/src/client.ts @@ -13,9 +13,14 @@ import { wpfs } from './utils/wpfs' import type { ViteBuildContext, ViteOptions } from './vite' import { writeManifest } from './manifest' import { devStyleSSRPlugin } from './plugins/dev-ssr-css' +import { viteNodePlugin } from './vite-node' export async function buildClient (ctx: ViteBuildContext) { + const useAsyncEntry = ctx.nuxt.options.experimental.asyncEntry + ctx.entry = resolve(ctx.nuxt.options.appDir, useAsyncEntry ? 'entry.async' : 'entry') + const clientConfig: vite.InlineConfig = vite.mergeConfig(ctx.config, { + entry: ctx.entry, experimental: { renderBuiltUrl: (filename, { type, hostType }) => { if (hostType !== 'js' || type === 'asset') { @@ -30,6 +35,9 @@ export async function buildClient (ctx: ViteBuildContext) { 'process.client': true, 'module.hot': false }, + optimizeDeps: { + entries: [ctx.entry] + }, resolve: { alias: { '#build/plugins': resolve(ctx.nuxt.options.buildDir, 'plugins/client'), @@ -38,7 +46,10 @@ export async function buildClient (ctx: ViteBuildContext) { }, build: { manifest: true, - outDir: resolve(ctx.nuxt.options.buildDir, 'dist/client') + outDir: resolve(ctx.nuxt.options.buildDir, 'dist/client'), + rollupOptions: { + input: ctx.entry + } }, plugins: [ cacheDirPlugin(ctx.nuxt.options.rootDir, 'client'), @@ -48,9 +59,7 @@ export async function buildClient (ctx: ViteBuildContext) { srcDir: ctx.nuxt.options.srcDir, buildAssetsURL: joinURL(ctx.nuxt.options.app.baseURL, ctx.nuxt.options.app.buildAssetsDir) }), - ctx.nuxt.options.experimental.viteNode - ? await import('./vite-node').then(r => r.viteNodePlugin(ctx)) - : undefined + viteNodePlugin(ctx) ], appType: 'custom', server: { diff --git a/packages/vite/src/server.ts b/packages/vite/src/server.ts index 3ca723a5d23..bdae8491d4c 100644 --- a/packages/vite/src/server.ts +++ b/packages/vite/src/server.ts @@ -8,10 +8,16 @@ import { joinURL, withoutLeadingSlash, withTrailingSlash } from 'ufo' import { ViteBuildContext, ViteOptions } from './vite' import { wpfs } from './utils/wpfs' import { cacheDirPlugin } from './plugins/cache-dir' +import { initViteNodeServer } from './vite-node' export async function buildServer (ctx: ViteBuildContext) { + const useAsyncEntry = ctx.nuxt.options.experimental.asyncEntry || + (ctx.nuxt.options.vite.devBundler === 'vite-node' && ctx.nuxt.options.dev) + ctx.entry = resolve(ctx.nuxt.options.appDir, useAsyncEntry ? 'entry.async' : 'entry') + const _resolve = (id: string) => resolveModule(id, { paths: ctx.nuxt.options.modulesDir }) const serverConfig: vite.InlineConfig = vite.mergeConfig(ctx.config, { + entry: ctx.entry, base: ctx.nuxt.options.dev ? joinURL(ctx.nuxt.options.app.baseURL.replace(/^\.\//, '/') || '/', ctx.nuxt.options.app.buildAssetsDir) : undefined, @@ -39,6 +45,9 @@ export async function buildServer (ctx: ViteBuildContext) { 'typeof location': '"undefined"', 'typeof XMLHttpRequest': '"undefined"' }, + optimizeDeps: { + entries: [ctx.entry] + }, resolve: { alias: { '#build/plugins': resolve(ctx.nuxt.options.buildDir, 'plugins/server'), @@ -73,6 +82,7 @@ export async function buildServer (ctx: ViteBuildContext) { outDir: resolve(ctx.nuxt.options.buildDir, 'dist/server'), ssr: ctx.nuxt.options.ssr ?? true, rollupOptions: { + input: ctx.entry, external: ['#internal/nitro', ...ctx.nuxt.options.experimental.externalVue ? ['vue', 'vue-router'] : []], output: { entryFileNames: 'server.mjs', @@ -141,10 +151,10 @@ export async function buildServer (ctx: ViteBuildContext) { // Initialize plugins await viteServer.pluginContainer.buildStart({}) - if (ctx.nuxt.options.experimental.viteNode) { - logger.info('Vite server using experimental `vite-node`...') - await import('./vite-node').then(r => r.initViteNodeServer(ctx)) + if (ctx.config.devBundler !== 'legacy') { + await initViteNodeServer(ctx) } else { + logger.info('Vite server using legacy server bundler...') await import('./dev-bundler').then(r => r.initViteDevBundler(ctx, onBuild)) } } diff --git a/packages/vite/src/vite.ts b/packages/vite/src/vite.ts index c92518dc2e9..fcaacedf23e 100644 --- a/packages/vite/src/vite.ts +++ b/packages/vite/src/vite.ts @@ -1,5 +1,5 @@ import * as vite from 'vite' -import { join, resolve } from 'pathe' +import { join } from 'pathe' import type { Nuxt } from '@nuxt/schema' import type { InlineConfig, SSROptions } from 'vite' import { logger, isIgnored } from '@nuxt/kit' @@ -16,6 +16,7 @@ import { composableKeysPlugin } from './plugins/composable-keys' export interface ViteOptions extends InlineConfig { vue?: Options ssr?: SSROptions + devBundler?: 'vite-node' | 'legacy' } export interface ViteBuildContext { @@ -27,10 +28,9 @@ export interface ViteBuildContext { } export async function bundle (nuxt: Nuxt) { - const entry = resolve(nuxt.options.appDir, nuxt.options.experimental.asyncEntry ? 'entry.async' : 'entry') const ctx: ViteBuildContext = { nuxt, - entry, + entry: null, config: vite.mergeConfig( { resolve: { @@ -47,14 +47,12 @@ export async function bundle (nuxt: Nuxt) { } }, optimizeDeps: { - entries: [entry], include: ['vue'] }, css: resolveCSSOptions(nuxt), build: { rollupOptions: { - output: { sanitizeFileName: sanitizeFilePath }, - input: resolve(nuxt.options.appDir, 'entry') + output: { sanitizeFileName: sanitizeFilePath } }, watch: { exclude: nuxt.options.ignore