From 14db47397b0a9f99923a326bbd23c2df7c5b6745 Mon Sep 17 00:00:00 2001 From: patak Date: Sat, 18 Jun 2022 15:29:49 +0200 Subject: [PATCH] feat!: appType (spa, mpa, custom), boolean middlewareMode (#8452) Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> Co-authored-by: Shinigami Co-authored-by: Bjorn Lu --- docs/config/server-options.md | 22 ++++++------ docs/config/shared-options.md | 14 +++++--- docs/guide/ssr.md | 22 +++++------- packages/vite/src/node/config.ts | 39 ++++++++++++++++++---- packages/vite/src/node/preview.ts | 2 +- packages/vite/src/node/server/index.ts | 19 ++++------- playground/json/server.js | 3 +- playground/optimize-missing-deps/server.js | 5 +-- playground/ssr-deps/server.js | 3 +- playground/ssr-html/server.js | 5 +-- playground/ssr-pug/server.js | 5 +-- playground/ssr-react/server.js | 5 +-- playground/ssr-vue/server.js | 5 +-- 13 files changed, 90 insertions(+), 59 deletions(-) diff --git a/docs/config/server-options.md b/docs/config/server-options.md index 3e09f7b744f2fc..aa187ae9b13b68 100644 --- a/docs/config/server-options.md +++ b/docs/config/server-options.md @@ -146,14 +146,12 @@ export default defineConfig({ ## server.middlewareMode -- **Type:** `'ssr' | 'html'` - -Create Vite server in middleware mode. (without a HTTP server) +- **Type:** `boolean` +- **Default:** `false` -- `'ssr'` will disable Vite's own HTML serving logic so that you should serve `index.html` manually. -- `'html'` will enable Vite's own HTML serving logic. +Create Vite server in middleware mode. -- **Related:** [SSR - Setting Up the Dev Server](/guide/ssr#setting-up-the-dev-server) +- **Related:** [appType](./shared-options#apptype), [SSR - Setting Up the Dev Server](/guide/ssr#setting-up-the-dev-server) - **Example:** @@ -164,17 +162,19 @@ const { createServer: createViteServer } = require('vite') async function createServer() { const app = express() - // Create Vite server in middleware mode. + // Create Vite server in middleware mode const vite = await createViteServer({ - server: { middlewareMode: 'ssr' } + server: { middlewareMode: true }, + appType: 'custom' // don't include Vite's default HTML handling middlewares }) // Use vite's connect instance as middleware app.use(vite.middlewares) app.use('*', async (req, res) => { - // If `middlewareMode` is `'ssr'`, should serve `index.html` here. - // If `middlewareMode` is `'html'`, there is no need to serve `index.html` - // because Vite will do that. + // Since `appType` is `'custom'`, should serve response here. + // Note: if `appType` is `'spa'` or `'mpa'`, Vite includes middlewares to handle + // HTML requests and 404s so user middlewares should be added + // before Vite's middlewares to take effect instead }) } diff --git a/docs/config/shared-options.md b/docs/config/shared-options.md index f44e02d88737dd..991f347c02be13 100644 --- a/docs/config/shared-options.md +++ b/docs/config/shared-options.md @@ -336,9 +336,15 @@ Env variables starts with `envPrefix` will be exposed to your client source code `envPrefix` should not be set as `''`, which will expose all your env variables and cause unexpected leaking of of sensitive information. Vite will throw error when detecting `''`. ::: -## spa +## appType -- **Type:** `boolean` -- **Default:** `true` +- **Type:** `'spa' | 'mpa' | 'custom'` +- **Default:** `'spa'` + +Whether your application is a Single Page Application (SPA), a [Multi Page Application (MPA)](../guide/build#multi-page-app), or Custom Application (SSR and frameworks with custom HTML handling): + +- `'spa'`: include SPA fallback middleware and configure [sirv](https://github.com/lukeed/sirv) with `single: true` in preview +- `'mpa'`: only include non-SPA HTML middlewares +- `'custom'`: don't include HTML middlewares -Whether your application is a Single Page Application (SPA). Set to `false` for other kinds of apps like MPAs. Learn more in Vite's [SSR guide](/guide/ssr#vite-cli). +Learn more in Vite's [SSR guide](/guide/ssr#vite-cli). Related: [`server.middlewareMode`](./server-options#servermiddlewaremode). diff --git a/docs/guide/ssr.md b/docs/guide/ssr.md index 4e75453bbac86a..391bf1eeaec864 100644 --- a/docs/guide/ssr.md +++ b/docs/guide/ssr.md @@ -74,13 +74,12 @@ const { createServer: createViteServer } = require('vite') async function createServer() { const app = express() - // Create Vite server in middleware mode. This disables Vite's own HTML - // serving logic and let the parent server take control. - // - // In middleware mode, if you want to use Vite's own HTML serving logic - // use `'html'` as the `middlewareMode` (ref https://vitejs.dev/config/#server-middlewaremode) + // Create Vite server in middleware mode and configure the app type as + // 'custom', disabling Vite's own HTML serving logic so parent server + // can take control const vite = await createViteServer({ - server: { middlewareMode: 'ssr' } + server: { middlewareMode: true }, + appType: 'custom' }) // use vite's connect instance as middleware app.use(vite.middlewares) @@ -267,11 +266,8 @@ In some cases like `webworker` runtimes, you might want to bundle your SSR build ## Vite CLI -The CLI commands `$ vite dev` and `$ vite preview` can also be used for SSR apps: +The CLI commands `$ vite dev` and `$ vite preview` can also be used for SSR apps. You can add your SSR middlewares to the development server with [`configureServer`](/guide/api-plugin#configureserver) and to the preview server with [`configurePreviewServer`](/guide/api-plugin#configurepreviewserver). -1. Add your SSR middleware to the development server with [`configureServer`](/guide/api-plugin#configureserver) and to the preview server with [`configurePreviewServer`](/guide/api-plugin#configurepreviewserver). - :::tip Note - Use a post hook so that your SSR middleware runs _after_ Vite's middlewares. - ::: - -2. Set `config.spa` to `false`. This switches the development and preview server from SPA mode to SSR/MPA mode. +:::tip Note +Use a post hook so that your SSR middleware runs _after_ Vite's middlewares. +::: diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index 98c2386eff1379..b2eb774861aa7a 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -53,6 +53,15 @@ export interface ConfigEnv { mode: string } +/** + * spa: include SPA fallback middleware and configure sirv with `single: true` in preview + * + * mpa: only include non-SPA HTML middlewares + * + * custom: don't include HTML middlewares + */ +export type AppType = 'spa' | 'mpa' | 'custom' + export type UserConfigFn = (env: ConfigEnv) => UserConfig | Promise export type UserConfigExport = UserConfig | Promise | UserConfigFn @@ -215,11 +224,12 @@ export interface UserConfig { > } /** - * Whether your application is a Single Page Application (SPA). Set to `false` - * for other kinds of apps like MPAs. - * @default true + * Whether your application is a Single Page Application (SPA), + * a Multi-Page Application (MPA), or Custom Application (SSR + * and frameworks with custom HTML handling) + * @default 'spa' */ - spa?: boolean + appType?: AppType } export interface ExperimentalOptions { @@ -302,7 +312,7 @@ export type ResolvedConfig = Readonly< /** @internal */ packageCache: PackageCache worker: ResolveWorkerOptions - spa: boolean + appType: AppType } > @@ -507,6 +517,8 @@ export async function resolveConfig( else ssr = { target: 'node', format: 'cjs' } } + const middlewareMode = config?.server?.middlewareMode + const optimizeDeps = config.optimizeDeps || {} const resolved: ResolvedConfig = { @@ -552,7 +564,22 @@ export async function resolveConfig( } }, worker: resolvedWorkerOptions, - spa: config.spa ?? true + appType: config.appType ?? middlewareMode === 'ssr' ? 'custom' : 'spa' + } + + if (middlewareMode === 'ssr') { + logger.warn( + colors.yellow( + `server.middlewareMode 'ssr' is now deprecated, use server.middlewareMode true and appType 'custom'` + ) + ) + } + if (middlewareMode === 'html') { + logger.warn( + colors.yellow( + `server.middlewareMode 'html' is now deprecated, use server.middlewareMode true` + ) + ) } if (resolved.legacy?.buildRollupPluginCommonjs) { diff --git a/packages/vite/src/node/preview.ts b/packages/vite/src/node/preview.ts index a8cb533e35ad19..dde12ef377bb41 100644 --- a/packages/vite/src/node/preview.ts +++ b/packages/vite/src/node/preview.ts @@ -104,7 +104,7 @@ export async function preview( sirv(distDir, { etag: true, dev: true, - single: config.spa + single: config.appType === 'spa' }) ) diff --git a/packages/vite/src/node/server/index.ts b/packages/vite/src/node/server/index.ts index 169600b680fd99..6397891a2ffe00 100644 --- a/packages/vite/src/node/server/index.ts +++ b/packages/vite/src/node/server/index.ts @@ -100,6 +100,7 @@ export interface ServerOptions extends CommonServerOptions { export interface ResolvedServerOptions extends ServerOptions { fs: Required + middlewareMode: boolean } export interface FileSystemServeOptions { @@ -268,10 +269,7 @@ export async function createServer( config.server.https, config.cacheDir ) - let { middlewareMode } = serverConfig - if (middlewareMode === true) { - middlewareMode = 'ssr' - } + const { middlewareMode } = serverConfig const middlewares = connect() as Connect.Server const httpServer = middlewareMode @@ -489,10 +487,8 @@ export async function createServer( middlewares.use(serveRawFsMiddleware(server)) middlewares.use(serveStaticMiddleware(root, server)) - const isMiddlewareMode = middlewareMode && middlewareMode !== 'html' - // spa fallback - if (config.spa && !isMiddlewareMode) { + if (config.appType === 'spa') { middlewares.use(spaFallbackMiddleware(root)) } @@ -501,12 +497,10 @@ export async function createServer( // serve custom content instead of index.html. postHooks.forEach((fn) => fn && fn()) - if (config.spa && !isMiddlewareMode) { + if (config.appType === 'spa' || config.appType === 'mpa') { // transform index.html middlewares.use(indexHtmlMiddleware(server)) - } - if (!isMiddlewareMode) { // handle 404s // Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...` middlewares.use(function vite404Middleware(_, res) { @@ -516,7 +510,7 @@ export async function createServer( } // error handler - middlewares.use(errorMiddleware(server, !!middlewareMode)) + middlewares.use(errorMiddleware(server, middlewareMode)) const initOptimizer = async () => { if (isDepsOptimizerEnabled(config)) { @@ -654,7 +648,8 @@ export function resolveServerOptions( ): ResolvedServerOptions { const server: ResolvedServerOptions = { preTransformRequests: true, - ...(raw as ResolvedServerOptions) + ...(raw as ResolvedServerOptions), + middlewareMode: !!raw?.middlewareMode } let allowDirs = server.fs?.allow const deny = server.fs?.deny || ['.env', '.env.*', '*.{crt,pem}'] diff --git a/playground/json/server.js b/playground/json/server.js index 6d9007dcd2b75e..b0c0dd18dc43ae 100644 --- a/playground/json/server.js +++ b/playground/json/server.js @@ -20,7 +20,7 @@ async function createServer( root, logLevel: isTest ? 'error' : 'info', server: { - middlewareMode: 'ssr', + middlewareMode: true, watch: { // During tests we edit the files too fast and sometimes chokidar // misses change events, so enforce polling for consistency @@ -28,6 +28,7 @@ async function createServer( interval: 100 } }, + appType: 'custom', json: { stringify: true } diff --git a/playground/optimize-missing-deps/server.js b/playground/optimize-missing-deps/server.js index 5d16bde38d41ba..ace9ca52c6f2b3 100644 --- a/playground/optimize-missing-deps/server.js +++ b/playground/optimize-missing-deps/server.js @@ -17,11 +17,12 @@ async function createServer(root = process.cwd(), hmrPort) { root, logLevel: isTest ? 'error' : 'info', server: { - middlewareMode: 'ssr', + middlewareMode: true, hmr: { port: hmrPort } - } + }, + appType: 'custom' }) app.use(vite.middlewares) diff --git a/playground/ssr-deps/server.js b/playground/ssr-deps/server.js index f1707892ab6aca..d036b736d4fe91 100644 --- a/playground/ssr-deps/server.js +++ b/playground/ssr-deps/server.js @@ -22,7 +22,7 @@ export async function createServer(root = process.cwd(), hmrPort) { root, logLevel: isTest ? 'error' : 'info', server: { - middlewareMode: 'ssr', + middlewareMode: true, watch: { // During tests we edit the files too fast and sometimes chokidar // misses change events, so enforce polling for consistency @@ -33,6 +33,7 @@ export async function createServer(root = process.cwd(), hmrPort) { port: hmrPort } }, + appType: 'custom', ssr: { noExternal: ['no-external-cjs'] } diff --git a/playground/ssr-html/server.js b/playground/ssr-html/server.js index facc502c9b4d07..a27556ceef35f8 100644 --- a/playground/ssr-html/server.js +++ b/playground/ssr-html/server.js @@ -37,7 +37,7 @@ export async function createServer(root = process.cwd(), hmrPort) { root, logLevel: isTest ? 'error' : 'info', server: { - middlewareMode: 'ssr', + middlewareMode: true, watch: { // During tests we edit the files too fast and sometimes chokidar // misses change events, so enforce polling for consistency @@ -47,7 +47,8 @@ export async function createServer(root = process.cwd(), hmrPort) { hmr: { port: hmrPort } - } + }, + appType: 'custom' }) // use vite's connect instance as middleware app.use(vite.middlewares) diff --git a/playground/ssr-pug/server.js b/playground/ssr-pug/server.js index 034540e81d47cf..3975141ce644d2 100644 --- a/playground/ssr-pug/server.js +++ b/playground/ssr-pug/server.js @@ -31,7 +31,7 @@ export async function createServer(root = process.cwd(), hmrPort) { root, logLevel: isTest ? 'error' : 'info', server: { - middlewareMode: 'ssr', + middlewareMode: true, watch: { // During tests we edit the files too fast and sometimes chokidar // misses change events, so enforce polling for consistency @@ -41,7 +41,8 @@ export async function createServer(root = process.cwd(), hmrPort) { hmr: { port: hmrPort } - } + }, + appType: 'custom' }) // use vite's connect instance as middleware app.use(vite.middlewares) diff --git a/playground/ssr-react/server.js b/playground/ssr-react/server.js index a1aee454acc48c..e60444dbcb52d2 100644 --- a/playground/ssr-react/server.js +++ b/playground/ssr-react/server.js @@ -33,7 +33,7 @@ export async function createServer( root, logLevel: isTest ? 'error' : 'info', server: { - middlewareMode: 'ssr', + middlewareMode: true, watch: { // During tests we edit the files too fast and sometimes chokidar // misses change events, so enforce polling for consistency @@ -43,7 +43,8 @@ export async function createServer( hmr: { port: hmrPort } - } + }, + appType: 'custom' }) // use vite's connect instance as middleware app.use(vite.middlewares) diff --git a/playground/ssr-vue/server.js b/playground/ssr-vue/server.js index 5e4e6718bff7bf..3748393deee7c6 100644 --- a/playground/ssr-vue/server.js +++ b/playground/ssr-vue/server.js @@ -37,7 +37,7 @@ export async function createServer( root, logLevel: isTest ? 'error' : 'info', server: { - middlewareMode: 'ssr', + middlewareMode: true, watch: { // During tests we edit the files too fast and sometimes chokidar // misses change events, so enforce polling for consistency @@ -47,7 +47,8 @@ export async function createServer( hmr: { port: hmrPort } - } + }, + appType: 'custom' }) // use vite's connect instance as middleware app.use(vite.middlewares)