diff --git a/packages/nuxt/src/app/nuxt.ts b/packages/nuxt/src/app/nuxt.ts index 26731e1479..afd8a87ed9 100644 --- a/packages/nuxt/src/app/nuxt.ts +++ b/packages/nuxt/src/app/nuxt.ts @@ -272,6 +272,7 @@ export function createNuxtApp (options: CreateOptions) { get vue () { return nuxtApp.vueApp.version }, }, payload: shallowReactive({ + ...options.ssrContext?.payload || {}, data: shallowReactive({}), state: reactive({}), once: new Set(), @@ -315,6 +316,20 @@ export function createNuxtApp (options: CreateOptions) { nuxtApp.payload.serverRendered = true } + if (import.meta.server && nuxtApp.ssrContext) { + nuxtApp.payload.path = nuxtApp.ssrContext.url + + // Expose nuxt to the renderContext + nuxtApp.ssrContext.nuxt = nuxtApp + nuxtApp.ssrContext.payload = nuxtApp.payload + + // Expose client runtime-config to the payload + nuxtApp.ssrContext.config = { + public: nuxtApp.ssrContext.runtimeConfig.public, + app: nuxtApp.ssrContext.runtimeConfig.app, + } + } + if (import.meta.client) { const __NUXT__ = multiApp ? window.__NUXT__?.[nuxtApp._id] : window.__NUXT__ // TODO: remove/refactor in https://github.com/nuxt/nuxt/issues/25336 @@ -361,29 +376,6 @@ export function createNuxtApp (options: CreateOptions) { defineGetter(nuxtApp.vueApp, '$nuxt', nuxtApp) defineGetter(nuxtApp.vueApp.config.globalProperties, '$nuxt', nuxtApp) - if (import.meta.server) { - if (nuxtApp.ssrContext) { - // Expose nuxt to the renderContext - nuxtApp.ssrContext.nuxt = nuxtApp - // Expose payload types - nuxtApp.ssrContext._payloadReducers = {} - // Expose current path - nuxtApp.payload.path = nuxtApp.ssrContext.url - } - // Expose to server renderer to create payload - 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 - nuxtApp.ssrContext!.config = { - public: options.ssrContext!.runtimeConfig.public, - app: options.ssrContext!.runtimeConfig.app, - } - } - // Listen to chunk load errors if (import.meta.client) { window.addEventListener('nuxt.preloadError', (event) => { diff --git a/packages/nuxt/src/app/plugins/revive-payload.server.ts b/packages/nuxt/src/app/plugins/revive-payload.server.ts index f2cb08983d..773b8c77fb 100644 --- a/packages/nuxt/src/app/plugins/revive-payload.server.ts +++ b/packages/nuxt/src/app/plugins/revive-payload.server.ts @@ -6,25 +6,25 @@ import { defineNuxtPlugin } from '../nuxt' // @ts-expect-error Virtual file. import { componentIslands } from '#build/nuxt.config.mjs' -const reducers: Record any> = { - NuxtError: data => isNuxtError(data) && data.toJSON(), - EmptyShallowRef: data => isRef(data) && isShallow(data) && !data.value && (typeof data.value === 'bigint' ? '0n' : (JSON.stringify(data.value) || '_')), - EmptyRef: data => isRef(data) && !data.value && (typeof data.value === 'bigint' ? '0n' : (JSON.stringify(data.value) || '_')), - ShallowRef: data => isRef(data) && isShallow(data) && data.value, - ShallowReactive: data => isReactive(data) && isShallow(data) && toRaw(data), - Ref: data => isRef(data) && data.value, - Reactive: data => isReactive(data) && toRaw(data), -} +const reducers: [string, (data: any) => any][] = [ + ['NuxtError', data => isNuxtError(data) && data.toJSON()], + ['EmptyShallowRef', data => isRef(data) && isShallow(data) && !data.value && (typeof data.value === 'bigint' ? '0n' : (JSON.stringify(data.value) || '_'))], + ['EmptyRef', data => isRef(data) && !data.value && (typeof data.value === 'bigint' ? '0n' : (JSON.stringify(data.value) || '_'))], + ['ShallowRef', data => isRef(data) && isShallow(data) && data.value], + ['ShallowReactive', data => isReactive(data) && isShallow(data) && toRaw(data)], + ['Ref', data => isRef(data) && data.value], + ['Reactive', data => isReactive(data) && toRaw(data)], +] if (componentIslands) { - reducers.Island = data => data && data?.__nuxt_island + reducers.push(['Island', data => data && data?.__nuxt_island]) } export default defineNuxtPlugin({ name: 'nuxt:revive-payload:server', setup () { - for (const reducer in reducers) { - definePayloadReducer(reducer, reducers[reducer as keyof typeof reducers]) + for (const [reducer, fn] of reducers) { + definePayloadReducer(reducer, fn) } }, }) diff --git a/packages/nuxt/src/core/runtime/nitro/dev-server-logs.ts b/packages/nuxt/src/core/runtime/nitro/dev-server-logs.ts index 239a5d24a5..67f2241d21 100644 --- a/packages/nuxt/src/core/runtime/nitro/dev-server-logs.ts +++ b/packages/nuxt/src/core/runtime/nitro/dev-server-logs.ts @@ -77,7 +77,8 @@ export default (nitroApp: NitroApp) => { const ctx = asyncContext.tryUse() if (!ctx) { return } try { - htmlContext.bodyAppend.unshift(``) + const reducers = Object.assign(Object.create(null), devReducers, ctx.event.context._payloadReducers) + htmlContext.bodyAppend.unshift(``) } catch (e) { const shortError = e instanceof Error && 'toString' in e ? ` Received \`${e.toString()}\`.` : '' console.warn(`[nuxt] Failed to stringify dev server logs.${shortError} You can define your own reducer/reviver for rich types following the instructions in https://nuxt.com/docs/api/composables/use-nuxt-app#payload.`) diff --git a/packages/nuxt/src/core/runtime/nitro/renderer.ts b/packages/nuxt/src/core/runtime/nitro/renderer.ts index 317e54c8c7..4cc13ce445 100644 --- a/packages/nuxt/src/core/runtime/nitro/renderer.ts +++ b/packages/nuxt/src/core/runtime/nitro/renderer.ts @@ -306,7 +306,7 @@ export default defineRenderHandler(async (event): Promise