diff --git a/package.json b/package.json index 0faa04b530..e5d3cc831b 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "lint:knip": "pnpx knip", "play": "nuxi dev playground", "play:build": "nuxi build playground", + "play:generate": "nuxi generate playground", "play:preview": "nuxi preview playground", "test": "pnpm test:fixtures && pnpm test:fixtures:dev && pnpm test:fixtures:webpack && pnpm test:unit && pnpm test:runtime && pnpm test:types && pnpm typecheck", "test:prepare": "jiti ./test/prepare.ts", diff --git a/packages/nuxt/src/app/composables/payload.ts b/packages/nuxt/src/app/composables/payload.ts index 921844f0bc..7a7d8fd2e7 100644 --- a/packages/nuxt/src/app/composables/payload.ts +++ b/packages/nuxt/src/app/composables/payload.ts @@ -1,7 +1,7 @@ import { hasProtocol, joinURL, withoutTrailingSlash } from 'ufo' import { parse } from 'devalue' import { useHead } from '@unhead/vue' -import { getCurrentInstance } from 'vue' +import { getCurrentInstance, onServerPrefetch } from 'vue' import { useNuxtApp, useRuntimeConfig } from '../nuxt' import { useRoute } from './router' @@ -16,9 +16,9 @@ interface LoadPayloadOptions { } /** @since 3.0.0 */ -export function loadPayload (url: string, opts: LoadPayloadOptions = {}): Record | Promise> | null { +export async function loadPayload (url: string, opts: LoadPayloadOptions = {}): Promise | null> { if (import.meta.server || !payloadExtraction) { return null } - const payloadURL = _getPayloadURL(url, opts) + const payloadURL = await _getPayloadURL(url, opts) const nuxtApp = useNuxtApp() const cache = nuxtApp._payloadCache = nuxtApp._payloadCache || {} if (payloadURL in cache) { @@ -39,26 +39,34 @@ export function loadPayload (url: string, opts: LoadPayloadOptions = {}): Record return cache[payloadURL] } /** @since 3.0.0 */ -export function preloadPayload (url: string, opts: LoadPayloadOptions = {}) { - const payloadURL = _getPayloadURL(url, opts) - useHead({ - link: [ - { rel: 'modulepreload', href: payloadURL }, - ], +export function preloadPayload (url: string, opts: LoadPayloadOptions = {}): Promise { + const nuxtApp = useNuxtApp() + const promise = _getPayloadURL(url, opts).then((payloadURL) => { + nuxtApp.runWithContext(() => useHead({ + link: [ + { rel: 'modulepreload', href: payloadURL }, + ], + })) }) + if (import.meta.server) { + onServerPrefetch(() => promise) + } + return promise } // --- Internal --- const filename = renderJsonPayloads ? '_payload.json' : '_payload.js' -function _getPayloadURL (url: string, opts: LoadPayloadOptions = {}) { +async function _getPayloadURL (url: string, opts: LoadPayloadOptions = {}) { const u = new URL(url, 'http://localhost') if (u.host !== 'localhost' || hasProtocol(u.pathname, { acceptRelative: true })) { throw new Error('Payload URL must not include hostname: ' + url) } const config = useRuntimeConfig() const hash = opts.hash || (opts.fresh ? Date.now() : config.app.buildId) - return joinURL(config.app.baseURL, u.pathname, filename + (hash ? `?${hash}` : '')) + const cdnURL = config.app.cdnURL + const baseOrCdnURL = cdnURL && await isPrerendered(url) ? cdnURL : config.app.baseURL + return joinURL(baseOrCdnURL, u.pathname, filename + (hash ? `?${hash}` : '')) } async function _importPayload (payloadURL: string) { diff --git a/packages/nuxt/src/core/runtime/nitro/renderer.ts b/packages/nuxt/src/core/runtime/nitro/renderer.ts index f0a2e5a9dc..ebd84414a7 100644 --- a/packages/nuxt/src/core/runtime/nitro/renderer.ts +++ b/packages/nuxt/src/core/runtime/nitro/renderer.ts @@ -327,7 +327,7 @@ export default defineRenderHandler(async (event): Promise