fix(nuxt): use payload error state as source of truth (#6389)

This commit is contained in:
Daniel Roe 2022-08-08 15:33:31 +01:00 committed by GitHub
parent 0cbba77011
commit 46f36a4038
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 33 additions and 29 deletions

View File

@ -1,10 +1,8 @@
import { createError as _createError, H3Error } from 'h3' import { createError as _createError, H3Error } from 'h3'
import { useNuxtApp, useState } from '#app' import { toRef } from 'vue'
import { useNuxtApp } from '#app'
export const useError = () => { export const useError = () => toRef(useNuxtApp().payload, 'error')
const nuxtApp = useNuxtApp()
return useState('error', () => process.server ? nuxtApp.ssrContext.error : nuxtApp.payload.error)
}
export interface NuxtError extends H3Error {} export interface NuxtError extends H3Error {}
@ -14,12 +12,8 @@ export const showError = (_err: string | Error | Partial<NuxtError>) => {
try { try {
const nuxtApp = useNuxtApp() const nuxtApp = useNuxtApp()
nuxtApp.callHook('app:error', err) nuxtApp.callHook('app:error', err)
if (process.server) {
nuxtApp.ssrContext.error = nuxtApp.ssrContext.error || err
} else {
const error = useError() const error = useError()
error.value = error.value || err error.value = error.value || err
}
} catch { } catch {
throw err throw err
} }

View File

@ -35,7 +35,7 @@ if (process.server) {
await nuxt.hooks.callHook('app:created', vueApp) await nuxt.hooks.callHook('app:created', vueApp)
} catch (err) { } catch (err) {
await nuxt.callHook('app:error', err) await nuxt.callHook('app:error', err)
ssrContext.error = ssrContext.error || err nuxt.payload.error = nuxt.payload.error || err
} }
return vueApp return vueApp

View File

@ -59,7 +59,8 @@ interface _NuxtApp {
res?: CompatibilityEvent['res'] res?: CompatibilityEvent['res']
runtimeConfig: RuntimeConfig runtimeConfig: RuntimeConfig
noSSR: boolean noSSR: boolean
error?: any /** whether we are rendering an SSR error */
error?: boolean
nuxt: _NuxtApp nuxt: _NuxtApp
payload: _NuxtApp['payload'] payload: _NuxtApp['payload']
teleports?: Record<string, string> teleports?: Record<string, string>
@ -70,6 +71,14 @@ interface _NuxtApp {
data?: Record<string, any> data?: Record<string, any>
state?: Record<string, any> state?: Record<string, any>
rendered?: Function rendered?: Function
error?: Error | {
url: string
statusCode: string
statusMessage: string
message: string
description: string
data?: any
}
[key: string]: any [key: string]: any
} }
@ -119,19 +128,19 @@ export function createNuxtApp (options: CreateOptions) {
defineGetter(nuxtApp.vueApp, '$nuxt', nuxtApp) defineGetter(nuxtApp.vueApp, '$nuxt', nuxtApp)
defineGetter(nuxtApp.vueApp.config.globalProperties, '$nuxt', nuxtApp) defineGetter(nuxtApp.vueApp.config.globalProperties, '$nuxt', nuxtApp)
if (process.server) {
// Expose nuxt to the renderContext // Expose nuxt to the renderContext
if (nuxtApp.ssrContext) { if (nuxtApp.ssrContext) {
nuxtApp.ssrContext.nuxt = nuxtApp nuxtApp.ssrContext.nuxt = nuxtApp
} }
if (process.server) {
// Expose to server renderer to create window.__NUXT__ // Expose to server renderer to create window.__NUXT__
nuxtApp.ssrContext = nuxtApp.ssrContext || {} as any nuxtApp.ssrContext = nuxtApp.ssrContext || {} as any
nuxtApp.ssrContext.payload = nuxtApp.payload if (nuxtApp.ssrContext.payload) {
Object.assign(nuxtApp.payload, nuxtApp.ssrContext.payload)
} }
nuxtApp.ssrContext.payload = nuxtApp.payload
// Expose client runtime-config to the payload // Expose client runtime-config to the payload
if (process.server) {
nuxtApp.payload.config = { nuxtApp.payload.config = {
public: options.ssrContext.runtimeConfig.public, public: options.ssrContext.runtimeConfig.public,
app: options.ssrContext.runtimeConfig.app app: options.ssrContext.runtimeConfig.app

View File

@ -2,13 +2,14 @@ import { withQuery } from 'ufo'
import type { NitroErrorHandler } from 'nitropack' import type { NitroErrorHandler } from 'nitropack'
// @ts-ignore TODO // @ts-ignore TODO
import { normalizeError, isJsonRequest } from '#internal/nitro/utils' import { normalizeError, isJsonRequest } from '#internal/nitro/utils'
import { NuxtApp } from '#app'
export default <NitroErrorHandler> async function errorhandler (_error, event) { export default <NitroErrorHandler> async function errorhandler (_error, event) {
// Parse and normalize error // Parse and normalize error
const { stack, statusCode, statusMessage, message } = normalizeError(_error) const { stack, statusCode, statusMessage, message } = normalizeError(_error)
// Create an error object // Create an error object
const errorObject = { const errorObject: Exclude<NuxtApp['payload']['error'], Error> = {
url: event.req.url, url: event.req.url,
statusCode, statusCode,
statusMessage, statusMessage,
@ -20,7 +21,7 @@ export default <NitroErrorHandler> async function errorhandler (_error, event) {
} }
// Set response code and message // Set response code and message
event.res.statusCode = errorObject.statusCode event.res.statusCode = errorObject.statusCode as any as number
event.res.statusMessage = errorObject.statusMessage event.res.statusMessage = errorObject.statusMessage
// Console output // Console output
@ -36,7 +37,7 @@ export default <NitroErrorHandler> async function errorhandler (_error, event) {
} }
// HTML response // HTML response
const url = withQuery('/__nuxt_error', errorObject as any) const url = withQuery('/__nuxt_error', errorObject)
const html = await $fetch(url).catch((error) => { const html = await $fetch(url).catch((error) => {
console.error('[nitro] Error while generating error response', error) console.error('[nitro] Error while generating error response', error)
return errorObject.statusMessage return errorObject.statusMessage

View File

@ -100,7 +100,7 @@ const getSPARenderer = lazyCachedFunction(async () => {
export default eventHandler(async (event) => { export default eventHandler(async (event) => {
// Whether we're rendering an error page // Whether we're rendering an error page
const ssrError = event.req.url?.startsWith('/__nuxt_error') ? useQuery(event) : null const ssrError = event.req.url?.startsWith('/__nuxt_error') ? useQuery(event) as Exclude<NuxtApp['payload']['error'], Error> : null
const url = ssrError?.url as string || event.req.url! const url = ssrError?.url as string || event.req.url!
// Initialize ssr context // Initialize ssr context
@ -111,9 +111,9 @@ export default eventHandler(async (event) => {
res: event.res, res: event.res,
runtimeConfig: useRuntimeConfig(), runtimeConfig: useRuntimeConfig(),
noSSR: !!event.req.headers['x-nuxt-no-ssr'], noSSR: !!event.req.headers['x-nuxt-no-ssr'],
error: ssrError, error: !!ssrError,
nuxt: undefined, /* NuxtApp */ nuxt: undefined, /* NuxtApp */
payload: undefined payload: ssrError ? { error: ssrError } : undefined
} }
// Render app // Render app
@ -126,8 +126,8 @@ export default eventHandler(async (event) => {
if (!_rendered) { if (!_rendered) {
return return
} }
if (ssrContext.error && !ssrError) { if (ssrContext.payload?.error && !ssrError) {
throw ssrContext.error throw ssrContext.payload.error
} }
// Render meta // Render meta