-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
/
page.ts
90 lines (80 loc) · 3.03 KB
/
page.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import type { RouteData, SSRResult } from '../../../@types/astro.js';
import { type NonAstroPageComponent, renderComponentToString } from './component.js';
import type { AstroComponentFactory } from './index.js';
import { isAstroComponentFactory } from './astro/index.js';
import { renderToAsyncIterable, renderToReadableStream, renderToString } from './astro/render.js';
import { encoder } from './common.js';
import { isNode, isDeno } from './util.js';
export async function renderPage(
result: SSRResult,
componentFactory: AstroComponentFactory | NonAstroPageComponent,
props: any,
children: any,
streaming: boolean,
route?: RouteData
): Promise<Response> {
if (!isAstroComponentFactory(componentFactory)) {
result._metadata.headInTree =
result.componentMetadata.get((componentFactory as any).moduleId)?.containsHead ?? false;
const pageProps: Record<string, any> = { ...(props ?? {}), 'server:root': true };
const str = await renderComponentToString(
result,
componentFactory.name,
componentFactory,
pageProps,
{},
true,
route
);
const bytes = encoder.encode(str);
return new Response(bytes, {
headers: new Headers([
['Content-Type', 'text/html; charset=utf-8'],
['Content-Length', bytes.byteLength.toString()],
]),
});
}
// Mark if this page component contains a <head> within its tree. If it does
// We avoid implicit head injection entirely.
result._metadata.headInTree =
result.componentMetadata.get(componentFactory.moduleId!)?.containsHead ?? false;
let body: BodyInit | Response;
if (streaming) {
// isNode is true in Deno node-compat mode but response construction from
// async iterables is not supported, so we fallback to ReadableStream if isDeno is true.
if (isNode && !isDeno) {
const nodeBody = await renderToAsyncIterable(
result,
componentFactory,
props,
children,
true,
route
);
// Node.js allows passing in an AsyncIterable to the Response constructor.
// This is non-standard so using `any` here to preserve types everywhere else.
body = nodeBody as any;
} else {
body = await renderToReadableStream(result, componentFactory, props, children, true, route);
}
} else {
body = await renderToString(result, componentFactory, props, children, true, route);
}
// If the Astro component returns a Response on init, return that response
if (body instanceof Response) return body;
// Create final response from body
const init = result.response;
const headers = new Headers(init.headers);
// For non-streaming, convert string to byte array to calculate Content-Length
if (!streaming && typeof body === 'string') {
body = encoder.encode(body);
headers.set('Content-Length', body.byteLength.toString());
}
// TODO: Revisit if user should manually set charset by themselves in Astro 4
// This code preserves the existing behaviour for markdown pages since Astro 2
if (route?.component.endsWith('.md')) {
headers.set('Content-Type', 'text/html; charset=utf-8');
}
const response = new Response(body, { ...init, headers });
return response;
}