fix(nuxt)!: allow app:rendered to modify ssr context and add render:html (#6521)

This commit is contained in:
pooya parsa 2022-08-11 18:34:39 +02:00 committed by GitHub
parent f6c31b1aaf
commit 24a499d285
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 35 additions and 41 deletions

View File

@ -6,8 +6,6 @@ import type { RuntimeConfig } from '@nuxt/schema'
import { getContext } from 'unctx'
import type { SSRContext } from 'vue-bundle-renderer/runtime'
import type { CompatibilityEvent } from 'h3'
// eslint-disable-next-line import/no-restricted-paths
import type { NuxtRenderContext } from '../core/runtime/nitro/renderer'
const nuxtAppCtx = getContext<NuxtApp>('nuxt-app')
@ -21,11 +19,13 @@ type NuxtMeta = {
}
type HookResult = Promise<void> | void
type AppRenderedContext = { ssrContext: NuxtApp['ssrContext'] }
export interface RuntimeNuxtHooks {
'app:created': (app: App<Element>) => HookResult
'app:beforeMount': (app: App<Element>) => HookResult
'app:mounted': (app: App<Element>) => HookResult
'app:rendered': (ctx: NuxtRenderContext) => HookResult
'app:rendered': (ctx: AppRenderedContext) => HookResult
'app:redirected': () => HookResult
'app:suspense:resolve': (Component?: VNode) => HookResult
'app:error': (err: any) => HookResult

View File

@ -15,16 +15,13 @@ export type NuxtSSRContext = NuxtApp['ssrContext']
const defineRenderHandler = _defineRenderHandler as (h: RenderHandler) => CompatibilityEvent
export interface NuxtRenderContext {
ssrContext: NuxtSSRContext
html: {
htmlAttrs: string[]
head: string[]
bodyAttrs: string[]
bodyPreprend: string[]
body: string[]
bodyAppend: string[]
}
export interface NuxtRenderHTMLContext {
htmlAttrs: string[]
head: string[]
bodyAttrs: string[]
bodyPreprend: string[]
body: string[]
bodyAppend: string[]
}
export interface NuxtRenderResponse {
@ -124,6 +121,7 @@ export default defineRenderHandler(async (event) => {
const _rendered = await renderer.renderToString(ssrContext).catch((err) => {
if (!ssrError) { throw err }
})
await ssrContext.nuxt?.hooks.callHook('app:rendered', { ssrContext })
// Handle errors
if (!_rendered) {
@ -137,42 +135,38 @@ export default defineRenderHandler(async (event) => {
const renderedMeta = await ssrContext.renderMeta?.() ?? {}
// Create render context
const rendered: NuxtRenderContext = {
ssrContext,
html: {
htmlAttrs: normalizeChunks([renderedMeta.htmlAttrs]),
head: normalizeChunks([
renderedMeta.headTags,
_rendered.renderResourceHints(),
_rendered.renderStyles(),
ssrContext.styles
]),
bodyAttrs: normalizeChunks([renderedMeta.bodyAttrs]),
bodyPreprend: normalizeChunks([
renderedMeta.bodyScriptsPrepend,
ssrContext.teleports?.body
]),
body: [
const htmlContext: NuxtRenderHTMLContext = {
htmlAttrs: normalizeChunks([renderedMeta.htmlAttrs]),
head: normalizeChunks([
renderedMeta.headTags,
_rendered.renderResourceHints(),
_rendered.renderStyles(),
ssrContext.styles
]),
bodyAttrs: normalizeChunks([renderedMeta.bodyAttrs]),
bodyPreprend: normalizeChunks([
renderedMeta.bodyScriptsPrepend,
ssrContext.teleports?.body
]),
body: [
// TODO: Rename to _rendered.body in next vue-bundle-renderer
_rendered.html
],
bodyAppend: normalizeChunks([
_rendered.html
],
bodyAppend: normalizeChunks([
`<script>window.__NUXT__=${devalue(ssrContext.payload)}</script>`,
_rendered.renderScripts(),
// Note: bodyScripts may contain tags other than <script>
renderedMeta.bodyScripts
])
}
])
}
// Allow hooking into the rendered result
const nitroApp = useNitroApp()
await ssrContext.nuxt?.hooks.callHook('app:rendered', rendered)
await nitroApp.hooks.callHook('nuxt:app:rendered', rendered)
await nitroApp.hooks.callHook('render:html', htmlContext, { event })
// Construct HTML response
const response: RenderResponse = {
body: renderHTMLDocument(rendered),
body: renderHTMLDocument(htmlContext),
statusCode: event.res.statusCode,
statusMessage: event.res.statusMessage,
headers: {
@ -206,10 +200,10 @@ function joinAttrs (chunks: string[]) {
return chunks.join(' ')
}
function renderHTMLDocument (rendered: NuxtRenderContext) {
function renderHTMLDocument (html: NuxtRenderHTMLContext) {
return `<!DOCTYPE html>
<html ${joinAttrs(rendered.html.htmlAttrs)}>
<head>${joinTags(rendered.html.head)}</head>
<body ${joinAttrs(rendered.html.bodyAttrs)}>${joinTags(rendered.html.bodyPreprend)}${joinTags(rendered.html.body)}${joinTags(rendered.html.bodyAppend)}</body>
<html ${joinAttrs(html.htmlAttrs)}>
<head>${joinTags(html.head)}</head>
<body ${joinAttrs(html.bodyAttrs)}>${joinTags(html.bodyPreprend)}${joinTags(html.body)}${joinTags(html.bodyAppend)}</body>
</html>`
}