UNPKG

astro

Version:

Astro is a modern site builder with web best practices, performance, and DX front-of-mind.

212 lines (211 loc) • 7.72 kB
import { VIRTUAL_PAGE_RESOLVED_MODULE_ID } from "../../vite-plugin-pages/const.js"; import { getVirtualModulePageName } from "../../vite-plugin-pages/util.js"; import { BEFORE_HYDRATION_SCRIPT_ID, PAGE_SCRIPT_ID } from "../../vite-plugin-scripts/index.js"; import { createConsoleLogger } from "../app/entrypoints/index.js"; import { RedirectSinglePageBuiltModule } from "../redirects/component.js"; import { Pipeline } from "../base-pipeline.js"; import { createAssetLink, createStylesheetElementSet } from "../render/ssr-element.js"; import { createDefaultRoutes } from "../routing/default.js"; import { getFallbackRoute, routeIsFallback, routeIsRedirect } from "../routing/helpers.js"; import { findRouteToRewrite } from "../routing/rewrite.js"; import { cssOrder, mergeInlineCss, getPageData } from "./runtime.js"; import { newNodePool } from "../../runtime/server/render/queue/pool.js"; import { HTMLStringCache } from "../../runtime/server/html-string-cache.js"; import { queueRenderingEnabled } from "../app/manifest.js"; class BuildPipeline extends Pipeline { internals; options; manifest; defaultRoutes; getName() { return "BuildPipeline"; } /** * This cache is needed to map a single `RouteData` to its file path. * @private */ #routesByFilePath = /* @__PURE__ */ new WeakMap(); getSettings() { if (!this.options) { throw new Error("No options defined"); } return this.options.settings; } getOptions() { if (!this.options) { throw new Error("No options defined"); } return this.options; } getInternals() { if (!this.internals) { throw new Error("No internals defined"); } return this.internals; } constructor(manifest, defaultRoutes = createDefaultRoutes(manifest)) { const resolveCache = /* @__PURE__ */ new Map(); async function resolve(specifier) { if (resolveCache.has(specifier)) { return resolveCache.get(specifier); } const hashedFilePath = manifest.entryModules[specifier]; if (typeof hashedFilePath !== "string" || hashedFilePath === "") { if (specifier === BEFORE_HYDRATION_SCRIPT_ID) { resolveCache.set(specifier, ""); return ""; } throw new Error(`Cannot find the built path for ${specifier}`); } const assetLink = createAssetLink(hashedFilePath, manifest.base, manifest.assetsPrefix); resolveCache.set(specifier, assetLink); return assetLink; } const logger = createConsoleLogger({ level: manifest.logLevel }); super(logger, manifest, "production", manifest.renderers, resolve, manifest.serverLike); this.manifest = manifest; this.defaultRoutes = defaultRoutes; if (queueRenderingEnabled(this.manifest.experimentalQueuedRendering)) { this.nodePool = newNodePool(this.manifest.experimentalQueuedRendering); if (this.manifest.experimentalQueuedRendering.contentCache) { this.htmlStringCache = new HTMLStringCache(1e3); } } } getRoutes() { return this.getOptions().routesList.routes; } static create({ manifest }) { return new BuildPipeline(manifest); } setInternals(internals) { this.internals = internals; } setOptions(options) { this.options = options; } headElements(routeData) { const { manifest: { assetsPrefix, base } } = this; const settings = this.getSettings(); const internals = this.getInternals(); const links = /* @__PURE__ */ new Set(); const pageBuildData = getPageData(internals, routeData.route, routeData.component); const scripts = /* @__PURE__ */ new Set(); const sortedCssAssets = pageBuildData?.styles.sort(cssOrder).map(({ sheet }) => sheet).reduce(mergeInlineCss, []); const styles = createStylesheetElementSet(sortedCssAssets ?? [], base, assetsPrefix); if (settings.scripts.some((script) => script.stage === "page")) { const hashedFilePath = internals.entrySpecifierToBundleMap.get(PAGE_SCRIPT_ID); if (typeof hashedFilePath !== "string") { throw new Error(`Cannot find the built path for ${PAGE_SCRIPT_ID}`); } const src = createAssetLink(hashedFilePath, base, assetsPrefix); scripts.add({ props: { type: "module", src }, children: "" }); } for (const script of settings.scripts) { if (script.stage === "head-inline") { scripts.add({ props: {}, children: script.content }); } } return { scripts, styles, links }; } componentMetadata() { } /** * It collects the routes to generate during the build. * It returns a map of page information and their relative entry point as a string. */ retrieveRoutesToGenerate() { const pages = /* @__PURE__ */ new Set(); const defaultRouteComponents = new Set(this.defaultRoutes.map((route) => route.component)); for (const { routeData } of this.manifest.routes) { if (routeIsRedirect(routeData)) { pages.add(routeData); continue; } if (routeIsFallback(routeData) && i18nHasFallback(this.manifest)) { pages.add(routeData); continue; } if (defaultRouteComponents.has(routeData.component)) { continue; } pages.add(routeData); const moduleSpecifier = getVirtualModulePageName( VIRTUAL_PAGE_RESOLVED_MODULE_ID, routeData.component ); const filePath = this.internals?.entrySpecifierToBundleMap.get(moduleSpecifier); if (filePath) { this.#routesByFilePath.set(routeData, filePath); } } return pages; } async getComponentByRoute(routeData) { const module = await this.getModuleForRoute(routeData); return module.page(); } async getModuleForRoute(route) { for (const defaultRoute of this.defaultRoutes) { if (route.component === defaultRoute.component) { return { page: () => Promise.resolve(defaultRoute.instance) }; } } let routeToProcess = route; if (routeIsRedirect(route)) { if (route.redirectRoute) { routeToProcess = route.redirectRoute; } else { return RedirectSinglePageBuiltModule; } } else if (routeIsFallback(route)) { routeToProcess = getFallbackRoute(route, this.manifest.routes); } if (this.manifest.pageMap) { const importComponentInstance = this.manifest.pageMap.get(routeToProcess.component); if (!importComponentInstance) { throw new Error( `Unexpectedly unable to find a component instance for route ${route.route}` ); } return await importComponentInstance(); } else if (this.manifest.pageModule) { return this.manifest.pageModule; } throw new Error( "Astro couldn't find the correct page to render, probably because it wasn't correctly mapped for SSR usage. This is an internal error, please file an issue." ); } async tryRewrite(payload, request) { const { routeData, pathname, newUrl } = findRouteToRewrite({ payload, request, routes: this.manifest.routes.map((routeInfo) => routeInfo.routeData), trailingSlash: this.manifest.trailingSlash, buildFormat: this.manifest.buildFormat, base: this.manifest.base, outDir: this.manifest.serverLike ? this.manifest.buildClientDir : this.manifest.outDir }); const componentInstance = await this.getComponentByRoute(routeData); return { routeData, componentInstance, newUrl, pathname }; } } function i18nHasFallback(manifest) { if (manifest.i18n && manifest.i18n.fallback) { return Object.keys(manifest.i18n.fallback).length > 0; } return false; } export { BuildPipeline };