import { AstroError, AstroErrorData } from "../../../../core/errors/index.js"; import { chunkToByteArray, chunkToString, encoder } from "../common.js"; import { isHeadAndContent } from "./head-and-content.js"; import { isRenderTemplateResult } from "./render-template.js"; import { promiseWithResolvers } from "../util.js"; const DOCTYPE_EXP = /" : "\n"; str += doctype; } } if (chunk instanceof Response) return; str += chunkToString(result, chunk); } }; await templateResult.render(destination); return str; } async function renderToReadableStream(result, componentFactory, props, children, isPage = false, route) { const templateResult = await callComponentAsTemplateResultOrResponse( result, componentFactory, props, children, route ); if (templateResult instanceof Response) return templateResult; let renderedFirstPageChunk = false; if (isPage) { await bufferHeadContent(result); } return new ReadableStream({ start(controller) { const destination = { write(chunk) { if (isPage && !renderedFirstPageChunk) { renderedFirstPageChunk = true; if (!result.partial && !DOCTYPE_EXP.test(String(chunk))) { const doctype = result.compressHTML ? "" : "\n"; controller.enqueue(encoder.encode(doctype)); } } if (chunk instanceof Response) { throw new AstroError({ ...AstroErrorData.ResponseSentError }); } const bytes = chunkToByteArray(result, chunk); controller.enqueue(bytes); } }; (async () => { try { await templateResult.render(destination); controller.close(); } catch (e) { if (AstroError.is(e) && !e.loc) { e.setLocation({ file: route?.component }); } setTimeout(() => controller.error(e), 0); } })(); } }); } async function callComponentAsTemplateResultOrResponse(result, componentFactory, props, children, route) { const factoryResult = await componentFactory(result, props, children); if (factoryResult instanceof Response) { return factoryResult; } else if (!isRenderTemplateResult(factoryResult)) { throw new AstroError({ ...AstroErrorData.OnlyResponseCanBeReturned, message: AstroErrorData.OnlyResponseCanBeReturned.message(route?.route, typeof factoryResult), location: { file: route?.component } }); } return isHeadAndContent(factoryResult) ? factoryResult.content : factoryResult; } 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 renderToAsyncIterable(result, componentFactory, props, children, isPage = false, route) { const templateResult = await callComponentAsTemplateResultOrResponse( result, componentFactory, props, children, route ); if (templateResult instanceof Response) return templateResult; let renderedFirstPageChunk = false; if (isPage) { await bufferHeadContent(result); } let error = null; let next = promiseWithResolvers(); const buffer = []; const iterator = { async next() { await next.promise; if (error) { throw error; } let length = 0; for (let i = 0, len = buffer.length; i < len; i++) { length += buffer[i].length; } let mergedArray = new Uint8Array(length); let offset = 0; for (let i = 0, len = buffer.length; i < len; i++) { const item = buffer[i]; mergedArray.set(item, offset); offset += item.length; } buffer.length = 0; const returnValue = { // The iterator is done if there are no chunks to return. done: length === 0, value: mergedArray }; return returnValue; } }; const destination = { write(chunk) { if (isPage && !renderedFirstPageChunk) { renderedFirstPageChunk = true; if (!result.partial && !DOCTYPE_EXP.test(String(chunk))) { const doctype = result.compressHTML ? "" : "\n"; buffer.push(encoder.encode(doctype)); } } if (chunk instanceof Response) { throw new AstroError(AstroErrorData.ResponseSentError); } const bytes = chunkToByteArray(result, chunk); if (bytes.length > 0) { buffer.push(bytes); next.resolve(); next = promiseWithResolvers(); } } }; const renderPromise = templateResult.render(destination); renderPromise.then(() => { next.resolve(); }).catch((err) => { error = err; next.resolve(); }); return { [Symbol.asyncIterator]() { return iterator; } }; } export { renderToAsyncIterable, renderToReadableStream, renderToString };