mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 21:55:11 +00:00
refactor: move runtime to src (typescript)
This commit is contained in:
parent
19e6542d27
commit
b07a4a5c8d
@ -106,7 +106,7 @@ export function getsigmaContext (nuxtOptions: NuxtOptions, input: SigmaInput): S
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
_internal: {
|
_internal: {
|
||||||
runtimeDir: resolve(__dirname, '../runtime'),
|
runtimeDir: resolve(__dirname, './runtime'),
|
||||||
hooks: new Hookable()
|
hooks: new Hookable()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
19
packages/nitro/src/runtime/app/config.ts
Normal file
19
packages/nitro/src/runtime/app/config.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import destr from 'destr'
|
||||||
|
|
||||||
|
const runtimeConfig = process.env.RUNTIME_CONFIG
|
||||||
|
|
||||||
|
for (const type of ['private', 'public']) {
|
||||||
|
for (const key in runtimeConfig[type]) {
|
||||||
|
runtimeConfig[type][key] = destr(process.env[key] || runtimeConfig[type][key])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const $config = global.$config = {
|
||||||
|
...runtimeConfig.public,
|
||||||
|
...runtimeConfig.private
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
public: runtimeConfig.public,
|
||||||
|
private: $config
|
||||||
|
}
|
71
packages/nitro/src/runtime/app/render.ts
Normal file
71
packages/nitro/src/runtime/app/render.ts
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import { createRenderer } from 'vue-bundle-renderer'
|
||||||
|
import devalue from '@nuxt/devalue'
|
||||||
|
import config from './config'
|
||||||
|
import { renderToString } from '~renderer'
|
||||||
|
import server from '~build/dist/server/server'
|
||||||
|
import clientManifest from '~build/dist/server/client.manifest.json'
|
||||||
|
import htmlTemplate from '~build/views/document.template.js'
|
||||||
|
|
||||||
|
const renderer = createRenderer(server, {
|
||||||
|
clientManifest,
|
||||||
|
renderToString
|
||||||
|
})
|
||||||
|
|
||||||
|
const STATIC_ASSETS_BASE = process.env.NUXT_STATIC_BASE + '/' + process.env.NUXT_STATIC_VERSION
|
||||||
|
const PAYLOAD_JS = '/payload.js'
|
||||||
|
|
||||||
|
export async function renderMiddleware (req, res) {
|
||||||
|
let url = req.url
|
||||||
|
|
||||||
|
// payload.json request detection
|
||||||
|
let isPayloadReq = false
|
||||||
|
if (url.startsWith(STATIC_ASSETS_BASE) && url.endsWith(PAYLOAD_JS)) {
|
||||||
|
isPayloadReq = true
|
||||||
|
url = url.substr(STATIC_ASSETS_BASE.length, url.length - STATIC_ASSETS_BASE.length - PAYLOAD_JS.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ssrContext = {
|
||||||
|
url,
|
||||||
|
runtimeConfig: {
|
||||||
|
public: config.public,
|
||||||
|
private: config.private
|
||||||
|
},
|
||||||
|
...(req.context || {})
|
||||||
|
}
|
||||||
|
const rendered = await renderer.renderToString(ssrContext)
|
||||||
|
const payload = ssrContext.nuxt /* nuxt 2 */ || ssrContext.payload /* nuxt 3 */
|
||||||
|
|
||||||
|
if (process.env.NUXT_FULL_STATIC) {
|
||||||
|
payload.staticAssetsBase = STATIC_ASSETS_BASE
|
||||||
|
}
|
||||||
|
|
||||||
|
let data
|
||||||
|
if (isPayloadReq) {
|
||||||
|
data = renderPayload(payload, url)
|
||||||
|
res.setHeader('Content-Type', 'text/javascript;charset=UTF-8')
|
||||||
|
} else {
|
||||||
|
data = renderHTML(payload, rendered, ssrContext)
|
||||||
|
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')
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderHTML (payload, rendered, ssrContext) {
|
||||||
|
const state = `<script>window.__NUXT__=${devalue(payload)}</script>`
|
||||||
|
const _html = rendered.html
|
||||||
|
|
||||||
|
return htmlTemplate({
|
||||||
|
HTML_ATTRS: '',
|
||||||
|
HEAD_ATTRS: '',
|
||||||
|
BODY_ATTRS: '',
|
||||||
|
HEAD: rendered.renderResourceHints() + rendered.renderStyles() + (ssrContext.styles || ''),
|
||||||
|
APP: _html + state + rendered.renderScripts()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderPayload (payload, url) {
|
||||||
|
return `__NUXT_JSONP__("${url}", ${devalue(payload)})`
|
||||||
|
}
|
7
packages/nitro/src/runtime/app/sigma.client.js
Normal file
7
packages/nitro/src/runtime/app/sigma.client.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { $fetch } from 'ohmyfetch'
|
||||||
|
|
||||||
|
global.process = global.process || {};
|
||||||
|
|
||||||
|
(function () { const o = Date.now(); const t = () => Date.now() - o; global.process.hrtime = global.process.hrtime || ((o) => { const e = Math.floor(0.001 * (Date.now() - t())); const a = 0.001 * t(); let l = Math.floor(a) + e; let n = Math.floor(a % 1 * 1e9); return o && (l -= o[0], n -= o[1], n < 0 && (l--, n += 1e9)), [l, n] }) })()
|
||||||
|
|
||||||
|
global.$fetch = $fetch
|
12
packages/nitro/src/runtime/app/vue2.basic.ts
Normal file
12
packages/nitro/src/runtime/app/vue2.basic.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import _renderToString from 'vue-server-renderer/basic'
|
||||||
|
|
||||||
|
export function renderToString (component, context) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
_renderToString(component, context, (err, result) => {
|
||||||
|
if (err) {
|
||||||
|
return reject(err)
|
||||||
|
}
|
||||||
|
return resolve(result)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
14
packages/nitro/src/runtime/app/vue2.ts
Normal file
14
packages/nitro/src/runtime/app/vue2.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { createRenderer } from '~vueServerRenderer'
|
||||||
|
|
||||||
|
const _renderer = createRenderer({})
|
||||||
|
|
||||||
|
export function renderToString (component, context) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
_renderer.renderToString(component, context, (err, result) => {
|
||||||
|
if (err) {
|
||||||
|
return reject(err)
|
||||||
|
}
|
||||||
|
return resolve(result)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
1
packages/nitro/src/runtime/app/vue3.ts
Normal file
1
packages/nitro/src/runtime/app/vue3.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { renderToString } from '@vue/server-renderer'
|
19
packages/nitro/src/runtime/entries/azure.ts
Normal file
19
packages/nitro/src/runtime/entries/azure.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import '~polyfill'
|
||||||
|
import { localCall } from '../server'
|
||||||
|
|
||||||
|
export default async function handle (context, req) {
|
||||||
|
const url = '/' + (req.params.url || '')
|
||||||
|
|
||||||
|
const { body, status, statusText, headers } = await localCall({
|
||||||
|
url,
|
||||||
|
headers: req.headers,
|
||||||
|
method: req.method,
|
||||||
|
body: req.body
|
||||||
|
})
|
||||||
|
|
||||||
|
context.res = {
|
||||||
|
status,
|
||||||
|
headers,
|
||||||
|
body: body ? body.toString() : statusText
|
||||||
|
}
|
||||||
|
}
|
23
packages/nitro/src/runtime/entries/cli.ts
Normal file
23
packages/nitro/src/runtime/entries/cli.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import '~polyfill'
|
||||||
|
import { localCall } from '../server/call'
|
||||||
|
|
||||||
|
async function cli () {
|
||||||
|
const url = process.argv[2] || '/'
|
||||||
|
const debug = (label, ...args) => console.debug(`> ${label}:`, ...args)
|
||||||
|
const r = await localCall({ url })
|
||||||
|
|
||||||
|
debug('URL', url)
|
||||||
|
debug('StatusCode', r.status)
|
||||||
|
debug('StatusMessage', r.statusText)
|
||||||
|
for (const header of r.headers.entries()) {
|
||||||
|
debug(header[0], header[1])
|
||||||
|
}
|
||||||
|
console.log('\n', r.body.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
cli().catch((err) => {
|
||||||
|
console.error(err)
|
||||||
|
process.exit(1)
|
||||||
|
})
|
||||||
|
}
|
46
packages/nitro/src/runtime/entries/cloudflare.ts
Normal file
46
packages/nitro/src/runtime/entries/cloudflare.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import '~polyfill'
|
||||||
|
import { getAssetFromKV } from '@cloudflare/kv-asset-handler'
|
||||||
|
import { localCall } from '../server'
|
||||||
|
|
||||||
|
const PUBLIC_PATH = process.env.PUBLIC_PATH // Default: /_nuxt/
|
||||||
|
|
||||||
|
addEventListener('fetch', (event) => {
|
||||||
|
event.respondWith(handleEvent(event))
|
||||||
|
})
|
||||||
|
|
||||||
|
async function handleEvent (event) {
|
||||||
|
try {
|
||||||
|
return await getAssetFromKV(event, { cacheControl: assetsCacheControl })
|
||||||
|
} catch (_err) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = new URL(event.request.url)
|
||||||
|
|
||||||
|
const r = await localCall({
|
||||||
|
event,
|
||||||
|
url: url.pathname,
|
||||||
|
host: url.hostname,
|
||||||
|
protocol: url.protocol,
|
||||||
|
headers: event.request.headers,
|
||||||
|
method: event.request.method,
|
||||||
|
redirect: event.request.redirect,
|
||||||
|
body: event.request.body
|
||||||
|
})
|
||||||
|
|
||||||
|
return new Response(r.body, {
|
||||||
|
headers: r.headers,
|
||||||
|
status: r.status,
|
||||||
|
statusText: r.statusText
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function assetsCacheControl (request) {
|
||||||
|
if (request.url.includes(PUBLIC_PATH) /* TODO: Check with routerBase */) {
|
||||||
|
return {
|
||||||
|
browserTTL: 31536000,
|
||||||
|
edgeTTL: 31536000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {}
|
||||||
|
}
|
20
packages/nitro/src/runtime/entries/lambda.ts
Normal file
20
packages/nitro/src/runtime/entries/lambda.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import '~polyfill'
|
||||||
|
import { localCall } from '../server'
|
||||||
|
|
||||||
|
export async function handler (event, context) {
|
||||||
|
const r = await localCall({
|
||||||
|
event,
|
||||||
|
url: event.path,
|
||||||
|
context,
|
||||||
|
headers: event.headers,
|
||||||
|
method: event.httpMethod,
|
||||||
|
query: event.queryStringParameters,
|
||||||
|
body: event.body // TODO: handle event.isBase64Encoded
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: r.status,
|
||||||
|
headers: r.headers,
|
||||||
|
body: r.body.toString()
|
||||||
|
}
|
||||||
|
}
|
14
packages/nitro/src/runtime/entries/local.ts
Normal file
14
packages/nitro/src/runtime/entries/local.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import '~polyfill'
|
||||||
|
import { Server } from 'http'
|
||||||
|
import { parentPort } from 'worker_threads'
|
||||||
|
import type { AddressInfo } from 'net'
|
||||||
|
import { handle } from '../server'
|
||||||
|
|
||||||
|
const server = new Server(handle)
|
||||||
|
|
||||||
|
const netServer = server.listen(0, () => {
|
||||||
|
parentPort.postMessage({
|
||||||
|
event: 'listen',
|
||||||
|
port: (netServer.address() as AddressInfo).port
|
||||||
|
})
|
||||||
|
})
|
2
packages/nitro/src/runtime/entries/node.ts
Normal file
2
packages/nitro/src/runtime/entries/node.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import '~polyfill'
|
||||||
|
export * from '../server'
|
18
packages/nitro/src/runtime/entries/server.ts
Normal file
18
packages/nitro/src/runtime/entries/server.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import '~polyfill'
|
||||||
|
import { Server } from 'http'
|
||||||
|
import { handle } from '../server'
|
||||||
|
|
||||||
|
const server = new Server(handle)
|
||||||
|
|
||||||
|
const port = process.env.NUXT_PORT || process.env.PORT || 3000
|
||||||
|
const host = process.env.NUXT_HOST || process.env.HOST || 'localhost'
|
||||||
|
|
||||||
|
server.listen(port, host, (err) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(err)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
console.log(`Listening on http://${host}:${port}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default {}
|
39
packages/nitro/src/runtime/entries/service-worker.ts
Normal file
39
packages/nitro/src/runtime/entries/service-worker.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import '~polyfill'
|
||||||
|
import { localCall } from '../server'
|
||||||
|
|
||||||
|
addEventListener('fetch', (event: any) => {
|
||||||
|
const url = new URL(event.request.url)
|
||||||
|
|
||||||
|
if (url.pathname.includes('.') /* is file */) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
event.respondWith(handleEvent(url, event))
|
||||||
|
})
|
||||||
|
|
||||||
|
async function handleEvent (url, event) {
|
||||||
|
const r = await localCall({
|
||||||
|
event,
|
||||||
|
url: url.pathname,
|
||||||
|
host: url.hostname,
|
||||||
|
protocol: url.protocol,
|
||||||
|
headers: event.request.headers,
|
||||||
|
method: event.request.method,
|
||||||
|
redirect: event.request.redirect,
|
||||||
|
body: event.request.body
|
||||||
|
})
|
||||||
|
|
||||||
|
return new Response(r.body, {
|
||||||
|
headers: r.headers,
|
||||||
|
status: r.status,
|
||||||
|
statusText: r.statusText
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
self.addEventListener('install', () => {
|
||||||
|
self.skipWaiting()
|
||||||
|
})
|
||||||
|
|
||||||
|
self.addEventListener('activate', (event) => {
|
||||||
|
event.waitUntil(self.clients.claim())
|
||||||
|
})
|
4
packages/nitro/src/runtime/entries/vercel.ts
Normal file
4
packages/nitro/src/runtime/entries/vercel.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import '~polyfill'
|
||||||
|
import { handle } from '../server'
|
||||||
|
|
||||||
|
export default handle
|
67
packages/nitro/src/runtime/server/error.ts
Normal file
67
packages/nitro/src/runtime/server/error.ts
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// import ansiHTML from 'ansi-html'
|
||||||
|
const cwd = process.cwd()
|
||||||
|
|
||||||
|
// TODO: Handle process.env.DEBUG
|
||||||
|
export function handleError (error, req, res) {
|
||||||
|
const stack = (error.stack || '')
|
||||||
|
.split('\n')
|
||||||
|
.splice(1)
|
||||||
|
.filter(line => line.includes('at '))
|
||||||
|
.map((line) => {
|
||||||
|
const text = line
|
||||||
|
.replace(cwd + '/', './')
|
||||||
|
.replace('webpack:/', '')
|
||||||
|
.replace('.vue', '.js') // TODO: Support sourcemap
|
||||||
|
.trim()
|
||||||
|
return {
|
||||||
|
text,
|
||||||
|
internal: (line.includes('node_modules') && !line.includes('.cache')) ||
|
||||||
|
line.includes('internal') ||
|
||||||
|
line.includes('new Promise')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
console.error(error.message + '\n' + stack.map(l => ' ' + l.text).join(' \n'))
|
||||||
|
|
||||||
|
const html = `
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Nuxt Error</title>
|
||||||
|
<style>
|
||||||
|
html, body {
|
||||||
|
background: white;
|
||||||
|
color: red;
|
||||||
|
font-family: monospace;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.stack {
|
||||||
|
padding-left: 2em;
|
||||||
|
}
|
||||||
|
.stack.internal {
|
||||||
|
color: grey;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div>${req.method} ${req.url}</div><br>
|
||||||
|
<h1>${error.toString()}</h1>
|
||||||
|
<pre>${stack.map(i =>
|
||||||
|
`<span class="stack${i.internal ? ' internal' : ''}">${i.text}</span>`
|
||||||
|
).join('\n')
|
||||||
|
}</pre>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`
|
||||||
|
|
||||||
|
res.statusCode = error.statusCode || 500
|
||||||
|
res.statusMessage = error.statusMessage || 'Invernal Error'
|
||||||
|
res.end(html)
|
||||||
|
}
|
23
packages/nitro/src/runtime/server/index.ts
Normal file
23
packages/nitro/src/runtime/server/index.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import '../app/config'
|
||||||
|
import { createApp, useBase } from 'h3'
|
||||||
|
import { createFetch } from 'ohmyfetch'
|
||||||
|
import destr from 'destr'
|
||||||
|
import { createCall, createFetch as createLocalFetch } from '@nuxt/un/runtime/fetch'
|
||||||
|
import { timingMiddleware } from './timing'
|
||||||
|
import { handleError } from './error'
|
||||||
|
import serverMiddleware from '~serverMiddleware'
|
||||||
|
|
||||||
|
const app = createApp({
|
||||||
|
debug: destr(process.env.DEBUG),
|
||||||
|
onError: handleError
|
||||||
|
})
|
||||||
|
|
||||||
|
app.use(timingMiddleware)
|
||||||
|
app.use(serverMiddleware)
|
||||||
|
app.use(() => import('../app/render').then(e => e.renderMiddleware), { lazy: true })
|
||||||
|
|
||||||
|
export const stack = app.stack
|
||||||
|
export const handle = useBase(process.env.ROUTER_BASE, app)
|
||||||
|
export const localCall = createCall(handle)
|
||||||
|
export const localFetch = createLocalFetch(localCall, global.fetch)
|
||||||
|
export const $fetch = global.$fetch = createFetch({ fetch: localFetch })
|
63
packages/nitro/src/runtime/server/static.ts
Normal file
63
packages/nitro/src/runtime/server/static.ts
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import { sendError } from 'h3'
|
||||||
|
import { getAsset, readAsset } from '~static'
|
||||||
|
|
||||||
|
const METHODS = ['HEAD', 'GET']
|
||||||
|
const PUBLIC_PATH = process.env.PUBLIC_PATH // Default: /_nuxt/
|
||||||
|
const TWO_DAYS = 2 * 60 * 60 * 24
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
export default async function serveStatic(req, res) {
|
||||||
|
if (!METHODS.includes(req.method)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let id = req.url.split('?')[0]
|
||||||
|
if (id.startsWith('/')) {
|
||||||
|
id = id.substr(1)
|
||||||
|
}
|
||||||
|
if (id.endsWith('/')) {
|
||||||
|
id = id.substr(0, id.length - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const asset = getAsset(id) || getAsset(id = id + '/index.html')
|
||||||
|
|
||||||
|
if (!asset) {
|
||||||
|
if (id.startsWith(PUBLIC_PATH)) {
|
||||||
|
sendError(res, 'Asset not found: ' + id, false, 404)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const ifNotMatch = req.headers['if-none-match'] === asset.etag
|
||||||
|
if (ifNotMatch) {
|
||||||
|
res.statusCode = 304
|
||||||
|
return res.end('Not Modified (etag)')
|
||||||
|
}
|
||||||
|
|
||||||
|
const ifModifiedSinceH = 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)')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (asset.type) {
|
||||||
|
res.setHeader('Content-Type', asset.type)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (asset.etag) {
|
||||||
|
res.setHeader('ETag', asset.etag)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (asset.mtime) {
|
||||||
|
res.setHeader('Last-Modified', asset.mtime)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id.startsWith(PUBLIC_PATH)) {
|
||||||
|
res.setHeader('Cache-Control', `max-age=${TWO_DAYS}, immutable`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const contents = await readAsset(id)
|
||||||
|
return res.end(contents)
|
||||||
|
}
|
22
packages/nitro/src/runtime/server/timing.ts
Normal file
22
packages/nitro/src/runtime/server/timing.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
export const globalTiming = global.__timing__ || {
|
||||||
|
start: () => 0,
|
||||||
|
end: () => 0,
|
||||||
|
metrics: []
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Server-Timing
|
||||||
|
export function timingMiddleware (_req, res, next) {
|
||||||
|
const start = globalTiming.start()
|
||||||
|
|
||||||
|
const _end = res.end
|
||||||
|
res.end = (data, encoding, callback) => {
|
||||||
|
const metrics = [['Generate', globalTiming.end(start)], ...globalTiming.metrics]
|
||||||
|
const serverTiming = metrics.map(m => `-;dur=${m[1]};desc="${encodeURIComponent(m[0])}"`).join(', ')
|
||||||
|
if (!res.headersSent) {
|
||||||
|
res.setHeader('Server-Timing', serverTiming)
|
||||||
|
}
|
||||||
|
_end.call(res, data, encoding, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
next()
|
||||||
|
}
|
6
packages/nitro/src/runtime/types.d.ts
vendored
Normal file
6
packages/nitro/src/runtime/types.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
declare module NodeJS {
|
||||||
|
interface Global {
|
||||||
|
__timing__: any
|
||||||
|
$config: any
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user