168 lines
5.6 KiB
JavaScript
168 lines
5.6 KiB
JavaScript
import { AstroError, AstroErrorData } from "../../../core/errors/index.js";
|
|
import { isHTMLString } from "../escape.js";
|
|
import { createResponse } from "../response.js";
|
|
import {
|
|
isAstroComponentFactory,
|
|
isAstroComponentInstance,
|
|
isHeadAndContent,
|
|
isRenderTemplateResult,
|
|
renderAstroTemplateResult
|
|
} from "./astro/index.js";
|
|
import { chunkToByteArray, encoder, HTMLParts } from "./common.js";
|
|
import { renderComponent } from "./component.js";
|
|
import { maybeRenderHead } from "./head.js";
|
|
const needsHeadRenderingSymbol = Symbol.for("astro.needsHeadRendering");
|
|
function nonAstroPageNeedsHeadInjection(pageComponent) {
|
|
return needsHeadRenderingSymbol in pageComponent && !!pageComponent[needsHeadRenderingSymbol];
|
|
}
|
|
async function iterableToHTMLBytes(result, iterable, onDocTypeInjection) {
|
|
const parts = new HTMLParts();
|
|
let i = 0;
|
|
for await (const chunk of iterable) {
|
|
if (isHTMLString(chunk)) {
|
|
if (i === 0) {
|
|
i++;
|
|
if (!/<!doctype html/i.test(String(chunk))) {
|
|
parts.append(`${result.compressHTML ? "<!DOCTYPE html>" : "<!DOCTYPE html>\n"}`, result);
|
|
if (onDocTypeInjection) {
|
|
await onDocTypeInjection(parts);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
parts.append(chunk, result);
|
|
}
|
|
return parts.toArrayBuffer();
|
|
}
|
|
async function bufferHeadContent(result) {
|
|
const iterator = result._metadata.propagators.values();
|
|
while (true) {
|
|
const { value, done } = iterator.next();
|
|
if (done) {
|
|
break;
|
|
}
|
|
const returnValue = await value.init(result);
|
|
if (isHeadAndContent(returnValue)) {
|
|
result._metadata.extraHead.push(returnValue.head);
|
|
}
|
|
}
|
|
}
|
|
async function renderPage(result, componentFactory, props, children, streaming, route) {
|
|
var _a, _b;
|
|
if (!isAstroComponentFactory(componentFactory)) {
|
|
result._metadata.headInTree = ((_a = result.componentMetadata.get(componentFactory.moduleId)) == null ? void 0 : _a.containsHead) ?? false;
|
|
const pageProps = { ...props ?? {}, "server:root": true };
|
|
let output;
|
|
let head = "";
|
|
try {
|
|
if (nonAstroPageNeedsHeadInjection(componentFactory)) {
|
|
const parts = new HTMLParts();
|
|
for await (const chunk of maybeRenderHead()) {
|
|
parts.append(chunk, result);
|
|
}
|
|
head = parts.toString();
|
|
}
|
|
const renderResult = await renderComponent(
|
|
result,
|
|
componentFactory.name,
|
|
componentFactory,
|
|
pageProps,
|
|
null
|
|
);
|
|
if (isAstroComponentInstance(renderResult)) {
|
|
output = renderResult.render();
|
|
} else {
|
|
output = renderResult;
|
|
}
|
|
} catch (e) {
|
|
if (AstroError.is(e) && !e.loc) {
|
|
e.setLocation({
|
|
file: route == null ? void 0 : route.component
|
|
});
|
|
}
|
|
throw e;
|
|
}
|
|
const bytes = await iterableToHTMLBytes(result, output, async (parts) => {
|
|
parts.append(head, result);
|
|
});
|
|
return new Response(bytes, {
|
|
headers: new Headers([
|
|
["Content-Type", "text/html; charset=utf-8"],
|
|
["Content-Length", bytes.byteLength.toString()]
|
|
])
|
|
});
|
|
}
|
|
result._metadata.headInTree = ((_b = result.componentMetadata.get(componentFactory.moduleId)) == null ? void 0 : _b.containsHead) ?? false;
|
|
const factoryReturnValue = await componentFactory(result, props, children);
|
|
const factoryIsHeadAndContent = isHeadAndContent(factoryReturnValue);
|
|
if (isRenderTemplateResult(factoryReturnValue) || factoryIsHeadAndContent) {
|
|
await bufferHeadContent(result);
|
|
const templateResult = factoryIsHeadAndContent ? factoryReturnValue.content : factoryReturnValue;
|
|
let iterable = renderAstroTemplateResult(templateResult);
|
|
let init = result.response;
|
|
let headers = new Headers(init.headers);
|
|
let body;
|
|
if (streaming) {
|
|
body = new ReadableStream({
|
|
start(controller) {
|
|
async function read() {
|
|
let i = 0;
|
|
try {
|
|
for await (const chunk of iterable) {
|
|
if (isHTMLString(chunk)) {
|
|
if (i === 0) {
|
|
if (!/<!doctype html/i.test(String(chunk))) {
|
|
controller.enqueue(
|
|
encoder.encode(
|
|
`${result.compressHTML ? "<!DOCTYPE html>" : "<!DOCTYPE html>\n"}`
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
if (chunk instanceof Response) {
|
|
throw new AstroError({
|
|
...AstroErrorData.ResponseSentError
|
|
});
|
|
}
|
|
const bytes = chunkToByteArray(result, chunk);
|
|
controller.enqueue(bytes);
|
|
i++;
|
|
}
|
|
controller.close();
|
|
} catch (e) {
|
|
if (AstroError.is(e) && !e.loc) {
|
|
e.setLocation({
|
|
file: route == null ? void 0 : route.component
|
|
});
|
|
}
|
|
controller.error(e);
|
|
}
|
|
}
|
|
read();
|
|
}
|
|
});
|
|
} else {
|
|
body = await iterableToHTMLBytes(result, iterable);
|
|
headers.set("Content-Length", body.byteLength.toString());
|
|
}
|
|
let response = createResponse(body, { ...init, headers });
|
|
return response;
|
|
}
|
|
if (!(factoryReturnValue instanceof Response)) {
|
|
throw new AstroError({
|
|
...AstroErrorData.OnlyResponseCanBeReturned,
|
|
message: AstroErrorData.OnlyResponseCanBeReturned.message(
|
|
route == null ? void 0 : route.route,
|
|
typeof factoryReturnValue
|
|
),
|
|
location: {
|
|
file: route == null ? void 0 : route.component
|
|
}
|
|
});
|
|
}
|
|
return factoryReturnValue;
|
|
}
|
|
export {
|
|
renderPage
|
|
};
|