astro
Version:
Astro is a modern site builder with web best practices, performance, and DX front-of-mind.
67 lines (66 loc) • 2.59 kB
JavaScript
import { getEnvironment, setEnvironment } from "../environment/index.js";
import { collectPrerenderMetadata } from "../render-scope/collect.js";
/**
* Makes render errors fail the prerender instead of rendering a 500 page.
*
* Adapter runtimes that prerender with a production app (e.g. the Cloudflare
* prerender worker) default to the production error strategy, which turns a
* render error into a 500 page. Requests handled by the app itself can be
* intercepted through `app.renderError`, but requests handled by a user-provided
* `src/fetch.ts` (`astro(new FetchState(request))`) resolve the error strategy
* from the manifest's environment, so it must be switched there too. Otherwise
* the error is swallowed and an empty page is written to disk.
*
* Only the production (`default`) strategy is replaced; dev and build
* environments already have the right behavior.
*/
function useBuildErrorStrategy(manifest) {
const env = getEnvironment(manifest);
if (env.errorStrategy === "default") {
setEnvironment(manifest, {
...env,
errorStrategy: "build"
});
}
}
/** Statuses the `Response` constructor rejects a body for. */
const NULL_BODY_STATUSES = [
101,
204,
205,
304
];
/**
* Renders a single prerendered path through `app.render`, collecting the
* per-render metadata (content entries rendered, image transforms resolved)
* and returning it by value on the result.
*
* The response body is fully buffered *inside* the render scope so
* lazily-driven rendering work records before the metadata snapshot; the
* returned response is a reconstruction carrying the buffered body with
* status/statusText/headers and body-nullness preserved.
*
* This function performs no scope installation; installing a render scope for
* the rendering runtime is the caller's responsibility. When no scope is
* installed, `metadata` is `undefined` ("not tracked").
*/
export async function renderForPrerender(app, request, options) {
if (app.manifest) {
useBuildErrorStrategy(app.manifest);
}
const routeData = options?.routeData;
const { value: response, metadata } = await collectPrerenderMetadata(async () => {
const rendered = await app.render(request, { routeData });
const bytes = rendered.body === null ? null : await rendered.arrayBuffer();
const nullBody = bytes === null || NULL_BODY_STATUSES.includes(rendered.status);
return new Response(nullBody ? null : bytes, {
status: rendered.status,
statusText: rendered.statusText,
headers: rendered.headers
});
}, app.logger, { staticImages: options?.staticImages });
return {
response,
metadata
};
}