mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-25 23:22:02 +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: {
|
||||
runtimeDir: resolve(__dirname, '../runtime'),
|
||||
runtimeDir: resolve(__dirname, './runtime'),
|
||||
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