Skip to content

Commit

Permalink
refactor(preview)!: use base middleware (#14818)
Browse files Browse the repository at this point in the history
Co-authored-by: 翠 / green <green@sapphi.red>
  • Loading branch information
bluwy and sapphi-red authored Nov 2, 2023
1 parent 42fee9e commit 69737f4
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 54 deletions.
3 changes: 3 additions & 0 deletions docs/guide/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))
There are some changes which only affect plugin/tool creators.

- [[#14119] refactor!: merge `PreviewServerForHook` into `PreviewServer` type](https://github.com/vitejs/vite/pull/14119)
- The `configurePreviewServer` hook now accepts the `PreviewServer` type instead of `PreviewServerForHook` type.
- [[#14818] refactor(preview)!: use base middleware](https://github.com/vitejs/vite/pull/14818)
- Middlewares added from the returned function in `configurePreviewServer` now does not have access to the `base` when comparing the `req.url` value. This aligns the behaviour with the dev server. You can check the `base` from the `configResolved` hook if needed.

Also there are other breaking changes which only affect few users.

Expand Down
39 changes: 16 additions & 23 deletions packages/vite/src/node/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
} from './http'
import { openBrowser } from './server/openBrowser'
import compression from './server/middlewares/compression'
import { baseMiddleware } from './server/middlewares/base'
import { htmlFallbackMiddleware } from './server/middlewares/htmlFallback'
import { indexHtmlMiddleware } from './server/middlewares/indexHtml'
import { notFoundMiddleware } from './server/middlewares/notFound'
Expand Down Expand Up @@ -167,8 +168,10 @@ export async function preview(

app.use(compression())

const previewBase =
config.base === './' || config.base === '' ? '/' : config.base
// base
if (config.base !== '/') {
app.use(baseMiddleware(config.rawBase, false))
}

// static assets
const headers = config.preview.headers
Expand All @@ -190,36 +193,28 @@ export async function preview(
},
})(...args)

app.use(previewBase, viteAssetMiddleware)
app.use(viteAssetMiddleware)

// html fallback
if (config.appType === 'spa' || config.appType === 'mpa') {
app.use(
previewBase,
htmlFallbackMiddleware(
distDir,
config.appType === 'spa',
previewBase !== '/',
),
)
app.use(htmlFallbackMiddleware(distDir, config.appType === 'spa'))
}

// apply post server hooks from plugins
postHooks.forEach((fn) => fn && fn())

if (config.appType === 'spa' || config.appType === 'mpa') {
// transform index.html
app.use(previewBase, indexHtmlMiddleware(distDir, server))
app.use(indexHtmlMiddleware(distDir, server))

// handle 404s
app.use(previewBase, notFoundMiddleware())
app.use(notFoundMiddleware())
}

const hostname = await resolveHostname(options.host)
const port = options.port ?? DEFAULT_PREVIEW_PORT
const protocol = options.https ? 'https' : 'http'

const serverPort = await httpServerStart(httpServer, {
await httpServerStart(httpServer, {
port,
strictPort: options.strictPort,
host: hostname.host,
Expand All @@ -233,14 +228,12 @@ export async function preview(
)

if (options.open) {
const path = typeof options.open === 'string' ? options.open : previewBase
openBrowser(
path.startsWith('http')
? path
: new URL(path, `${protocol}://${hostname.name}:${serverPort}`).href,
true,
logger,
)
const url = server.resolvedUrls?.local[0] ?? server.resolvedUrls?.network[0]
if (url) {
const path =
typeof options.open === 'string' ? new URL(options.open, url).href : url
openBrowser(path, true, logger)
}
}

return server as PreviewServer
Expand Down
2 changes: 1 addition & 1 deletion packages/vite/src/node/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,7 @@ export async function _createServer(

// base
if (config.base !== '/') {
middlewares.use(baseMiddleware(server))
middlewares.use(baseMiddleware(config.rawBase, middlewareMode))
}

// open in editor support
Expand Down
28 changes: 16 additions & 12 deletions packages/vite/src/node/server/middlewares/base.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,39 @@
import type { Connect } from 'dep-types/connect'
import type { ViteDevServer } from '..'
import { joinUrlSegments, stripBase, withTrailingSlash } from '../../utils'
import {
cleanUrl,
joinUrlSegments,
stripBase,
withTrailingSlash,
} from '../../utils'

// this middleware is only active when (base !== '/')

export function baseMiddleware({
config,
}: ViteDevServer): Connect.NextHandleFunction {
export function baseMiddleware(
rawBase: string,
middlewareMode: boolean,
): Connect.NextHandleFunction {
// Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...`
return function viteBaseMiddleware(req, res, next) {
const url = req.url!
const parsed = new URL(url, 'http://vitejs.dev')
const path = parsed.pathname || '/'
const base = config.rawBase
const pathname = cleanUrl(url)
const base = rawBase

if (path.startsWith(base)) {
if (pathname.startsWith(base)) {
// rewrite url to remove base. this ensures that other middleware does
// not need to consider base being prepended or not
req.url = stripBase(url, base)
return next()
}

// skip redirect and error fallback on middleware mode, #4057
if (config.server.middlewareMode) {
if (middlewareMode) {
return next()
}

if (path === '/' || path === '/index.html') {
if (pathname === '/' || pathname === '/index.html') {
// redirect root visit to based url with search and hash
res.writeHead(302, {
Location: base + (parsed.search || '') + (parsed.hash || ''),
Location: base + url.slice(pathname.length),
})
res.end()
return
Expand Down
22 changes: 4 additions & 18 deletions packages/vite/src/node/server/middlewares/htmlFallback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,7 @@ const debug = createDebugger('vite:html-fallback')
export function htmlFallbackMiddleware(
root: string,
spaFallback: boolean,
mounted = false,
): Connect.NextHandleFunction {
// When this middleware is mounted on a route, we need to re-assign `req.url` with a
// leading `.` to signal a relative rewrite. Returning with a leading `/` returns a
// buggy `req.url`. e.g.:
//
// mount /foo/bar:
// req.url = /index.html
// final = /foo/barindex.html
//
// mount /foo/bar:
// req.url = ./index.html
// final = /foo/bar/index.html
const prepend = mounted ? '.' : ''

// Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...`
return function viteHtmlFallbackMiddleware(req, res, next) {
if (
Expand Down Expand Up @@ -51,7 +37,7 @@ export function htmlFallbackMiddleware(
const filePath = path.join(root, pathname)
if (fs.existsSync(filePath)) {
debug?.(`Rewriting ${req.method} ${req.url} to ${url}`)
req.url = prepend + url
req.url = url
return next()
}
}
Expand All @@ -61,7 +47,7 @@ export function htmlFallbackMiddleware(
if (fs.existsSync(filePath)) {
const newUrl = url + 'index.html'
debug?.(`Rewriting ${req.method} ${req.url} to ${newUrl}`)
req.url = prepend + newUrl
req.url = newUrl
return next()
}
}
Expand All @@ -71,14 +57,14 @@ export function htmlFallbackMiddleware(
if (fs.existsSync(filePath)) {
const newUrl = url + '.html'
debug?.(`Rewriting ${req.method} ${req.url} to ${newUrl}`)
req.url = prepend + newUrl
req.url = newUrl
return next()
}
}

if (spaFallback) {
debug?.(`Rewriting ${req.method} ${req.url} to /index.html`)
req.url = prepend + '/index.html'
req.url = '/index.html'
}

next()
Expand Down

0 comments on commit 69737f4

Please sign in to comment.