Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feat!: appType (spa, mpa, custom), boolean middlewareMode (#8452)
Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com>
Co-authored-by: Shinigami <chrissi92@hotmail.de>
Co-authored-by: Bjorn Lu <bjornlu.dev@gmail.com>
  • Loading branch information
4 people committed Jun 18, 2022
1 parent dbd9688 commit 14db473
Show file tree
Hide file tree
Showing 13 changed files with 90 additions and 59 deletions.
22 changes: 11 additions & 11 deletions docs/config/server-options.md
Expand Up @@ -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:**

Expand All @@ -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
})
}

Expand Down
14 changes: 10 additions & 4 deletions docs/config/shared-options.md
Expand Up @@ -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).
22 changes: 9 additions & 13 deletions docs/guide/ssr.md
Expand Up @@ -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)
Expand Down Expand Up @@ -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.
:::
39 changes: 33 additions & 6 deletions packages/vite/src/node/config.ts
Expand Up @@ -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<UserConfig>
export type UserConfigExport = UserConfig | Promise<UserConfig> | UserConfigFn

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -302,7 +312,7 @@ export type ResolvedConfig = Readonly<
/** @internal */
packageCache: PackageCache
worker: ResolveWorkerOptions
spa: boolean
appType: AppType
}
>

Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion packages/vite/src/node/preview.ts
Expand Up @@ -104,7 +104,7 @@ export async function preview(
sirv(distDir, {
etag: true,
dev: true,
single: config.spa
single: config.appType === 'spa'
})
)

Expand Down
19 changes: 7 additions & 12 deletions packages/vite/src/node/server/index.ts
Expand Up @@ -100,6 +100,7 @@ export interface ServerOptions extends CommonServerOptions {

export interface ResolvedServerOptions extends ServerOptions {
fs: Required<FileSystemServeOptions>
middlewareMode: boolean
}

export interface FileSystemServeOptions {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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))
}

Expand All @@ -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) {
Expand All @@ -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)) {
Expand Down Expand Up @@ -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}']
Expand Down
3 changes: 2 additions & 1 deletion playground/json/server.js
Expand Up @@ -20,14 +20,15 @@ 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
usePolling: true,
interval: 100
}
},
appType: 'custom',
json: {
stringify: true
}
Expand Down
5 changes: 3 additions & 2 deletions playground/optimize-missing-deps/server.js
Expand Up @@ -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)

Expand Down
3 changes: 2 additions & 1 deletion playground/ssr-deps/server.js
Expand Up @@ -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
Expand All @@ -33,6 +33,7 @@ export async function createServer(root = process.cwd(), hmrPort) {
port: hmrPort
}
},
appType: 'custom',
ssr: {
noExternal: ['no-external-cjs']
}
Expand Down
5 changes: 3 additions & 2 deletions playground/ssr-html/server.js
Expand Up @@ -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
Expand All @@ -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)
Expand Down
5 changes: 3 additions & 2 deletions playground/ssr-pug/server.js
Expand Up @@ -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
Expand All @@ -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)
Expand Down
5 changes: 3 additions & 2 deletions playground/ssr-react/server.js
Expand Up @@ -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
Expand All @@ -43,7 +43,8 @@ export async function createServer(
hmr: {
port: hmrPort
}
}
},
appType: 'custom'
})
// use vite's connect instance as middleware
app.use(vite.middlewares)
Expand Down
5 changes: 3 additions & 2 deletions playground/ssr-vue/server.js
Expand Up @@ -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
Expand All @@ -47,7 +47,8 @@ export async function createServer(
hmr: {
port: hmrPort
}
}
},
appType: 'custom'
})
// use vite's connect instance as middleware
app.use(vite.middlewares)
Expand Down

0 comments on commit 14db473

Please sign in to comment.