astro-ghostcms/.pnpm-store/v3/files/21/a9893acb4dd21931a6c70616640...

495 lines
18 KiB
Plaintext

import { bgGreen, black, blue, bold, dim, green, magenta, red } from "kleur/colors";
import fs from "node:fs";
import os from "node:os";
import { fileURLToPath } from "node:url";
import PQueue from "p-queue";
import {
generateImagesForPath,
getStaticImageList,
prepareAssetsGenerationEnv
} from "../../assets/build/generate.js";
import { hasPrerenderedPages } from "../../core/build/internal.js";
import {
isRelativePath,
joinPaths,
prependForwardSlash,
removeLeadingForwardSlash,
removeTrailingForwardSlash
} from "../../core/path.js";
import { createI18nMiddleware, i18nPipelineHook } from "../../i18n/middleware.js";
import { runHookBuildGenerated } from "../../integrations/index.js";
import { getOutputDirectory, isServerLikeOutput } from "../../prerender/utils.js";
import { PAGE_SCRIPT_ID } from "../../vite-plugin-scripts/index.js";
import { AstroError, AstroErrorData } from "../errors/index.js";
import { sequence } from "../middleware/index.js";
import { routeIsFallback } from "../redirects/helpers.js";
import {
RedirectSinglePageBuiltModule,
getRedirectLocationOrThrow,
routeIsRedirect
} from "../redirects/index.js";
import { createRenderContext } from "../render/index.js";
import { callGetStaticPaths } from "../render/route-cache.js";
import {
createAssetLink,
createModuleScriptsSet,
createStylesheetElementSet
} from "../render/ssr-element.js";
import { createRequest } from "../request.js";
import { matchRoute } from "../routing/match.js";
import { getOutputFilename } from "../util.js";
import { BuildPipeline } from "./buildPipeline.js";
import { getOutDirWithinCwd, getOutFile, getOutFolder } from "./common.js";
import {
cssOrder,
getEntryFilePathFromComponentPath,
getPageDataByComponent,
mergeInlineCss
} from "./internal.js";
import { getTimeStat, shouldAppendForwardSlash } from "./util.js";
function createEntryURL(filePath, outFolder) {
return new URL("./" + filePath + `?time=${Date.now()}`, outFolder);
}
async function getEntryForRedirectRoute(route, internals, outFolder) {
if (route.type !== "redirect") {
throw new Error(`Expected a redirect route.`);
}
if (route.redirectRoute) {
const filePath = getEntryFilePathFromComponentPath(internals, route.redirectRoute.component);
if (filePath) {
const url = createEntryURL(filePath, outFolder);
const ssrEntryPage = await import(url.toString());
return ssrEntryPage;
}
}
return RedirectSinglePageBuiltModule;
}
async function getEntryForFallbackRoute(route, internals, outFolder) {
if (route.type !== "fallback") {
throw new Error(`Expected a redirect route.`);
}
if (route.redirectRoute) {
const filePath = getEntryFilePathFromComponentPath(internals, route.redirectRoute.component);
if (filePath) {
const url = createEntryURL(filePath, outFolder);
const ssrEntryPage = await import(url.toString());
return ssrEntryPage;
}
}
return RedirectSinglePageBuiltModule;
}
function rootRelativeFacadeId(facadeId, settings) {
return facadeId.slice(fileURLToPath(settings.config.root).length);
}
function chunkIsPage(settings, output, internals) {
if (output.type !== "chunk") {
return false;
}
const chunk = output;
if (chunk.facadeModuleId) {
const facadeToEntryId = prependForwardSlash(
rootRelativeFacadeId(chunk.facadeModuleId, settings)
);
return internals.entrySpecifierToBundleMap.has(facadeToEntryId);
}
return false;
}
async function generatePages(opts, internals) {
const generatePagesTimer = performance.now();
const ssr = isServerLikeOutput(opts.settings.config);
let manifest;
if (ssr) {
manifest = await BuildPipeline.retrieveManifest(opts, internals);
} else {
const baseDirectory = getOutputDirectory(opts.settings.config);
const renderersEntryUrl = new URL("renderers.mjs", baseDirectory);
const renderers = await import(renderersEntryUrl.toString());
manifest = createBuildManifest(
opts.settings,
internals,
renderers.renderers
);
}
const pipeline = new BuildPipeline(opts, internals, manifest);
const outFolder = ssr ? opts.settings.config.build.server : getOutDirWithinCwd(opts.settings.config.outDir);
const logger = pipeline.getLogger();
if (ssr && !hasPrerenderedPages(internals)) {
delete globalThis?.astroAsset?.addStaticImage;
return;
}
const verb = ssr ? "prerendering" : "generating";
logger.info("SKIP_FORMAT", `
${bgGreen(black(` ${verb} static routes `))}`);
const builtPaths = /* @__PURE__ */ new Set();
const pagesToGenerate = pipeline.retrieveRoutesToGenerate();
if (ssr) {
for (const [pageData, filePath] of pagesToGenerate) {
if (pageData.route.prerender) {
const ssrEntryURLPage = createEntryURL(filePath, outFolder);
const ssrEntryPage = await import(ssrEntryURLPage.toString());
if (opts.settings.adapter?.adapterFeatures?.functionPerRoute) {
const ssrEntry = ssrEntryPage?.pageModule;
if (ssrEntry) {
await generatePage(pageData, ssrEntry, builtPaths, pipeline);
} else {
throw new Error(
`Unable to find the manifest for the module ${ssrEntryURLPage.toString()}. This is unexpected and likely a bug in Astro, please report.`
);
}
} else {
const ssrEntry = ssrEntryPage;
await generatePage(pageData, ssrEntry, builtPaths, pipeline);
}
}
}
} else {
for (const [pageData, filePath] of pagesToGenerate) {
if (routeIsRedirect(pageData.route)) {
const entry = await getEntryForRedirectRoute(pageData.route, internals, outFolder);
await generatePage(pageData, entry, builtPaths, pipeline);
} else if (routeIsFallback(pageData.route)) {
const entry = await getEntryForFallbackRoute(pageData.route, internals, outFolder);
await generatePage(pageData, entry, builtPaths, pipeline);
} else {
const ssrEntryURLPage = createEntryURL(filePath, outFolder);
const entry = await import(ssrEntryURLPage.toString());
await generatePage(pageData, entry, builtPaths, pipeline);
}
}
}
logger.info(
null,
green(`\u2713 Completed in ${getTimeStat(generatePagesTimer, performance.now())}.
`)
);
const staticImageList = getStaticImageList();
if (staticImageList.size) {
logger.info("SKIP_FORMAT", `${bgGreen(black(` generating optimized images `))}`);
const totalCount = Array.from(staticImageList.values()).map((x) => x.transforms.size).reduce((a, b) => a + b, 0);
const cpuCount = os.cpus().length;
const assetsCreationEnvironment = await prepareAssetsGenerationEnv(pipeline, totalCount);
const queue = new PQueue({ concurrency: Math.max(cpuCount, 1) });
const assetsTimer = performance.now();
for (const [originalPath, transforms] of staticImageList) {
await generateImagesForPath(originalPath, transforms, assetsCreationEnvironment, queue);
}
await queue.onIdle();
const assetsTimeEnd = performance.now();
logger.info(null, green(`\u2713 Completed in ${getTimeStat(assetsTimer, assetsTimeEnd)}.
`));
delete globalThis?.astroAsset?.addStaticImage;
}
await runHookBuildGenerated({
config: opts.settings.config,
logger: pipeline.getLogger()
});
}
async function generatePage(pageData, ssrEntry, builtPaths, pipeline) {
const logger = pipeline.getLogger();
const config = pipeline.getConfig();
const pageModulePromise = ssrEntry.page;
const onRequest = ssrEntry.onRequest;
const pageInfo = getPageDataByComponent(pipeline.getInternals(), pageData.route.component);
const styles = pageData.styles.sort(cssOrder).map(({ sheet }) => sheet).reduce(mergeInlineCss, []);
const linkIds = [];
const scripts = pageInfo?.hoistedScript ?? null;
const i18nMiddleware = createI18nMiddleware(
pipeline.getManifest().i18n,
pipeline.getManifest().base,
pipeline.getManifest().trailingSlash,
pipeline.getManifest().buildFormat
);
if (config.i18n && i18nMiddleware) {
if (onRequest) {
pipeline.setMiddlewareFunction(sequence(i18nMiddleware, onRequest));
} else {
pipeline.setMiddlewareFunction(i18nMiddleware);
}
pipeline.onBeforeRenderRoute(i18nPipelineHook);
} else if (onRequest) {
pipeline.setMiddlewareFunction(onRequest);
}
if (!pageModulePromise) {
throw new Error(
`Unable to find the module for ${pageData.component}. This is unexpected and likely a bug in Astro, please report.`
);
}
const pageModule = await pageModulePromise();
const generationOptions = {
pageData,
linkIds,
scripts,
styles,
mod: pageModule
};
for (const route of eachRouteInRouteData(pageData)) {
const icon = route.type === "page" || route.type === "redirect" || route.type === "fallback" ? green("\u25B6") : magenta("\u03BB");
logger.info(null, `${icon} ${getPrettyRouteName(route)}`);
const paths = await getPathsForRoute(route, pageModule, pipeline, builtPaths);
let timeStart = performance.now();
let prevTimeEnd = timeStart;
for (let i = 0; i < paths.length; i++) {
const path = paths[i];
pipeline.getEnvironment().logger.debug("build", `Generating: ${path}`);
const filePath = getOutputFilename(pipeline.getConfig(), path, pageData.route.type);
const lineIcon = i === paths.length - 1 ? "\u2514\u2500" : "\u251C\u2500";
logger.info(null, ` ${blue(lineIcon)} ${dim(filePath)}`, false);
await generatePath(path, pipeline, generationOptions, route);
const timeEnd = performance.now();
const timeChange = getTimeStat(prevTimeEnd, timeEnd);
const timeIncrease = `(+${timeChange})`;
logger.info("SKIP_FORMAT", ` ${dim(timeIncrease)}`);
prevTimeEnd = timeEnd;
}
}
}
function* eachRouteInRouteData(data) {
yield data.route;
for (const fallbackRoute of data.route.fallbackRoutes) {
yield fallbackRoute;
}
}
async function getPathsForRoute(route, mod, pipeline, builtPaths) {
const opts = pipeline.getStaticBuildOptions();
const logger = pipeline.getLogger();
let paths = [];
if (route.pathname) {
paths.push(route.pathname);
builtPaths.add(route.pathname);
for (const virtualRoute of route.fallbackRoutes) {
if (virtualRoute.pathname) {
paths.push(virtualRoute.pathname);
builtPaths.add(virtualRoute.pathname);
}
}
} else {
const staticPaths = await callGetStaticPaths({
mod,
route,
routeCache: opts.routeCache,
logger,
ssr: isServerLikeOutput(opts.settings.config)
}).catch((err) => {
logger.debug("build", `\u251C\u2500\u2500 ${bold(red("\u2717"))} ${route.component}`);
throw err;
});
const label = staticPaths.length === 1 ? "page" : "pages";
logger.debug(
"build",
`\u251C\u2500\u2500 ${bold(green("\u2714"))} ${route.component} \u2192 ${magenta(`[${staticPaths.length} ${label}]`)}`
);
paths = staticPaths.map((staticPath) => {
try {
return route.generate(staticPath.params);
} catch (e) {
if (e instanceof TypeError) {
throw getInvalidRouteSegmentError(e, route, staticPath);
}
throw e;
}
}).filter((staticPath) => {
if (!builtPaths.has(removeTrailingForwardSlash(staticPath))) {
return true;
}
const matchedRoute = matchRoute(staticPath, opts.manifest);
return matchedRoute === route;
});
for (const staticPath of paths) {
builtPaths.add(removeTrailingForwardSlash(staticPath));
}
}
return paths;
}
function getInvalidRouteSegmentError(e, route, staticPath) {
const invalidParam = e.message.match(/^Expected "([^"]+)"/)?.[1];
const received = invalidParam ? staticPath.params[invalidParam] : void 0;
let hint = "Learn about dynamic routes at https://docs.astro.build/en/core-concepts/routing/#dynamic-routes";
if (invalidParam && typeof received === "string") {
const matchingSegment = route.segments.find(
(segment) => segment[0]?.content === invalidParam
)?.[0];
const mightBeMissingSpread = matchingSegment?.dynamic && !matchingSegment?.spread;
if (mightBeMissingSpread) {
hint = `If the param contains slashes, try using a rest parameter: **[...${invalidParam}]**. Learn more at https://docs.astro.build/en/core-concepts/routing/#dynamic-routes`;
}
}
return new AstroError({
...AstroErrorData.InvalidDynamicRoute,
message: invalidParam ? AstroErrorData.InvalidDynamicRoute.message(
route.route,
JSON.stringify(invalidParam),
JSON.stringify(received)
) : `Generated path for ${route.route} is invalid.`,
hint
});
}
function addPageName(pathname, opts) {
const trailingSlash = opts.settings.config.trailingSlash;
const buildFormat = opts.settings.config.build.format;
const pageName = shouldAppendForwardSlash(trailingSlash, buildFormat) ? pathname.replace(/\/?$/, "/").replace(/^\//, "") : pathname.replace(/^\//, "");
opts.pageNames.push(pageName);
}
function getUrlForPath(pathname, base, origin, format, routeType) {
const ending = format === "directory" ? "/" : ".html";
let buildPathname;
if (pathname === "/" || pathname === "") {
buildPathname = base;
} else if (routeType === "endpoint") {
const buildPathRelative = removeLeadingForwardSlash(pathname);
buildPathname = joinPaths(base, buildPathRelative);
} else {
const buildPathRelative = removeTrailingForwardSlash(removeLeadingForwardSlash(pathname)) + ending;
buildPathname = joinPaths(base, buildPathRelative);
}
const url = new URL(buildPathname, origin);
return url;
}
async function generatePath(pathname, pipeline, gopts, route) {
const { mod, scripts: hoistedScripts, styles: _styles } = gopts;
const manifest = pipeline.getManifest();
pipeline.getEnvironment().logger.debug("build", `Generating: ${pathname}`);
const links = /* @__PURE__ */ new Set();
const scripts = createModuleScriptsSet(
hoistedScripts ? [hoistedScripts] : [],
manifest.base,
manifest.assetsPrefix
);
const styles = createStylesheetElementSet(_styles, manifest.base, manifest.assetsPrefix);
if (pipeline.getSettings().scripts.some((script) => script.stage === "page")) {
const hashedFilePath = pipeline.getInternals().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, manifest.base, manifest.assetsPrefix);
scripts.add({
props: { type: "module", src },
children: ""
});
}
for (const script of pipeline.getSettings().scripts) {
if (script.stage === "head-inline") {
scripts.add({
props: {},
children: script.content
});
}
}
if (route.type === "page") {
addPageName(pathname, pipeline.getStaticBuildOptions());
}
const ssr = isServerLikeOutput(pipeline.getConfig());
const url = getUrlForPath(
pathname,
pipeline.getConfig().base,
pipeline.getStaticBuildOptions().origin,
pipeline.getConfig().build.format,
route.type
);
const request = createRequest({
url,
headers: new Headers(),
logger: pipeline.getLogger(),
ssr
});
const i18n = pipeline.getConfig().i18n;
const renderContext = await createRenderContext({
pathname,
request,
componentMetadata: manifest.componentMetadata,
scripts,
styles,
links,
route,
env: pipeline.getEnvironment(),
mod,
locales: i18n?.locales,
routing: i18n?.routing,
defaultLocale: i18n?.defaultLocale
});
let body;
let response;
try {
response = await pipeline.renderRoute(renderContext, mod);
} catch (err) {
if (!AstroError.is(err) && !err.id && typeof err === "object") {
err.id = route.component;
}
throw err;
}
if (response.status >= 300 && response.status < 400) {
if (!pipeline.getConfig().build.redirects) {
return;
}
const locationSite = getRedirectLocationOrThrow(response.headers);
const siteURL = pipeline.getConfig().site;
const location = siteURL ? new URL(locationSite, siteURL) : locationSite;
const fromPath = new URL(renderContext.request.url).pathname;
const delay = response.status === 302 ? 2 : 0;
body = `<!doctype html>
<title>Redirecting to: ${location}</title>
<meta http-equiv="refresh" content="${delay};url=${location}">
<meta name="robots" content="noindex">
<link rel="canonical" href="${location}">
<body>
<a href="${location}">Redirecting from <code>${fromPath}</code> to <code>${location}</code></a>
</body>`;
if (pipeline.getConfig().compressHTML === true) {
body = body.replaceAll("\n", "");
}
if (route.type !== "redirect") {
route.redirect = location.toString();
}
} else {
if (!response.body)
return;
body = Buffer.from(await response.arrayBuffer());
}
const outFolder = getOutFolder(pipeline.getConfig(), pathname, route.type);
const outFile = getOutFile(pipeline.getConfig(), outFolder, pathname, route.type);
route.distURL = outFile;
await fs.promises.mkdir(outFolder, { recursive: true });
await fs.promises.writeFile(outFile, body);
}
function getPrettyRouteName(route) {
if (isRelativePath(route.component)) {
return route.route;
} else if (route.component.includes("node_modules/")) {
return route.component.match(/.*node_modules\/(.+)/)?.[1] ?? route.component;
} else {
return route.component;
}
}
function createBuildManifest(settings, internals, renderers) {
let i18nManifest = void 0;
if (settings.config.i18n) {
i18nManifest = {
fallback: settings.config.i18n.fallback,
routing: settings.config.i18n.routing,
defaultLocale: settings.config.i18n.defaultLocale,
locales: settings.config.i18n.locales
};
}
return {
trailingSlash: settings.config.trailingSlash,
assets: /* @__PURE__ */ new Set(),
entryModules: Object.fromEntries(internals.entrySpecifierToBundleMap.entries()),
routes: [],
adapterName: "",
clientDirectives: settings.clientDirectives,
compressHTML: settings.config.compressHTML,
renderers,
base: settings.config.base,
assetsPrefix: settings.config.build.assetsPrefix,
site: settings.config.site ? new URL(settings.config.base, settings.config.site).toString() : settings.config.site,
componentMetadata: internals.componentMetadata,
i18n: i18nManifest,
buildFormat: settings.config.build.format
};
}
export {
chunkIsPage,
createBuildManifest,
generatePages,
rootRelativeFacadeId
};