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
 | 
						|
};
 |