UNPKG

nuxt

Version:

Nuxt is a free and open-source framework with an intuitive and extendable way to create type-safe, performant and production-grade full-stack web applications and websites with Vue.js.

226 lines (225 loc) • 8.3 kB
import { isInComponentSetup, tryUseNuxtApp, useNuxtApp, useRuntimeConfig } from "../nuxt.js"; import { useRoute } from "./router.js"; import { useHead as useHead$1 } from "../../head/runtime/composables.js"; import "./head.js"; import { onServerPrefetch as onServerPrefetch$1 } from "./server-prefetch.js"; import { stateDiagnostics } from "../diagnostics/state.js"; import { getAppManifest, getRouteRules } from "./manifest.js"; import { onServerPrefetch, reactive } from "vue"; import { appId, appManifest, multiApp, payloadExtraction, renderJsonPayloads } from "#build/nuxt.config.mjs"; import { hasProtocol, joinURL } from "ufo"; import { defineLink } from "@unhead/vue"; import { parse } from "devalue"; //#region src/app/composables/payload.ts /** @since 3.0.0 */ async function loadPayload(url, opts = {}) { if (import.meta.server || !payloadExtraction) return null; if (await shouldLoadPayload(url)) { const payloadURL = await _getPayloadURL(url, opts); const cache = isCachedPayloadRoute(url) ? "default" : "force-cache"; if (opts.fresh) return await _importPayload(payloadURL, cache, opts.signal, opts.promoted) || null; return await _sharedImportPayload(payloadURL, cache, opts.signal, opts.promoted) || null; } return null; } const inFlightPayloads = /* @__PURE__ */ new Map(); /** * Concurrent identical `GET`s are not coalesced by the browser, and `force-cache` cannot help * until the first response is stored, so a navigation joins a speculative fetch instead. */ function _sharedImportPayload(payloadURL, cache, signal, promoted) { const running = inFlightPayloads.get(payloadURL); if (running) return running.then((attempt) => { if (attempt.aborted && !signal?.aborted) return _sharedImportPayload(payloadURL, cache, signal, promoted); return attempt.payload; }); const attempt = _importPayload(payloadURL, cache, signal, promoted).then((payload) => ({ payload, aborted: !payload && !!signal?.aborted })); attempt.finally(() => { if (inFlightPayloads.get(payloadURL) === attempt) inFlightPayloads.delete(payloadURL); }); inFlightPayloads.set(payloadURL, attempt); return attempt.then((result) => result.payload); } let linkRelType; function detectLinkRelType() { if (import.meta.server) return "preload"; if (linkRelType) return linkRelType; const relList = document.createElement("link").relList; linkRelType = relList && relList.supports && relList.supports("prefetch") ? "prefetch" : "preload"; return linkRelType; } /** @since 3.0.0 */ function preloadPayload(url, opts = {}) { const nuxtApp = useNuxtApp(); const promise = shouldLoadPayload(url).then(async (shouldPreload) => { if (!shouldPreload) return; const payloadURL = await _getPayloadURL(url, opts); const rel = detectLinkRelType(); const link = renderJsonPayloads ? defineLink({ rel, as: "fetch", crossorigin: "anonymous", href: payloadURL }) : { rel: "modulepreload", crossorigin: "", href: payloadURL }; if (import.meta.server) nuxtApp.runWithContext(() => useHead$1({ link: [link] })); else { const linkEl = document.createElement("link"); linkEl.rel = rel; linkEl.setAttribute("as", "fetch"); linkEl.crossOrigin = "anonymous"; linkEl.href = payloadURL; document.head.appendChild(linkEl); return new Promise((resolve, reject) => { const cleanup = () => { linkEl.removeEventListener("load", onLoad); linkEl.removeEventListener("error", onError); }; const onLoad = () => { cleanup(); resolve(); }; const onError = () => { cleanup(); reject(); }; linkEl.addEventListener("load", onLoad); linkEl.addEventListener("error", onError); }); } }); if (import.meta.server) onServerPrefetch(() => promise); else onServerPrefetch$1(); return promise; } const filename = renderJsonPayloads ? "_payload.json" : "_payload.js"; const payloadBuildIdParam = "_b"; async function _getPayloadURL(url, opts = {}) { const u = new URL(url, "http://localhost"); if (u.host !== "localhost" || hasProtocol(u.pathname, { acceptRelative: true })) throw stateDiagnostics.NUXT_E7001({ url }); const config = /* @__PURE__ */ useRuntimeConfig(); const hash = opts.hash || (opts.fresh || import.meta.dev ? Date.now() : config.app.buildId); const cdnURL = config.app.cdnURL; const baseOrCdnURL = cdnURL && await isPrerendered(url) ? cdnURL : config.app.baseURL; const payloadURL = joinURL(baseOrCdnURL, u.pathname, filename); if (!isCachedPayloadRoute(url)) u.search = ""; if (hash) u.searchParams.set(payloadBuildIdParam, String(hash)); return payloadURL + u.search; } async function _importPayload(payloadURL, cache, signal, promoted) { if (import.meta.server || !payloadExtraction) return null; const priority = promoted ? "high" : void 0; try { if (renderJsonPayloads) { const res = await fetch(payloadURL, import.meta.dev ? { signal, priority } : { cache, signal, priority }); if (!res.ok) { if (import.meta.dev) stateDiagnostics.NUXT_E7002({ url: payloadURL }); return null; } return await parsePayload(await res.text()); } else return await import( /* webpackIgnore: true */ /* @vite-ignore */ payloadURL ).then((r) => r.default || r); } catch (err) { if (!signal?.aborted) stateDiagnostics.NUXT_E7002({ url: payloadURL, cause: err }); } return null; } function _shouldLoadPrerenderedPayload(rules) { if (rules.redirect) return false; if (rules.prerender) return true; } function _getRoutePath(url) { return new URL(url, "http://localhost").pathname; } /** @internal */ function isCachedPayloadRoute(url) { return !!getRouteRules({ path: _getRoutePath(url) }).payload; } async function _isPrerenderedInManifest(url) { if (!appManifest) return false; url = _getRoutePath(url); url = url === "/" ? url : url.replace(/\/$/, ""); try { return (await getAppManifest()).prerendered.includes(url); } catch { return false; } } /** * @internal */ async function shouldLoadPayload(url = useRoute().path) { const rules = getRouteRules({ path: _getRoutePath(url) }); if (rules.ssr === false) return false; const res = _shouldLoadPrerenderedPayload(rules); if (res !== void 0) return res; if (rules.payload) return true; return await _isPrerenderedInManifest(url); } /** @since 3.0.0 */ async function isPrerendered(url = useRoute().path) { const res = _shouldLoadPrerenderedPayload(getRouteRules({ path: _getRoutePath(url) })); if (res !== void 0) return res; return await _isPrerenderedInManifest(url); } let payloadCache = null; /** @since 3.4.0 */ async function getNuxtClientPayload() { if (import.meta.server) return null; if (payloadCache) return payloadCache; const el = multiApp ? document.querySelector(`[data-nuxt-data="${appId}"]`) : document.getElementById("__NUXT_DATA__"); if (!el) return {}; const inlineData = await parsePayload(el.textContent || ""); const externalData = el.dataset.src ? await _importPayload(el.dataset.src, inlineData.prerenderedAt ? "force-cache" : "no-cache") : void 0; payloadCache = { ...inlineData, ...externalData, ...multiApp ? window.__NUXT__?.[appId] : window.__NUXT__ }; if (payloadCache.config?.public) payloadCache.config.public = reactive(payloadCache.config.public); return payloadCache; } async function parsePayload(payload) { return await parse(payload, useNuxtApp()._payloadRevivers); } /** * This is an experimental function for configuring passing rich data from server -> client. * @since 3.4.0 */ function definePayloadReducer(name, reduce) { if (import.meta.server) useNuxtApp().ssrContext["~payloadReducers"][name] = reduce; } /** * This is an experimental function for configuring passing rich data from server -> client. * * This function _must_ be called in a Nuxt plugin that is `unshift`ed to the beginning of the Nuxt plugins array. * @since 3.4.0 */ function definePayloadReviver(name, revive) { if (import.meta.dev) { const nuxtApp = tryUseNuxtApp(); if (nuxtApp && isInComponentSetup(nuxtApp)) stateDiagnostics.NUXT_E7004(); } if (import.meta.client) useNuxtApp()._payloadRevivers[name] = revive; } //#endregion export { definePayloadReducer, definePayloadReviver, getNuxtClientPayload, isCachedPayloadRoute, isPrerendered, loadPayload, parsePayload, preloadPayload, shouldLoadPayload };