Skip to content

Commit

Permalink
refactor: cleanup renderer entry
Browse files Browse the repository at this point in the history
  • Loading branch information
danielroe committed May 31, 2022
1 parent 0c58661 commit 0e4b0b0
Showing 1 changed file with 75 additions and 62 deletions.
137 changes: 75 additions & 62 deletions src/runtime/nitro/renderer.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,75 @@
import { createRenderer } from 'vue-bundle-renderer'
import { eventHandler, useQuery } from 'h3'
import type { SSRContext } from 'vue-bundle-renderer'
import { CompatibilityEvent, eventHandler, useQuery } from 'h3'
import devalue from '@nuxt/devalue'
import { RuntimeConfig } from '@nuxt/schema'
// @ts-ignore
import { useRuntimeConfig } from '#internal/nitro'
// @ts-ignore
import { buildAssetsURL } from '#paths'
// @ts-ignore
import htmlTemplate from '#build/views/document.template.mjs'
// @ts-ignore
import { renderToString } from '#vue-renderer'

const STATIC_ASSETS_BASE = process.env.NUXT_STATIC_BASE + '/' + process.env.NUXT_STATIC_VERSION
const NUXT_NO_SSR = process.env.NUXT_NO_SSR
const PAYLOAD_JS = '/payload.js'

interface NuxtSSRContext extends SSRContext {
url: string
noSSR: boolean
redirected?: boolean
event: CompatibilityEvent
req: CompatibilityEvent['req']
res: CompatibilityEvent['res']
runtimeConfig: RuntimeConfig
error?: any
nuxt?: any
payload?: any
renderMeta?: () => Promise<any>
}

interface RenderResult {
html: any
renderResourceHints: () => string
renderStyles: () => string
renderScripts: () => string
meta?: Partial<{
htmlAttrs?: string,
bodyAttrs: string,
headAttrs: string,
headTags: string,
bodyScriptsPrepend : string,
bodyScripts : string
}>
}

// @ts-ignore
const getClientManifest = cachedImport(() => import('#build/dist/server/client.manifest.mjs'))
const getClientManifest = () => import('#build/dist/server/client.manifest.mjs').then(r => r.default || r)

// @ts-ignore
const getSSRApp = !process.env.NUXT_NO_SSR ? cachedImport(() => import('#build/dist/server/server.mjs')) : undefined
const getServerEntry = () => process.env.NUXT_NO_SSR ? Promise.resolve(null) : import('#build/dist/server/server.mjs').then(r => r.default || r)

const getSSRRenderer = cachedResult(async () => {
const getSSRRenderer = lazyCachedFunction(async () => {
// Load client manifest
const clientManifest = await getClientManifest()
if (!clientManifest) { throw new Error('client.manifest is not available') }

// Load server bundle
const createSSRApp = await getSSRApp?.()
const createSSRApp = await getServerEntry()
if (!createSSRApp) { throw new Error('Server bundle is not available') }
// Create renderer
// @ts-ignore
const { renderToString } = await import('#vue-renderer') // Alias to vue2.ts or vue3.ts
return createRenderer((createSSRApp), { clientManifest, renderToString, publicPath: buildAssetsURL() }).renderToString

return createRenderer(createSSRApp, {
clientManifest,
renderToString,
publicPath: buildAssetsURL()
})
})

const getSPARenderer = cachedResult(async () => {
// -- SPA Renderer --
const getSPARenderer = lazyCachedFunction(async () => {
const clientManifest = await getClientManifest()
return (ssrContext) => {
const renderToString = (ssrContext: NuxtSSRContext) => {
const config = useRuntimeConfig()
ssrContext.nuxt = {
serverRendered: false,
Expand All @@ -42,16 +79,14 @@ const getSPARenderer = cachedResult(async () => {
}
}

let entryFiles = Object.values(clientManifest).filter(
(fileValue: any) => fileValue.isEntry
)
let entryFiles = Object.values(clientManifest).filter((fileValue: any) => fileValue.isEntry)
if ('all' in clientManifest && 'initial' in clientManifest) {
// Upgrade legacy manifest (also see normalizeClientManifest in vue-bundle-renderer)
// https://github.com/nuxt-contrib/vue-bundle-renderer/issues/12
entryFiles = clientManifest.initial.map(file => ({ file }))
}

return {
return Promise.resolve({
html: '<div id="__nuxt"></div>',
renderResourceHints: () => '',
renderStyles: () =>
Expand All @@ -67,14 +102,11 @@ const getSPARenderer = cachedResult(async () => {
return `<script ${isMJS ? 'type="module"' : ''} src="${buildAssetsURL(file)}"></script>`
})
.join('')
}
})
}
})

function renderToString (ssrContext) {
const getRenderer = (NUXT_NO_SSR || ssrContext.noSSR) ? getSPARenderer : getSSRRenderer
return getRenderer().then(renderToString => renderToString(ssrContext))
}
return { renderToString }
})

export default eventHandler(async (event) => {
// Whether we're rendering an error page
Expand All @@ -90,24 +122,24 @@ export default eventHandler(async (event) => {

// Initialize ssr context
const config = useRuntimeConfig()
const ssrContext = {
const ssrContext: NuxtSSRContext = {
url,
event,
req: event.req,
res: event.res,
runtimeConfig: { private: config, public: { public: config.public, app: config.app } },
noSSR: event.req.headers['x-nuxt-no-ssr'],

noSSR: !!event.req.headers['x-nuxt-no-ssr'],
error: ssrError,
redirected: undefined,
nuxt: undefined as undefined | Record<string, any>, /* NuxtApp */
payload: undefined
}

// Render app
const rendered = await renderToString(ssrContext).catch((e) => {
const renderer = (process.env.NUXT_NO_SSR || ssrContext.noSSR) ? await getSPARenderer() : await getSSRRenderer()
const rendered = await renderer.renderToString(ssrContext).catch((e) => {
if (!ssrError) { throw e }
})
}) as RenderResult

// If we error on rendering error page, we bail out and directly return to the error handler
if (!rendered) { return }
Expand All @@ -116,10 +148,9 @@ export default eventHandler(async (event) => {
return
}

const error = ssrContext.nuxt?.error
// Handle errors
if (error && !ssrError) {
throw error
if (ssrContext.nuxt?.error && !ssrError) {
throw ssrContext.nuxt.error
}

if (ssrContext.nuxt?.hooks) {
Expand All @@ -132,59 +163,41 @@ export default eventHandler(async (event) => {
ssrContext.nuxt.staticAssetsBase = STATIC_ASSETS_BASE
}

let data
if (isPayloadReq) {
data = renderPayload(ssrContext.nuxt, url)
const data = renderPayload(ssrContext.nuxt, url)
event.res.setHeader('Content-Type', 'text/javascript;charset=UTF-8')
return data
} else {
data = await renderHTML(ssrContext.nuxt, rendered, ssrContext)
const data = await renderHTML(ssrContext.nuxt, rendered, ssrContext)
event.res.setHeader('Content-Type', 'text/html;charset=UTF-8')
return data
}

event.res.end(data, 'utf-8')
})

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

if ('renderMeta' in ssrContext) {
rendered.meta = await ssrContext.renderMeta()
rendered.meta = rendered.meta || {}
if (ssrContext.renderMeta) {
Object.assign(rendered.meta, await ssrContext.renderMeta())
}

const {
htmlAttrs = '',
bodyAttrs = '',
headAttrs = '',
headTags = '',
bodyScriptsPrepend = '',
bodyScripts = ''
} = rendered.meta || {}

return htmlTemplate({
HTML_ATTRS: htmlAttrs,
HEAD_ATTRS: headAttrs,
HEAD: headTags +
HTML_ATTRS: (rendered.meta.htmlAttrs || ''),
HEAD_ATTRS: (rendered.meta.headAttrs || ''),
HEAD: (rendered.meta.headTags || '') +
rendered.renderResourceHints() + rendered.renderStyles() + (ssrContext.styles || ''),
BODY_ATTRS: bodyAttrs,
BODY_PREPEND: ssrContext.teleports?.body || '',
APP: bodyScriptsPrepend + html + state + rendered.renderScripts() + bodyScripts
BODY_ATTRS: (rendered.meta.bodyAttrs || ''),
BODY_PREPEND: (ssrContext.teleports?.body || ''),
APP: (rendered.meta.bodyScriptsPrepend || '') + rendered.html + state + rendered.renderScripts() + (rendered.meta.bodyScripts || '')
})
}

function renderPayload (payload, url) {
return `__NUXT_JSONP__("${url}", ${devalue(payload)})`
}

function _interopDefault (e) {
return e && typeof e === 'object' && 'default' in e ? e.default : e
}

function cachedImport <M> (importer: () => Promise<M>) {
return cachedResult(() => importer().then(_interopDefault)) as () => Promise<M>
}

function cachedResult <T> (fn: () => Promise<T>): () => Promise<T> {
function lazyCachedFunction <T> (fn: () => Promise<T>): () => Promise<T> {
let res: Promise<T> | null = null
return () => {
if (res === null) {
Expand Down

0 comments on commit 0e4b0b0

Please sign in to comment.