Skip to content

Commit

Permalink
feat!: migrate to h3 0.5x with events API (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
pi0 authored Mar 29, 2022
1 parent e06573c commit c705dd4
Show file tree
Hide file tree
Showing 18 changed files with 114 additions and 109 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"fs-extra": "^10.0.1",
"globby": "^13.1.1",
"gzip-size": "^7.0.0",
"h3": "^0.4.1",
"h3": "^0.5.1",
"hasha": "^5.2.2",
"hookable": "^5.1.1",
"http-proxy": "^1.18.1",
Expand Down
4 changes: 2 additions & 2 deletions src/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ export async function writeTypes (nitro: Nitro) {
]

for (const mw of middleware) {
if (typeof mw.handle !== 'string') { continue }
const relativePath = relative(nitro.options.buildDir, mw.handle).replace(/\.[a-z]+$/, '')
if (typeof mw.handler !== 'string') { continue }
const relativePath = relative(nitro.options.buildDir, mw.handler).replace(/\.[a-z]+$/, '')
routeTypes[mw.route] = routeTypes[mw.route] || []
routeTypes[mw.route].push(`Awaited<ReturnType<typeof import('${relativePath}').default>>`)
}
Expand Down
2 changes: 1 addition & 1 deletion src/rollup/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export const getRollupConfig = (nitro: Nitro) => {
prefix = 'nuxt'
} else if (lastModule.startsWith(runtimeDir)) {
prefix = 'nitro'
} else if (nitro.options.middleware.find(m => lastModule.startsWith(m.handle as string))) {
} else if (nitro.options.middleware.find(m => lastModule.startsWith(m.handler as string))) {
prefix = 'middleware'
} else if (lastModule.includes('assets')) {
prefix = 'assets'
Expand Down
25 changes: 13 additions & 12 deletions src/rollup/plugins/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,32 +29,33 @@ export function middleware (getMiddleware: () => ServerMiddleware[]) {
}

// Imports take priority
const imports = unique(middleware.filter(m => m.lazy === false).map(m => m.handle))
const imports = unique(middleware.filter(m => m.lazy === false).map(m => m.handler || m.handle))

// Lazy imports should fill in the gaps
const lazyImports = unique(middleware.filter(m => m.lazy !== false && !imports.includes(m.handle)).map(m => m.handle))
const lazyImports = unique(middleware.filter(m => m.lazy !== false && !imports.includes(m.handler || m.handle)).map(m => m.handler || m.handle))

return `
${imports.map(handle => `import ${getImportId(handle)} from '${handle}';`).join('\n')}
const code = `
${imports.map(handler => `import ${getImportId(handler)} from '${handler}';`).join('\n')}
${lazyImports.map(handle => `const ${getImportId(handle)} = () => import('${handle}');`).join('\n')}
${lazyImports.map(handler => `const ${getImportId(handler)} = () => import('${handler}');`).join('\n')}
const middleware = [
${middleware.map(m => `{ route: '${m.route}', handle: ${getImportId(m.handle)}, lazy: ${m.lazy || true}, promisify: ${m.promisify !== undefined ? m.promisify : true} }`).join(',\n')}
];
const middleware = [
${middleware.map(m => ` { route: '${m.route}', handler: ${getImportId(m.handler || m.handle)}, lazy: ${m.lazy || true} }`).join(',\n')}
];
export default middleware
`
export default middleware
`.trim()
return code
}
}
})
}

function dumpMiddleware (middleware: ServerMiddleware[]) {
const data = middleware.map(({ route, handle, ...props }) => {
const data = middleware.map(({ route, handler, ...props }) => {
return [
(route && route !== '/') ? route : '*',
relative(process.cwd(), handle as string),
relative(process.cwd(), handler as string),
dumpObject(props)
]
})
Expand Down
6 changes: 3 additions & 3 deletions src/runtime/cache.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import hash from 'object-hash'
import type { Handle } from 'h3'
import type { Handler } from 'h3'
import { storage } from '#storage'

export interface CacheEntry {
Expand Down Expand Up @@ -86,7 +86,7 @@ function getKey (...args) {
return args.length ? hash(args, {}) : ''
}

export function cachifyHandle (handle: Handle, opts: Omit<CachifyOptions, 'getKey'> = defaultCacheOptions) {
export function cachifyHandle (handler: Handler, opts: Omit<CachifyOptions, 'getKey'> = defaultCacheOptions) {
const _opts: CachifyOptions = {
getKey: req => req.originalUrl || req.url,
transform (entry, _req, res) {
Expand All @@ -111,7 +111,7 @@ export function cachifyHandle (handle: Handle, opts: Omit<CachifyOptions, 'getKe
}

return cachify(async (req, res) => {
const body = await handle(req, res)
const body = await handler(req, res)
const headers = res.getHeaders()
return { body, headers }
}, _opts)
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/entries/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { join } from 'path'
import { mkdirSync } from 'fs'
import { threadId, parentPort } from 'worker_threads'
import { isWindows, provider } from 'std-env'
import { handle } from '..'
import { app } from '..'

const server = new Server(handle)
const server = new Server(app.nodeHandler)

function getAddress () {
// https://github.com/nuxt/framework/issues/1636
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/entries/firebase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ import '#polyfill'

// @ts-ignore
import functions from 'firebase-functions'
import { handle } from '..'
import { app } from '..'

export const server = functions.https.onRequest(handle)
export const server = functions.https.onRequest(app.nodeHandler)
4 changes: 2 additions & 2 deletions src/runtime/entries/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import '#polyfill'
import { Server as HttpServer } from 'http'
import { Server as HttpsServer } from 'https'
import destr from 'destr'
import { handle } from '..'
import { app } from '..'
import { baseURL } from '#paths'

const cert = process.env.NITRO_SSL_CERT
const key = process.env.NITRO_SSL_KEY

const server = cert && key ? new HttpsServer({ key, cert }, handle) : new HttpServer(handle)
const server = cert && key ? new HttpsServer({ key, cert }, app.nodeHandler) : new HttpServer(app.nodeHandler)

const port = (destr(process.env.NUXT_PORT || process.env.PORT) || 3000) as number
const hostname = process.env.NUXT_HOST || process.env.HOST || 'localhost'
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/entries/vercel.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import '#polyfill'
import { handle } from '..'
import { app } from '..'

export default handle
export default app.nodeHandler
12 changes: 6 additions & 6 deletions src/runtime/error.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// import ansiHTML from 'ansi-html'
import type { IncomingMessage, ServerResponse } from 'http'
import type { CompatibilityEvent } from 'h3'
const cwd = process.cwd()

// const hasReqHeader = (req, header, includes) => req.headers[header] && req.headers[header].toLowerCase().includes(includes)

const isDev = process.env.NODE_ENV === 'development'

export function handleError (error, _req: IncomingMessage, res: ServerResponse) {
export function handleError (error, event: CompatibilityEvent) {
// const isJsonRequest = hasReqHeader(req, 'accept', 'application/json') || hasReqHeader(req, 'user-agent', 'curl/') || hasReqHeader(req, 'user-agent', 'httpie/')

const stack = (error.stack || '')
Expand Down Expand Up @@ -40,8 +40,8 @@ export function handleError (error, _req: IncomingMessage, res: ServerResponse)
: ''
}

res.statusCode = error.statusCode || 500
res.statusMessage = error.statusMessage || 'Internal Server Error'
event.res.statusCode = error.statusCode || 500
event.res.statusMessage = error.statusMessage || 'Internal Server Error'

// Console output
if (!is404) {
Expand All @@ -50,8 +50,8 @@ export function handleError (error, _req: IncomingMessage, res: ServerResponse)

// JSON response
// if (isJsonRequest) {
res.setHeader('Content-Type', 'application/json')
return res.end(JSON.stringify(errorObject))
event.res.setHeader('Content-Type', 'application/json')
return event.res.end(JSON.stringify(errorObject))
// }

// HTML response
Expand Down
10 changes: 5 additions & 5 deletions src/runtime/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import './config'
import { createApp, useBase } from 'h3'
import { createApp } from 'h3'
import { createFetch, Headers } from 'ohmyfetch'
import destr from 'destr'
import { createCall, createFetch as createLocalFetch } from 'unenv/runtime/fetch/index'
Expand All @@ -8,17 +8,17 @@ import { handleError } from './error'
// @ts-ignore
import serverMiddleware from '#server-middleware'

const app = createApp({
export const app = createApp({
debug: destr(process.env.DEBUG),
onError: handleError
})

app.use(timingMiddleware)
app.use(serverMiddleware)

export const stack = app.stack
export const handle = useBase(process.env.ROUTER_BASE, app)
export const localCall = createCall(handle)
app.use('/test', () => 'OK')

export const localCall = createCall(app.nodeHandler as any)
export const localFetch = createLocalFetch(localCall, globalThis.fetch)

export const $fetch = createFetch({ fetch: localFetch, Headers })
Expand Down
34 changes: 18 additions & 16 deletions src/runtime/static.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createError } from 'h3'
import { createError, defineEventHandler } from 'h3'
import { withoutTrailingSlash, withLeadingSlash, parseURL } from 'ufo'
// @ts-ignore
import { getAsset, readAsset } from '#static'
Expand All @@ -9,12 +9,12 @@ const METHODS = ['HEAD', 'GET']
const TWO_DAYS = 2 * 60 * 60 * 24
const STATIC_ASSETS_BASE = process.env.NUXT_STATIC_BASE + '/' + process.env.NUXT_STATIC_VERSION

export default async function serveStatic (req, res) {
if (!METHODS.includes(req.method)) {
export default defineEventHandler(async (event) => {
if (!METHODS.includes(event.req.method)) {
return
}

let id = decodeURIComponent(withLeadingSlash(withoutTrailingSlash(parseURL(req.url).pathname)))
let id = decodeURIComponent(withLeadingSlash(withoutTrailingSlash(parseURL(event.req.url).pathname)))
let asset

for (const _id of [id, id + '/index.html']) {
Expand All @@ -38,36 +38,38 @@ export default async function serveStatic (req, res) {
return
}

const ifNotMatch = req.headers['if-none-match'] === asset.etag
const ifNotMatch = event.req.headers['if-none-match'] === asset.etag
if (ifNotMatch) {
res.statusCode = 304
return res.end('Not Modified (etag)')
event.res.statusCode = 304
event.res.end('Not Modified (etag)')
return
}

const ifModifiedSinceH = req.headers['if-modified-since']
const ifModifiedSinceH = event.req.headers['if-modified-since']
if (ifModifiedSinceH && asset.mtime) {
if (new Date(ifModifiedSinceH) >= new Date(asset.mtime)) {
res.statusCode = 304
return res.end('Not Modified (mtime)')
event.res.statusCode = 304
event.res.end('Not Modified (mtime)')
return
}
}

if (asset.type) {
res.setHeader('Content-Type', asset.type)
event.res.setHeader('Content-Type', asset.type)
}

if (asset.etag) {
res.setHeader('ETag', asset.etag)
event.res.setHeader('ETag', asset.etag)
}

if (asset.mtime) {
res.setHeader('Last-Modified', asset.mtime)
event.res.setHeader('Last-Modified', asset.mtime)
}

if (isBuildAsset) {
res.setHeader('Cache-Control', `max-age=${TWO_DAYS}, immutable`)
event.res.setHeader('Cache-Control', `max-age=${TWO_DAYS}, immutable`)
}

const contents = await readAsset(id)
return res.end(contents)
}
event.res.end(contents)
})
31 changes: 18 additions & 13 deletions src/runtime/vue/render.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ServerResponse } from 'http'
import { createRenderer } from 'vue-bundle-renderer'
import { defineEventHandler } from 'h3'
import devalue from '@nuxt/devalue'
import { privateConfig, publicConfig } from '../config'
import { buildAssetsURL } from '../paths'
Expand Down Expand Up @@ -67,8 +67,8 @@ function renderToString (ssrContext) {
return getRenderer().then(renderToString => renderToString(ssrContext))
}

export default async function renderMiddleware (req, res: ServerResponse) {
let url = req.url
export default defineEventHandler(async (event) => {
let url = event.req.url

// payload.json request detection
let isPayloadReq = false
Expand All @@ -80,11 +80,16 @@ export default async function renderMiddleware (req, res: ServerResponse) {
// Initialize ssr context
const ssrContext = {
url,
req,
res,
event,
req: event.req,
res: event.res,
runtimeConfig: { private: privateConfig, public: publicConfig },
noSSR: req.spa || req.headers['x-nuxt-no-ssr'],
...(req.context || {})
noSSR: event.req.headers['x-nuxt-no-ssr'],

error: undefined,
redirected: undefined,
nuxt: undefined, /* NuxtApp */
payload: undefined
}

// Render app
Expand All @@ -95,7 +100,7 @@ export default async function renderMiddleware (req, res: ServerResponse) {
throw ssrContext.error
}

if (ssrContext.redirected || res.writableEnded) {
if (ssrContext.redirected || event.res.writableEnded) {
return
}

Expand All @@ -113,16 +118,16 @@ export default async function renderMiddleware (req, res: ServerResponse) {
let data
if (isPayloadReq) {
data = renderPayload(payload, url)
res.setHeader('Content-Type', 'text/javascript;charset=UTF-8')
event.res.setHeader('Content-Type', 'text/javascript;charset=UTF-8')
} else {
data = await renderHTML(payload, rendered, ssrContext)
res.setHeader('Content-Type', 'text/html;charset=UTF-8')
event.res.setHeader('Content-Type', 'text/html;charset=UTF-8')
}

const error = ssrContext.nuxt && ssrContext.nuxt.error
res.statusCode = error ? error.statusCode : 200
res.end(data, 'utf-8')
}
event.res.statusCode = error ? error.statusCode : 200
event.res.end(data, 'utf-8')
})

async function renderHTML (payload, rendered, ssrContext) {
const state = `<script>window.__NUXT__=${devalue(payload)}</script>`
Expand Down
Loading

0 comments on commit c705dd4

Please sign in to comment.