diff --git a/packages/nuxt/src/app/composables/payload.ts b/packages/nuxt/src/app/composables/payload.ts index 558a50886a..1708bbad99 100644 --- a/packages/nuxt/src/app/composables/payload.ts +++ b/packages/nuxt/src/app/composables/payload.ts @@ -9,7 +9,7 @@ import { useRoute } from './router' import { getAppManifest, getRouteRules } from './manifest' // @ts-expect-error virtual import -import { appManifest, payloadExtraction, renderJsonPayloads } from '#build/nuxt.config.mjs' +import { appId, appManifest, multiApp, payloadExtraction, renderJsonPayloads } from '#build/nuxt.config.mjs' interface LoadPayloadOptions { fresh?: boolean @@ -107,7 +107,7 @@ export async function getNuxtClientPayload () { return payloadCache } - const el = document.getElementById('__NUXT_DATA__') + const el = multiApp ? document.querySelector(`[data-nuxt-data="${appId}"]`) as HTMLElement : document.getElementById('__NUXT_DATA__') if (!el) { return {} as Partial } @@ -119,7 +119,7 @@ export async function getNuxtClientPayload () { payloadCache = { ...inlineData, ...externalData, - ...window.__NUXT__, + ...(multiApp ? window.__NUXT__?.[appId] : window.__NUXT__), } if (payloadCache!.config?.public) { diff --git a/packages/nuxt/src/app/entry.ts b/packages/nuxt/src/app/entry.ts index 8c33ccae87..e60a48bd8f 100644 --- a/packages/nuxt/src/app/entry.ts +++ b/packages/nuxt/src/app/entry.ts @@ -15,7 +15,7 @@ import plugins from '#build/plugins' // @ts-expect-error virtual file import RootComponent from '#build/root-component.mjs' // @ts-expect-error virtual file -import { vueAppRootContainer } from '#build/nuxt.config.mjs' +import { appId, multiApp, vueAppRootContainer } from '#build/nuxt.config.mjs' let entry: (ssrContext?: CreateOptions['ssrContext']) => Promise> @@ -50,9 +50,10 @@ if (import.meta.client) { entry = async function initApp () { if (vueAppPromise) { return vueAppPromise } + const isSSR = Boolean( - window.__NUXT__?.serverRendered || - document.getElementById('__NUXT_DATA__')?.dataset.ssr === 'true', + (multiApp ? window.__NUXT__?.[appId] : window.__NUXT__)?.serverRendered ?? + (multiApp ? document.querySelector(`[data-nuxt-data="${appId}"]`) as HTMLElement : document.getElementById('__NUXT_DATA__'))?.dataset.ssr === 'true', ) const vueApp = isSSR ? createSSRApp(RootComponent) : createApp(RootComponent) diff --git a/packages/nuxt/src/app/nuxt.ts b/packages/nuxt/src/app/nuxt.ts index d1b660b3ce..6024fb5701 100644 --- a/packages/nuxt/src/app/nuxt.ts +++ b/packages/nuxt/src/app/nuxt.ts @@ -21,7 +21,7 @@ import type { RouteAnnouncer } from '../app/composables/route-announcer' import type { ViewTransition } from './plugins/view-transitions.client' // @ts-expect-error virtual file -import { appId } from '#build/nuxt.config.mjs' +import { appId, multiApp } from '#build/nuxt.config.mjs' import type { NuxtAppLiterals } from '#app' @@ -310,19 +310,22 @@ export function createNuxtApp (options: CreateOptions) { nuxtApp.payload.serverRendered = true } - // TODO: remove/refactor in https://github.com/nuxt/nuxt/issues/25336 - if (import.meta.client && window.__NUXT__) { - for (const key in window.__NUXT__) { - switch (key) { - case 'data': - case 'state': - case '_errors': - // Preserve reactivity for non-rich payload support - Object.assign(nuxtApp.payload[key], window.__NUXT__[key]) - break + if (import.meta.client) { + const __NUXT__ = multiApp ? window.__NUXT__?.[nuxtApp._id] : window.__NUXT__ + // TODO: remove/refactor in https://github.com/nuxt/nuxt/issues/25336 + if (__NUXT__) { + for (const key in __NUXT__) { + switch (key) { + case 'data': + case 'state': + case '_errors': + // Preserve reactivity for non-rich payload support + Object.assign(nuxtApp.payload[key], __NUXT__[key]) + break - default: - nuxtApp.payload[key] = window.__NUXT__[key] + default: + nuxtApp.payload[key] = __NUXT__[key] + } } } } diff --git a/packages/nuxt/src/app/plugins/dev-server-logs.ts b/packages/nuxt/src/app/plugins/dev-server-logs.ts index 5da79ba16f..398ab47cdd 100644 --- a/packages/nuxt/src/app/plugins/dev-server-logs.ts +++ b/packages/nuxt/src/app/plugins/dev-server-logs.ts @@ -40,7 +40,8 @@ export default defineNuxtPlugin(async (nuxtApp) => { } if (typeof window !== 'undefined') { - const content = document.getElementById('__NUXT_LOGS__')?.textContent + const nuxtLogsElement = document.querySelector(`[data-nuxt-logs="${nuxtApp._name}"]`) + const content = nuxtLogsElement?.textContent const logs = content ? parse(content, { ...devRevivers, ...nuxtApp._payloadRevivers }) as LogObject[] : [] await nuxtApp.hooks.callHook('dev:ssr-logs', logs) } diff --git a/packages/nuxt/src/app/types/augments.d.ts b/packages/nuxt/src/app/types/augments.d.ts index 3717d48d0c..5e5dd8cf44 100644 --- a/packages/nuxt/src/app/types/augments.d.ts +++ b/packages/nuxt/src/app/types/augments.d.ts @@ -26,7 +26,7 @@ declare global { } interface Window { - __NUXT__?: Record + __NUXT__?: Record | Record> useNuxtApp?: typeof useNuxtApp } } 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 5a0da7e569..6be7c7dfbd 100644 --- a/packages/nuxt/src/core/runtime/nitro/dev-server-logs.ts +++ b/packages/nuxt/src/core/runtime/nitro/dev-server-logs.ts @@ -13,6 +13,8 @@ import type { NitroApp } from 'nitro/types' // @ts-expect-error virtual file import { rootDir } from '#internal/dev-server-logs-options' +// @ts-expect-error virtual file +import { appId } from '#internal/nuxt.config.mjs' const devReducers: Record any> = { VNode: data => isVNode(data) ? { type: data.type, props: data.props } : undefined, @@ -75,7 +77,7 @@ export default (nitroApp: NitroApp) => { const ctx = asyncContext.tryUse() if (!ctx) { return } try { - htmlContext.bodyAppend.unshift(``) + 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 e57b557053..e79b40db25 100644 --- a/packages/nuxt/src/core/runtime/nitro/renderer.ts +++ b/packages/nuxt/src/core/runtime/nitro/renderer.ts @@ -31,7 +31,7 @@ import { renderSSRHeadOptions } from '#internal/unhead.config.mjs' import type { NuxtPayload, NuxtSSRContext } from '#app' // @ts-expect-error virtual file -import { appHead, appRootAttrs, appRootTag, appTeleportAttrs, appTeleportTag, componentIslands } from '#internal/nuxt.config.mjs' +import { appHead, appId, appRootAttrs, appRootTag, appTeleportAttrs, appTeleportTag, componentIslands, multiApp } from '#internal/nuxt.config.mjs' // @ts-expect-error virtual file import { buildAssetsURL, publicAssetsURL } from '#internal/nuxt/paths' @@ -427,10 +427,10 @@ export default defineRenderHandler(async (event): Promise ` -export const useRuntimeConfig = () => window?.__NUXT__?.config || window?.useNuxtApp?.().payload?.config || {} -`, + getContents: ({ nuxt }) => { + const appId = JSON.stringify(nuxt.options.appId) + return [ + 'export const useRuntimeConfig = () => ', + (!nuxt.options.future.multiApp + ? 'window?.__NUXT__?.config || window?.useNuxtApp?.().payload?.config' + : `window?.__NUXT__?.[${appId}]?.config || window?.useNuxtApp?.(${appId}).payload?.config`) + || {}, + ].join('\n') + }, } export const appConfigDeclarationTemplate: NuxtTemplate = { @@ -496,6 +503,7 @@ export const nuxtConfigTemplate: NuxtTemplate = { `export const viewTransition = ${ctx.nuxt.options.experimental.viewTransition}`, `export const appId = ${JSON.stringify(ctx.nuxt.options.appId)}`, `export const outdatedBuildInterval = ${ctx.nuxt.options.experimental.checkOutdatedBuildInterval}`, + `export const multiApp = ${!!ctx.nuxt.options.future.multiApp}`, ].join('\n\n') }, } diff --git a/test/utils.ts b/test/utils.ts index 6fce667c31..3e21fb0c27 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -114,7 +114,9 @@ export function parseData (html: string) { attrs: {}, } } - const { script, attrs = '' } = html.match(/