import { createRenderer, getRequestDependencies, getPreloadLinks, getPrefetchLinks } from 'vue-bundle-renderer/runtime'; import { j as joinRelativeURL, u as useRuntimeConfig, a as getResponseStatusText, b as getResponseStatus, e as defineRenderHandler, f as getQuery, c as createError, h as destr, i as getRouteRules, k as joinURL, l as useNitroApp } from '../nitro/nitro.mjs'; import { renderToString } from 'vue/server-renderer'; import { createHead as createHead$1, propsToString, renderSSRHead } from 'unhead/server'; import { stringify, uneval } from 'devalue'; import { walkResolver } from 'unhead/utils'; import { isRef, toValue, hasInjectionContext, inject, ref, watchEffect, getCurrentInstance, onBeforeUnmount, onDeactivated, onActivated } from 'vue'; const VueResolver = (_, value) => { return isRef(value) ? toValue(value) : value; }; const headSymbol = "usehead"; // @__NO_SIDE_EFFECTS__ function vueInstall(head) { const plugin = { install(app) { app.config.globalProperties.$unhead = head; app.config.globalProperties.$head = head; app.provide(headSymbol, head); } }; return plugin.install; } // @__NO_SIDE_EFFECTS__ function injectHead() { if (hasInjectionContext()) { const instance = inject(headSymbol); if (instance) { return instance; } } throw new Error("useHead() was called without provide context, ensure you call it through the setup() function."); } function useHead(input, options = {}) { const head = options.head || /* @__PURE__ */ injectHead(); return head.ssr ? head.push(input || {}, options) : clientUseHead(head, input, options); } function clientUseHead(head, input, options = {}) { const deactivated = ref(false); let entry; watchEffect(() => { const i = deactivated.value ? {} : walkResolver(input, VueResolver); if (entry) { entry.patch(i); } else { entry = head.push(i, options); } }); const vm = getCurrentInstance(); if (vm) { onBeforeUnmount(() => { entry.dispose(); }); onDeactivated(() => { deactivated.value = true; }); onActivated(() => { deactivated.value = false; }); } return entry; } // @__NO_SIDE_EFFECTS__ function createHead(options = {}) { const head = createHead$1({ ...options, propResolvers: [VueResolver] }); head.install = vueInstall(head); return head; } const NUXT_RUNTIME_PAYLOAD_EXTRACTION = false; const appHead = {"meta":[{"name":"viewport","content":"width=device-width, initial-scale=1"},{"charset":"utf-8"}],"link":[{"rel":"icon","type":"image/svg+xml","href":"/favicon.svg"}],"style":[],"script":[],"noscript":[],"htmlAttrs":{"lang":"en"}}; const appRootTag = "div"; const appRootAttrs = {"id":"__nuxt"}; const appTeleportTag = "div"; const appTeleportAttrs = {"id":"teleports"}; const appSpaLoaderTag = "div"; const appSpaLoaderAttrs = {"id":"__nuxt-loader"}; const appId = "nuxt-app"; function baseURL() { // TODO: support passing event to `useRuntimeConfig` return useRuntimeConfig().app.baseURL; } function buildAssetsDir() { // TODO: support passing event to `useRuntimeConfig` return useRuntimeConfig().app.buildAssetsDir; } function buildAssetsURL(...path) { return joinRelativeURL(publicAssetsURL(), buildAssetsDir(), ...path); } function publicAssetsURL(...path) { // TODO: support passing event to `useRuntimeConfig` const app = useRuntimeConfig().app; const publicBase = app.cdnURL || app.baseURL; return path.length ? joinRelativeURL(publicBase, ...path) : publicBase; } const APP_ROOT_OPEN_TAG = `<${appRootTag}${propsToString(appRootAttrs)}>`; const APP_ROOT_CLOSE_TAG = ``; // @ts-expect-error file will be produced after app build const getServerEntry = () => import('../build/server.mjs').then((r) => r.default || r); // @ts-expect-error file will be produced after app build const getPrecomputedDependencies = () => import('../build/client.precomputed.mjs').then((r) => r.default || r).then((r) => typeof r === "function" ? r() : r); // -- SSR Renderer -- const getSSRRenderer = lazyCachedFunction(async () => { // Load server bundle const createSSRApp = await getServerEntry(); if (!createSSRApp) { throw new Error("Server bundle is not available"); } // Load precomputed dependencies const precomputed = await getPrecomputedDependencies(); // Create renderer const renderer = createRenderer(createSSRApp, { precomputed, manifest: undefined, renderToString: renderToString$1, buildAssetsURL }); async function renderToString$1(input, context) { const html = await renderToString(input, context); return APP_ROOT_OPEN_TAG + html + APP_ROOT_CLOSE_TAG; } return renderer; }); // -- SPA Renderer -- const getSPARenderer = lazyCachedFunction(async () => { const precomputed = await getPrecomputedDependencies(); // @ts-expect-error virtual file const spaTemplate = await import('../virtual/_virtual_spa-template.mjs').then((r) => r.template).catch(() => "").then((r) => { { const APP_SPA_LOADER_OPEN_TAG = `<${appSpaLoaderTag}${propsToString(appSpaLoaderAttrs)}>`; const APP_SPA_LOADER_CLOSE_TAG = ``; const appTemplate = APP_ROOT_OPEN_TAG + APP_ROOT_CLOSE_TAG; const loaderTemplate = r ? APP_SPA_LOADER_OPEN_TAG + r + APP_SPA_LOADER_CLOSE_TAG : ""; return appTemplate + loaderTemplate; } }); // Create SPA renderer and cache the result for all requests const renderer = createRenderer(() => () => {}, { precomputed, manifest: undefined, renderToString: () => spaTemplate, buildAssetsURL }); const result = await renderer.renderToString({}); const renderToString = (ssrContext) => { const config = useRuntimeConfig(ssrContext.event); ssrContext.modules ||= new Set(); ssrContext.payload.serverRendered = false; ssrContext.config = { public: config.public, app: config.app }; return Promise.resolve(result); }; return { rendererContext: renderer.rendererContext, renderToString }; }); function lazyCachedFunction(fn) { let res = null; return () => { if (res === null) { res = fn().catch((err) => { res = null; throw err; }); } return res; }; } function getRenderer(ssrContext) { return ssrContext.noSSR ? getSPARenderer() : getSSRRenderer(); } // @ts-expect-error file will be produced after app build const getSSRStyles = lazyCachedFunction(() => import('../build/styles.mjs').then((r) => r.default || r)); function renderPayloadResponse(ssrContext) { return { body: stringify(splitPayload(ssrContext).payload, ssrContext["~payloadReducers"]) , statusCode: getResponseStatus(ssrContext.event), statusMessage: getResponseStatusText(ssrContext.event), headers: { "content-type": "application/json;charset=utf-8" , "x-powered-by": "Nuxt" } }; } function renderPayloadJsonScript(opts) { const contents = opts.data ? stringify(opts.data, opts.ssrContext["~payloadReducers"]) : ""; const payload = { "type": "application/json", "innerHTML": contents, "data-nuxt-data": appId, "data-ssr": !(opts.ssrContext.noSSR) }; { payload.id = "__NUXT_DATA__"; } if (opts.src) { payload["data-src"] = opts.src; } const config = uneval(opts.ssrContext.config); return [payload, { innerHTML: `window.__NUXT__={};window.__NUXT__.config=${config}` }]; } function splitPayload(ssrContext) { const { data, prerenderedAt, ...initial } = ssrContext.payload; return { initial: { ...initial, prerenderedAt }, payload: { data, prerenderedAt } }; } const unheadOptions = { disableDefaults: true, }; function createSSRContext(event) { const ssrContext = { url: event.path, event, runtimeConfig: useRuntimeConfig(event), noSSR: event.context.nuxt?.noSSR || (false), head: createHead(unheadOptions), error: false, nuxt: undefined, payload: {}, ["~payloadReducers"]: Object.create(null), modules: new Set() }; return ssrContext; } function setSSRError(ssrContext, error) { ssrContext.error = true; ssrContext.payload = { error }; ssrContext.url = error.url; } async function renderInlineStyles(usedModules) { const styleMap = await getSSRStyles(); const inlinedStyles = new Set(); for (const mod of usedModules) { if (mod in styleMap && styleMap[mod]) { for (const style of await styleMap[mod]()) { inlinedStyles.add(style); } } } return Array.from(inlinedStyles).map((style) => ({ innerHTML: style })); } const renderSSRHeadOptions = {"omitLineBreaks":true}; const entryIds = []; // @ts-expect-error private property consumed by vite-generated url helpers globalThis.__buildAssetsURL = buildAssetsURL; // @ts-expect-error private property consumed by vite-generated url helpers globalThis.__publicAssetsURL = publicAssetsURL; const HAS_APP_TELEPORTS = !!(appTeleportAttrs.id); const APP_TELEPORT_OPEN_TAG = HAS_APP_TELEPORTS ? `<${appTeleportTag}${propsToString(appTeleportAttrs)}>` : ""; const APP_TELEPORT_CLOSE_TAG = HAS_APP_TELEPORTS ? `` : ""; const PAYLOAD_URL_RE = /^[^?]*\/_payload.json(?:\?.*)?$/ ; const PAYLOAD_FILENAME = "_payload.json" ; const renderer = defineRenderHandler(async (event) => { const nitroApp = useNitroApp(); // Whether we're rendering an error page const ssrError = event.path.startsWith("/__nuxt_error") ? getQuery(event) : null; if (ssrError && !("__unenv__" in event.node.req)) { throw createError({ status: 404, statusText: "Page Not Found: /__nuxt_error", message: "Page Not Found: /__nuxt_error" }); } // Initialize ssr context const ssrContext = createSSRContext(event); // needed for hash hydration plugin to work const headEntryOptions = { mode: "server" }; ssrContext.head.push(appHead, headEntryOptions); if (ssrError) { // eslint-disable-next-line @typescript-eslint/no-deprecated const status = ssrError.status || ssrError.statusCode; if (status) { // eslint-disable-next-line @typescript-eslint/no-deprecated ssrError.status = ssrError.statusCode = Number.parseInt(status); } if (typeof ssrError.data === "string") { try { ssrError.data = destr(ssrError.data); } catch {} } setSSRError(ssrContext, ssrError); } // Get route options (for `ssr: false`, `isr`, `cache` and `noScripts`) const routeOptions = getRouteRules(event); // Whether we are prerendering route or using ISR/SWR caching const _PAYLOAD_EXTRACTION = !ssrContext.noSSR && (NUXT_RUNTIME_PAYLOAD_EXTRACTION); const isRenderingPayload = (_PAYLOAD_EXTRACTION || false) && PAYLOAD_URL_RE.test(ssrContext.url); if (isRenderingPayload) { const url = ssrContext.url.substring(0, ssrContext.url.lastIndexOf("/")) || "/"; ssrContext.url = url; event._path = event.node.req.url = url; } if (routeOptions.ssr === false) { ssrContext.noSSR = true; } const payloadURL = _PAYLOAD_EXTRACTION ? joinURL(ssrContext.runtimeConfig.app.cdnURL || ssrContext.runtimeConfig.app.baseURL, ssrContext.url.replace(/\?.*$/, ""), PAYLOAD_FILENAME) + "?" + ssrContext.runtimeConfig.app.buildId : undefined; // Render app const renderer = await getRenderer(ssrContext); { for (const id of entryIds) { ssrContext.modules.add(id); } } const _rendered = await renderer.renderToString(ssrContext).catch(async (error) => { // We use error to bypass full render if we have an early response we can make // TODO: remove _renderResponse in nuxt v5 if ((ssrContext["~renderResponse"] || ssrContext._renderResponse) && error.message === "skipping render") { return {}; } // Use explicitly thrown error in preference to subsequent rendering errors const _err = !ssrError && ssrContext.payload?.error || error; await ssrContext.nuxt?.hooks.callHook("app:error", _err); throw _err; }); // Render inline styles // TODO: remove _renderResponse in nuxt v5 const inlinedStyles = !ssrContext["~renderResponse"] && !ssrContext._renderResponse && !isRenderingPayload ? await renderInlineStyles(ssrContext.modules ?? []) : []; await ssrContext.nuxt?.hooks.callHook("app:rendered", { ssrContext, renderResult: _rendered }); if (ssrContext["~renderResponse"] || ssrContext._renderResponse) { // TODO: remove _renderResponse in nuxt v5 return ssrContext["~renderResponse"] || ssrContext._renderResponse; } // Handle errors if (ssrContext.payload?.error && !ssrError) { throw ssrContext.payload.error; } // Directly render payload routes if (isRenderingPayload) { const response = renderPayloadResponse(ssrContext); return response; } const NO_SCRIPTS = routeOptions.noScripts; // Setup head const { styles, scripts } = getRequestDependencies(ssrContext, renderer.rendererContext); // 1. Preload payloads and app manifest if (_PAYLOAD_EXTRACTION && !NO_SCRIPTS) { ssrContext.head.push({ link: [{ rel: "preload", as: "fetch", crossorigin: "anonymous", href: payloadURL } ] }, headEntryOptions); } if (ssrContext["~preloadManifest"] && !NO_SCRIPTS) { ssrContext.head.push({ link: [{ rel: "preload", as: "fetch", fetchpriority: "low", crossorigin: "anonymous", href: buildAssetsURL(`builds/meta/${ssrContext.runtimeConfig.app.buildId}.json`) }] }, { ...headEntryOptions, tagPriority: "low" }); } // 2. Styles if (inlinedStyles.length) { ssrContext.head.push({ style: inlinedStyles }); } const link = []; for (const resource of Object.values(styles)) { // Add CSS links in for CSS files // - in production // - in dev mode when not rendering an island link.push({ rel: "stylesheet", href: renderer.rendererContext.buildAssetsURL(resource.file), crossorigin: "" }); } if (link.length) { ssrContext.head.push({ link }, headEntryOptions); } if (!NO_SCRIPTS) { // 4. Resource Hints // Remove lazy hydrated modules from ssrContext.modules so they don't get preloaded // (CSS links are already added above, this only affects JS preloads) if (ssrContext["~lazyHydratedModules"]) { for (const id of ssrContext["~lazyHydratedModules"]) { ssrContext.modules?.delete(id); } } ssrContext.head.push({ link: getPreloadLinks(ssrContext, renderer.rendererContext) }, headEntryOptions); ssrContext.head.push({ link: getPrefetchLinks(ssrContext, renderer.rendererContext) }, headEntryOptions); // 5. Payloads ssrContext.head.push({ script: _PAYLOAD_EXTRACTION ? renderPayloadJsonScript({ ssrContext, data: splitPayload(ssrContext).initial, src: payloadURL }) : renderPayloadJsonScript({ ssrContext, data: ssrContext.payload }) }, { ...headEntryOptions, tagPosition: "bodyClose", tagPriority: "high" }); } // 6. Scripts if (!routeOptions.noScripts) { const tagPosition = "head"; ssrContext.head.push({ script: Object.values(scripts).map((resource) => ({ type: resource.module ? "module" : null, src: renderer.rendererContext.buildAssetsURL(resource.file), defer: resource.module ? null : true, tagPosition, crossorigin: "" })) }, headEntryOptions); } const { headTags, bodyTags, bodyTagsOpen, htmlAttrs, bodyAttrs } = await renderSSRHead(ssrContext.head, renderSSRHeadOptions); // Create render context const htmlContext = { htmlAttrs: htmlAttrs ? [htmlAttrs] : [], head: normalizeChunks([headTags]), bodyAttrs: bodyAttrs ? [bodyAttrs] : [], bodyPrepend: normalizeChunks([bodyTagsOpen, ssrContext.teleports?.body]), body: [_rendered.html, APP_TELEPORT_OPEN_TAG + (HAS_APP_TELEPORTS ? joinTags([ssrContext.teleports?.[`#${appTeleportAttrs.id}`]]) : "") + APP_TELEPORT_CLOSE_TAG], bodyAppend: [bodyTags] }; // Allow hooking into the rendered result await nitroApp.hooks.callHook("render:html", htmlContext, { event }); // Construct HTML response return { body: renderHTMLDocument(htmlContext), statusCode: getResponseStatus(event), statusMessage: getResponseStatusText(event), headers: { "content-type": "text/html;charset=utf-8", "x-powered-by": "Nuxt" } }; }); function normalizeChunks(chunks) { const result = []; for (const _chunk of chunks) { const chunk = _chunk?.trim(); if (chunk) { result.push(chunk); } } return result; } function joinTags(tags) { return tags.join(""); } function joinAttrs(chunks) { if (chunks.length === 0) { return ""; } return " " + chunks.join(" "); } function renderHTMLDocument(html) { return "" + `` + `${joinTags(html.head)}` + `${joinTags(html.bodyPrepend)}${joinTags(html.body)}${joinTags(html.bodyAppend)}` + ""; } const renderer$1 = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({ __proto__: null, default: renderer }, Symbol.toStringTag, { value: 'Module' })); export { baseURL as b, headSymbol as h, renderer$1 as r, useHead as u }; //# sourceMappingURL=renderer.mjs.map