Skip to content
This repository has been archived by the owner on Apr 6, 2023. It is now read-only.

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

Merged
merged 6 commits into from
Aug 11, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions packages/nuxt/src/app/nuxt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')

Expand All @@ -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
Expand Down
70 changes: 32 additions & 38 deletions packages/nuxt/src/core/runtime/nitro/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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) {
Expand All @@ -137,42 +135,38 @@ export default defineRenderHandler(async (event) => {
const renderedMeta = await ssrContext.renderMeta?.() ?? {}

// Create render context
const rendered: NuxtRenderContext = {
ssrContext,
pi0 marked this conversation as resolved.
Show resolved Hide resolved
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: {
Expand Down Expand Up @@ -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>`
}
8 changes: 8 additions & 0 deletions test/fixtures/basic/nuxt.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// This file is auto generated by `nuxt prepare`
// Please do not manually modify this file.

/// <reference path=".nuxt-node/nitro.d.ts" />
/// <reference types="nuxt3" />
/// <reference path=".nuxt-node/components.d.ts" />
/// <reference path=".nuxt-node/auto-imports.d.ts" />
export {}
pi0 marked this conversation as resolved.
Show resolved Hide resolved