From 46f36a403827a2897a3675b7077e67fb02c64b69 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Mon, 8 Aug 2022 15:33:31 +0100 Subject: [PATCH] fix(nuxt): use payload error state as source of truth (#6389) --- packages/nuxt/src/app/composables/error.ts | 16 ++++------- packages/nuxt/src/app/entry.ts | 2 +- packages/nuxt/src/app/nuxt.ts | 27 ++++++++++++------- packages/nuxt/src/core/runtime/nitro/error.ts | 7 ++--- .../nuxt/src/core/runtime/nitro/renderer.ts | 10 +++---- 5 files changed, 33 insertions(+), 29 deletions(-) diff --git a/packages/nuxt/src/app/composables/error.ts b/packages/nuxt/src/app/composables/error.ts index 729ded637d..f1a270e87d 100644 --- a/packages/nuxt/src/app/composables/error.ts +++ b/packages/nuxt/src/app/composables/error.ts @@ -1,10 +1,8 @@ import { createError as _createError, H3Error } from 'h3' -import { useNuxtApp, useState } from '#app' +import { toRef } from 'vue' +import { useNuxtApp } from '#app' -export const useError = () => { - const nuxtApp = useNuxtApp() - return useState('error', () => process.server ? nuxtApp.ssrContext.error : nuxtApp.payload.error) -} +export const useError = () => toRef(useNuxtApp().payload, 'error') export interface NuxtError extends H3Error {} @@ -14,12 +12,8 @@ export const showError = (_err: string | Error | Partial) => { try { const nuxtApp = useNuxtApp() nuxtApp.callHook('app:error', err) - if (process.server) { - nuxtApp.ssrContext.error = nuxtApp.ssrContext.error || err - } else { - const error = useError() - error.value = error.value || err - } + const error = useError() + error.value = error.value || err } catch { throw err } diff --git a/packages/nuxt/src/app/entry.ts b/packages/nuxt/src/app/entry.ts index 36388b7826..c9a6b2ef77 100644 --- a/packages/nuxt/src/app/entry.ts +++ b/packages/nuxt/src/app/entry.ts @@ -35,7 +35,7 @@ if (process.server) { await nuxt.hooks.callHook('app:created', vueApp) } catch (err) { await nuxt.callHook('app:error', err) - ssrContext.error = ssrContext.error || err + nuxt.payload.error = nuxt.payload.error || err } return vueApp diff --git a/packages/nuxt/src/app/nuxt.ts b/packages/nuxt/src/app/nuxt.ts index d738e5738a..2f5bc1ec72 100644 --- a/packages/nuxt/src/app/nuxt.ts +++ b/packages/nuxt/src/app/nuxt.ts @@ -59,7 +59,8 @@ interface _NuxtApp { res?: CompatibilityEvent['res'] runtimeConfig: RuntimeConfig noSSR: boolean - error?: any + /** whether we are rendering an SSR error */ + error?: boolean nuxt: _NuxtApp payload: _NuxtApp['payload'] teleports?: Record @@ -70,6 +71,14 @@ interface _NuxtApp { data?: Record state?: Record rendered?: Function + error?: Error | { + url: string + statusCode: string + statusMessage: string + message: string + description: string + data?: any + } [key: string]: any } @@ -119,19 +128,19 @@ export function createNuxtApp (options: CreateOptions) { defineGetter(nuxtApp.vueApp, '$nuxt', nuxtApp) defineGetter(nuxtApp.vueApp.config.globalProperties, '$nuxt', nuxtApp) - // Expose nuxt to the renderContext - if (nuxtApp.ssrContext) { - nuxtApp.ssrContext.nuxt = nuxtApp - } - if (process.server) { + // Expose nuxt to the renderContext + if (nuxtApp.ssrContext) { + nuxtApp.ssrContext.nuxt = nuxtApp + } // Expose to server renderer to create window.__NUXT__ nuxtApp.ssrContext = nuxtApp.ssrContext || {} as any + if (nuxtApp.ssrContext.payload) { + Object.assign(nuxtApp.payload, nuxtApp.ssrContext.payload) + } nuxtApp.ssrContext.payload = nuxtApp.payload - } - // Expose client runtime-config to the payload - if (process.server) { + // Expose client runtime-config to the payload nuxtApp.payload.config = { public: options.ssrContext.runtimeConfig.public, app: options.ssrContext.runtimeConfig.app diff --git a/packages/nuxt/src/core/runtime/nitro/error.ts b/packages/nuxt/src/core/runtime/nitro/error.ts index 74ab0f78b9..d0579af724 100644 --- a/packages/nuxt/src/core/runtime/nitro/error.ts +++ b/packages/nuxt/src/core/runtime/nitro/error.ts @@ -2,13 +2,14 @@ import { withQuery } from 'ufo' import type { NitroErrorHandler } from 'nitropack' // @ts-ignore TODO import { normalizeError, isJsonRequest } from '#internal/nitro/utils' +import { NuxtApp } from '#app' export default async function errorhandler (_error, event) { // Parse and normalize error const { stack, statusCode, statusMessage, message } = normalizeError(_error) // Create an error object - const errorObject = { + const errorObject: Exclude = { url: event.req.url, statusCode, statusMessage, @@ -20,7 +21,7 @@ export default async function errorhandler (_error, event) { } // Set response code and message - event.res.statusCode = errorObject.statusCode + event.res.statusCode = errorObject.statusCode as any as number event.res.statusMessage = errorObject.statusMessage // Console output @@ -36,7 +37,7 @@ export default async function errorhandler (_error, event) { } // HTML response - const url = withQuery('/__nuxt_error', errorObject as any) + const url = withQuery('/__nuxt_error', errorObject) const html = await $fetch(url).catch((error) => { console.error('[nitro] Error while generating error response', error) return errorObject.statusMessage diff --git a/packages/nuxt/src/core/runtime/nitro/renderer.ts b/packages/nuxt/src/core/runtime/nitro/renderer.ts index e23f964617..6287e295bb 100644 --- a/packages/nuxt/src/core/runtime/nitro/renderer.ts +++ b/packages/nuxt/src/core/runtime/nitro/renderer.ts @@ -100,7 +100,7 @@ const getSPARenderer = lazyCachedFunction(async () => { export default eventHandler(async (event) => { // 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 : null const url = ssrError?.url as string || event.req.url! // Initialize ssr context @@ -111,9 +111,9 @@ export default eventHandler(async (event) => { res: event.res, runtimeConfig: useRuntimeConfig(), noSSR: !!event.req.headers['x-nuxt-no-ssr'], - error: ssrError, + error: !!ssrError, nuxt: undefined, /* NuxtApp */ - payload: undefined + payload: ssrError ? { error: ssrError } : undefined } // Render app @@ -126,8 +126,8 @@ export default eventHandler(async (event) => { if (!_rendered) { return } - if (ssrContext.error && !ssrError) { - throw ssrContext.error + if (ssrContext.payload?.error && !ssrError) { + throw ssrContext.payload.error } // Render meta