import { withLeadingSlash } from 'ufo'; function createRendererContext({ manifest, precomputed, buildAssetsURL }) { if (!manifest && !precomputed) { throw new Error("Either manifest or precomputed data must be provided"); } const ctx = { // Options buildAssetsURL: buildAssetsURL || withLeadingSlash, manifest, precomputed, updateManifest, // Internal cache _dependencies: {}, _dependencySets: {}, _entrypoints: [] }; function updateManifest(manifest2) { const manifestEntries = Object.entries(manifest2); ctx.manifest = manifest2; ctx._dependencies = {}; ctx._dependencySets = {}; ctx._entrypoints = manifestEntries.filter((e) => e[1].isEntry).map(([module]) => module); } if (precomputed) { ctx._dependencies = precomputed.dependencies; ctx._entrypoints = precomputed.entrypoints; } else if (manifest) { updateManifest(manifest); } return ctx; } function getModuleDependencies(id, rendererContext) { if (rendererContext._dependencies[id]) { return rendererContext._dependencies[id]; } const dependencies = rendererContext._dependencies[id] = { scripts: {}, styles: {}, preload: {}, prefetch: {} }; if (!rendererContext.manifest) { return dependencies; } const meta = rendererContext.manifest[id]; if (!meta) { return dependencies; } if (meta.file) { dependencies.preload[id] = meta; if (meta.isEntry || meta.sideEffects) { dependencies.scripts[id] = meta; } } for (const css of meta.css || []) { dependencies.styles[css] = dependencies.preload[css] = dependencies.prefetch[css] = rendererContext.manifest[css]; } for (const asset of meta.assets || []) { dependencies.preload[asset] = dependencies.prefetch[asset] = rendererContext.manifest[asset]; } for (const depId of meta.imports || []) { const depDeps = getModuleDependencies(depId, rendererContext); for (const key in depDeps.styles) { dependencies.styles[key] = depDeps.styles[key]; } for (const key in depDeps.preload) { dependencies.preload[key] = depDeps.preload[key]; } for (const key in depDeps.prefetch) { dependencies.prefetch[key] = depDeps.prefetch[key]; } } const filteredPreload = {}; for (const id2 in dependencies.preload) { const dep = dependencies.preload[id2]; if (dep.preload) { filteredPreload[id2] = dep; } } dependencies.preload = filteredPreload; return dependencies; } function getAllDependencies(ids, rendererContext) { let cacheKey = ""; const sortedIds = [...ids].sort(); for (let i = 0; i < sortedIds.length; i++) { if (i > 0) cacheKey += ","; cacheKey += sortedIds[i]; } if (rendererContext._dependencySets[cacheKey]) { return rendererContext._dependencySets[cacheKey]; } const allDeps = { scripts: {}, styles: {}, preload: {}, prefetch: {} }; for (const id of ids) { const deps = getModuleDependencies(id, rendererContext); for (const key in deps.scripts) { allDeps.scripts[key] = deps.scripts[key]; } for (const key in deps.styles) { allDeps.styles[key] = deps.styles[key]; } for (const key in deps.preload) { allDeps.preload[key] = deps.preload[key]; } for (const key in deps.prefetch) { allDeps.prefetch[key] = deps.prefetch[key]; } for (const dynamicDepId of rendererContext.manifest?.[id]?.dynamicImports || []) { const dynamicDeps = getModuleDependencies(dynamicDepId, rendererContext); for (const key in dynamicDeps.scripts) { allDeps.prefetch[key] = dynamicDeps.scripts[key]; } for (const key in dynamicDeps.styles) { allDeps.prefetch[key] = dynamicDeps.styles[key]; } for (const key in dynamicDeps.preload) { allDeps.prefetch[key] = dynamicDeps.preload[key]; } } } const filteredPrefetch = {}; for (const id in allDeps.prefetch) { const dep = allDeps.prefetch[id]; if (dep.prefetch) { filteredPrefetch[id] = dep; } } allDeps.prefetch = filteredPrefetch; for (const id in allDeps.preload) { delete allDeps.prefetch[id]; } for (const style in allDeps.styles) { delete allDeps.preload[style]; delete allDeps.prefetch[style]; } rendererContext._dependencySets[cacheKey] = allDeps; return allDeps; } function getRequestDependencies(ssrContext, rendererContext) { if (ssrContext._requestDependencies) { return ssrContext._requestDependencies; } const ids = new Set(Array.from([ ...rendererContext._entrypoints, ...ssrContext.modules || ssrContext._registeredComponents || [] ])); const deps = getAllDependencies(ids, rendererContext); ssrContext._requestDependencies = deps; return deps; } function renderStyles(ssrContext, rendererContext) { const { styles } = getRequestDependencies(ssrContext, rendererContext); let result = ""; for (const key in styles) { const resource = styles[key]; result += ``; } return result; } function getResources(ssrContext, rendererContext) { return [...getPreloadLinks(ssrContext, rendererContext), ...getPrefetchLinks(ssrContext, rendererContext)]; } function renderResourceHints(ssrContext, rendererContext) { const { preload, prefetch } = getRequestDependencies(ssrContext, rendererContext); let result = ""; for (const key in preload) { const resource = preload[key]; const href = rendererContext.buildAssetsURL(resource.file); const rel = resource.module ? "modulepreload" : "preload"; const crossorigin = resource.resourceType === "style" || resource.resourceType === "font" || resource.resourceType === "script" || resource.module ? " crossorigin" : ""; if (resource.resourceType && resource.mimeType) { result += ``; } else if (resource.resourceType) { result += ``; } else { result += ``; } } for (const key in prefetch) { const resource = prefetch[key]; const href = rendererContext.buildAssetsURL(resource.file); const crossorigin = resource.resourceType === "style" || resource.resourceType === "font" || resource.resourceType === "script" || resource.module ? " crossorigin" : ""; if (resource.resourceType && resource.mimeType) { result += ``; } else if (resource.resourceType) { result += ``; } else { result += ``; } } return result; } function renderResourceHeaders(ssrContext, rendererContext) { const { preload, prefetch } = getRequestDependencies(ssrContext, rendererContext); const links = []; for (const key in preload) { const resource = preload[key]; const href = rendererContext.buildAssetsURL(resource.file); const rel = resource.module ? "modulepreload" : "preload"; let header = `<${href}>; rel="${rel}"`; if (resource.resourceType) { header += `; as="${resource.resourceType}"`; } if (resource.mimeType) { header += `; type="${resource.mimeType}"`; } if (resource.resourceType === "style" || resource.resourceType === "font" || resource.resourceType === "script" || resource.module) { header += "; crossorigin"; } links.push(header); } for (const key in prefetch) { const resource = prefetch[key]; const href = rendererContext.buildAssetsURL(resource.file); let header = `<${href}>; rel="prefetch"`; if (resource.resourceType) { header += `; as="${resource.resourceType}"`; } if (resource.mimeType) { header += `; type="${resource.mimeType}"`; } if (resource.resourceType === "style" || resource.resourceType === "font" || resource.resourceType === "script" || resource.module) { header += "; crossorigin"; } links.push(header); } return { link: links.join(", ") }; } function getPreloadLinks(ssrContext, rendererContext) { const { preload } = getRequestDependencies(ssrContext, rendererContext); const result = []; for (const key in preload) { const resource = preload[key]; result.push({ rel: resource.module ? "modulepreload" : "preload", as: resource.resourceType, type: resource.mimeType ?? null, crossorigin: resource.resourceType === "style" || resource.resourceType === "font" || resource.resourceType === "script" || resource.module ? "" : null, href: rendererContext.buildAssetsURL(resource.file) }); } return result; } function getPrefetchLinks(ssrContext, rendererContext) { const { prefetch } = getRequestDependencies(ssrContext, rendererContext); const result = []; for (const key in prefetch) { const resource = prefetch[key]; result.push({ rel: "prefetch", as: resource.resourceType, type: resource.mimeType ?? null, crossorigin: resource.resourceType === "style" || resource.resourceType === "font" || resource.resourceType === "script" || resource.module ? "" : null, href: rendererContext.buildAssetsURL(resource.file) }); } return result; } function renderScripts(ssrContext, rendererContext) { const { scripts } = getRequestDependencies(ssrContext, rendererContext); let result = ""; for (const key in scripts) { const resource = scripts[key]; if (resource.module) { result += `