Skip to content

Commit

Permalink
refactor: move middlewares to a dedicated folder
Browse files Browse the repository at this point in the history
  • Loading branch information
magne4000 committed Feb 26, 2025
1 parent 1fe4a59 commit 293880e
Show file tree
Hide file tree
Showing 10 changed files with 186 additions and 159 deletions.
4 changes: 2 additions & 2 deletions packages/vike-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@
},
"dependencies": {
"@brillout/picocolors": "^1.0.26",
"@universal-middleware/compress": "^0.2.10",
"@universal-middleware/core": "^0.4.0",
"@universal-middleware/compress": "^0.2.11",
"@universal-middleware/core": "^0.4.1",
"@vercel/nft": "^0.29.2",
"esbuild": "^0.25.0",
"resolve-from": "^5.0.0",
Expand Down
21 changes: 21 additions & 0 deletions packages/vike-server/src/middlewares/compress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import compressMiddlewareFactory from '@universal-middleware/compress'
import { isVercel } from '../utils/isVercel.js'
import { globalStore } from '../runtime/globalStore.js'
import type { VikeOptions } from '../runtime/types.js'
import type { Get, UniversalMiddleware } from '@universal-middleware/core'

export const compressMiddleware = ((options?) => async (request, _context) => {
const compressionType = options?.compress ?? !isVercel()
const compressMiddlewareInternal = compressMiddlewareFactory()(request)

return async (response) => {
if (!globalStore.isDev) {
const isAsset = new URL(request.url).pathname.startsWith('/assets/')
const shouldCompressResponse = compressionType === true || (compressionType === 'static' && isAsset)
if (shouldCompressResponse) {
return compressMiddlewareInternal(response)
}
}
return response
}
}) satisfies Get<[options: VikeOptions], UniversalMiddleware>
22 changes: 22 additions & 0 deletions packages/vike-server/src/middlewares/devServer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { Get, UniversalMiddleware } from '@universal-middleware/core'
import { connectToWeb } from '../runtime/adapters/connectToWeb.js'
import { handleViteDevServer } from '../runtime/adapters/handleViteDevServer.js'
import type { IncomingMessage } from 'node:http'
import { globalStore } from '../runtime/globalStore.js'

export const devServerMiddleware = (() => async (request, _context, runtime) => {
const nodeReq: IncomingMessage | undefined = 'req' in runtime ? runtime.req : undefined

if (nodeReq) {
const needsUpgrade = globalStore.setupHMRProxy(nodeReq)

if (needsUpgrade) {
// Early response for HTTP connection upgrade
return new Response(null)
}
}

const handled = await connectToWeb(handleViteDevServer)(request)

if (handled) return handled
}) satisfies Get<[], UniversalMiddleware>
72 changes: 72 additions & 0 deletions packages/vike-server/src/middlewares/serveStatic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import type { Get, UniversalMiddleware } from '@universal-middleware/core'
import { connectToWeb } from '../runtime/adapters/connectToWeb.js'
import type { IncomingMessage, ServerResponse } from 'node:http'
import { getGlobalContextAsync } from 'vike/server'
import { globalStore } from '../runtime/globalStore.js'
import { assert } from '../utils/assert.js'
import type { ConnectMiddleware, VikeOptions } from '../runtime/types.js'
import { isVercel } from '../utils/isVercel.js'
import { dirname, isAbsolute, join } from 'node:path'
import { fileURLToPath } from 'node:url'

async function removeBaseUrl(req: IncomingMessage) {
if (!req.url) return
const globalContext = await getGlobalContextAsync(!globalStore.isDev)
const baseAssets = globalContext.baseAssets as string
// Don't choke on older Vike versions
if (baseAssets === undefined) return
const { url } = req
assert(url.startsWith('/'))
let urlWithoutBase = url.slice(baseAssets.length)
if (!urlWithoutBase.startsWith('/')) urlWithoutBase = `/${urlWithoutBase}`
req.url = urlWithoutBase
}

function resolveStaticConfig(static_: VikeOptions['static']): false | { root: string; cache: boolean } {
// Disable static file serving for Vercel
// Vercel will serve static files on its own
// See vercel.json > outputDirectory
if (isVercel()) return false
if (static_ === false) return false

const argv1 = process.argv[1]
const entrypointDirAbs = argv1
? dirname(isAbsolute(argv1) ? argv1 : join(process.cwd(), argv1))
: dirname(fileURLToPath(import.meta.url))
const defaultStaticDir = join(entrypointDirAbs, '..', 'client')

if (static_ === true || static_ === undefined) {
return { root: defaultStaticDir, cache: true }
}
if (typeof static_ === 'string') {
return { root: static_, cache: true }
}
return {
root: static_.root ?? defaultStaticDir,
cache: static_.cache ?? true
}
}

export const serveStaticMiddleware = ((options?) => async (request, _context) => {
const staticConfig = resolveStaticConfig(options?.static)
let staticMiddleware: ConnectMiddleware | undefined

// FIXME port sirv to universal-middleware
async function serveStaticFiles(req: IncomingMessage, res: ServerResponse): Promise<boolean> {
await removeBaseUrl(req)

if (!staticMiddleware) {
const { default: sirv } = await import('sirv')
staticMiddleware = sirv((staticConfig as { root: string; cache: boolean }).root, { etag: true })
}

return new Promise<boolean>((resolve) => {
res.once('close', () => resolve(true))
// biome-ignore lint/style/noNonNullAssertion: <explanation>
staticMiddleware!(req, res, () => resolve(false))
})
}

const handled = await connectToWeb(serveStaticFiles)(request)
if (handled) return handled
}) satisfies Get<[options: VikeOptions], UniversalMiddleware>
3 changes: 3 additions & 0 deletions packages/vike-server/src/plugin/plugins/commonConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ function commonConfig(configVikeNodePlugin: ConfigVikeNodePlugin): Plugin[] {
config(config) {
;(config as Record<string, unknown>).configVikeNode = resolvedConfig
return {
build: {
target: 'es2022'
},
ssr: {
external: resolvedConfig.server.external
},
Expand Down
29 changes: 0 additions & 29 deletions packages/vike-server/src/runtime/utils/resolve-static-config.ts

This file was deleted.

123 changes: 26 additions & 97 deletions packages/vike-server/src/runtime/vike-handler.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
import type { IncomingMessage, ServerResponse } from 'node:http'
import compressMiddlewareFactory from '@universal-middleware/compress'
import type { Get, RuntimeAdapter, UniversalHandler, UniversalMiddleware } from '@universal-middleware/core'
import { getGlobalContextAsync, renderPage as _renderPage } from 'vike/server'
import { isVercel } from '../utils/isVercel.js'
import { connectToWeb } from './adapters/connectToWeb.js'
import { globalStore } from './globalStore.js'
import type { ConnectMiddleware, VikeHttpResponse, VikeOptions } from './types.js'
import { enhance, type Get, type RuntimeAdapter, type UniversalHandler } from '@universal-middleware/core'
import { renderPage as _renderPage } from 'vike/server'
import type { VikeHttpResponse, VikeOptions } from './types.js'
import { parseHeaders } from './utils/header-utils.js'
import { assert } from '../utils/assert.js'

async function renderPage<T extends RuntimeAdapter>({
url,
Expand Down Expand Up @@ -42,92 +36,27 @@ async function renderPage<T extends RuntimeAdapter>({
return pageContext.httpResponse
}

export const compressMiddleware = ((options?) => async (request, _context) => {
const compressionType = options?.compress ?? !isVercel()
const compressMiddlewareInternal = compressMiddlewareFactory()(request)

return async (response) => {
if (!globalStore.isDev) {
const isAsset = new URL(request.url).pathname.startsWith('/assets/')
const shouldCompressResponse = compressionType === true || (compressionType === 'static' && isAsset)
if (shouldCompressResponse) {
return compressMiddlewareInternal(response)
}
}
return response
}
}) satisfies Get<[options: VikeOptions], UniversalMiddleware>

export const renderPageHandler = ((options?) => async (request, context, runtime) => {
const nodeReq: IncomingMessage | undefined = 'req' in runtime ? runtime.req : undefined
let staticConfig: false | { root: string; cache: boolean } = false
let staticMiddleware: ConnectMiddleware | undefined

if (nodeReq) {
const needsUpgrade = globalStore.setupHMRProxy(nodeReq)

if (needsUpgrade) {
// Early response for HTTP connection upgrade
return new Response(null)
export const renderPageHandler = ((options?) =>
enhance(
async (request, context, runtime) => {
const pageContextInit = { ...context, runtime, urlOriginal: request.url, headersOriginal: request.headers }
const response = await renderPage({
url: request.url,
headers: parseHeaders(request.headers),
runtimeRequest: runtime,
options: {
...options,
pageContextUniversal: pageContextInit,
pageContext: options?.pageContext
}
})

return new Response(response.getReadableWebStream(), {
status: response.statusCode,
headers: response.headers
})
},
{
order: 0
}

const { resolveStaticConfig } = await import('./utils/resolve-static-config.js')
staticConfig = resolveStaticConfig(options?.static)
}

if (globalStore.isDev) {
const { handleViteDevServer } = await import('./adapters/handleViteDevServer.js')
const handled = await connectToWeb(handleViteDevServer)(request)
if (handled) return handled
} else if (nodeReq) {
if (staticConfig) {
const handled = await connectToWeb(serveStaticFiles)(request)
if (handled) return handled
}
}

async function serveStaticFiles(req: IncomingMessage, res: ServerResponse): Promise<boolean> {
await removeBaseUrl(req)

if (!staticMiddleware) {
const { default: sirv } = await import('sirv')
staticMiddleware = sirv((staticConfig as { root: string; cache: boolean }).root, { etag: true })
}

return new Promise<boolean>((resolve) => {
res.once('close', () => resolve(true))
// biome-ignore lint/style/noNonNullAssertion: <explanation>
staticMiddleware!(req, res, () => resolve(false))
})
}

const pageContextInit = { ...context, runtime, urlOriginal: request.url, headersOriginal: request.headers }
const response = await renderPage({
url: request.url,
headers: parseHeaders(request.headers),
runtimeRequest: runtime,
options: {
...options,
pageContextUniversal: pageContextInit,
pageContext: options?.pageContext
}
})

return new Response(response.getReadableWebStream(), {
status: response.statusCode,
headers: response.headers
})
}) satisfies Get<[options: VikeOptions], UniversalHandler>

async function removeBaseUrl(req: IncomingMessage) {
if (!req.url) return
const globalContext = await getGlobalContextAsync(!globalStore.isDev)
const baseAssets = globalContext.baseAssets as string
// Don't choke on older Vike versions
if (baseAssets === undefined) return
const { url } = req
assert(url.startsWith('/'))
let urlWithoutBase = url.slice(baseAssets.length)
if (!urlWithoutBase.startsWith('/')) urlWithoutBase = `/${urlWithoutBase}`
req.url = urlWithoutBase
}
)) satisfies Get<[options: VikeOptions], UniversalHandler>
20 changes: 14 additions & 6 deletions packages/vike-server/src/vike.handler.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { type Get, type RuntimeAdapter, type UniversalHandler, pipe } from '@universal-middleware/core'
import { type Get, pipe, type RuntimeAdapter, type UniversalHandler } from '@universal-middleware/core'
import type { VikeOptions } from './runtime/types.js'
import { compressMiddleware, renderPageHandler } from './runtime/vike-handler.js'
import { renderPageHandler } from './runtime/vike-handler.js'
import { globalStore } from './runtime/globalStore.js'

// https://vike.dev/pageContext#typescript
declare global {
Expand All @@ -11,9 +12,16 @@ declare global {
}
}

const renderPageUniversal = ((options?) => pipe(compressMiddleware(options), renderPageHandler(options))) satisfies Get<
[options: VikeOptions],
UniversalHandler
>
let renderPageUniversal: Get<[options: VikeOptions], UniversalHandler>

if (globalStore.isDev) {
const { devServerMiddleware } = await import('./middlewares/devServer.js')
renderPageUniversal = (options?) => pipe(devServerMiddleware(), renderPageHandler(options))
} else {
const { compressMiddleware } = await import('./middlewares/compress.js')
const { serveStaticMiddleware } = await import('./middlewares/serveStatic.js')
renderPageUniversal = (options?) =>
pipe(compressMiddleware(options), serveStaticMiddleware(options), renderPageHandler(options))
}

export default renderPageUniversal
3 changes: 2 additions & 1 deletion packages/vike-server/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"rootDir": "./src",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"target": "ES2019",
"target": "ES2022",
"verbatimModuleSyntax": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"types": ["vite/client"],
"esModuleInterop": true,
Expand Down
Loading

0 comments on commit 293880e

Please sign in to comment.