summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar James Garbutt <43081j@users.noreply.github.com> 2024-04-01 14:25:20 +0100
committerGravatar GitHub <noreply@github.com> 2024-04-01 21:25:20 +0800
commit0be26d8b10fb19efaa9c1d2f88a4950d9550994a (patch)
tree8ef231c974d776dee363dbd93dba8f03a9fea6d2
parentad50784adc6f262fc563999e97df3a5dc9087c88 (diff)
downloadastro-0be26d8b10fb19efaa9c1d2f88a4950d9550994a.tar.gz
astro-0be26d8b10fb19efaa9c1d2f88a4950d9550994a.tar.zst
astro-0be26d8b10fb19efaa9c1d2f88a4950d9550994a.zip
feat: rework child rendering to use class (#10624)
-rw-r--r--packages/astro/src/runtime/server/render/any.ts5
-rw-r--r--packages/astro/src/runtime/server/render/util.ts79
2 files changed, 51 insertions, 33 deletions
diff --git a/packages/astro/src/runtime/server/render/any.ts b/packages/astro/src/runtime/server/render/any.ts
index 20de2d732..e7e9f0b56 100644
--- a/packages/astro/src/runtime/server/render/any.ts
+++ b/packages/astro/src/runtime/server/render/any.ts
@@ -1,11 +1,14 @@
import { escapeHTML, isHTMLString, markHTMLString } from '../escape.js';
+import { isPromise } from '../util.js';
import { isAstroComponentInstance, isRenderTemplateResult } from './astro/index.js';
import { type RenderDestination, isRenderInstance } from './common.js';
import { SlotString } from './slot.js';
import { renderToBufferDestination } from './util.js';
export async function renderChild(destination: RenderDestination, child: any) {
- child = await child;
+ if (isPromise(child)) {
+ child = await child;
+ }
if (child instanceof SlotString) {
destination.write(child);
} else if (isHTMLString(child)) {
diff --git a/packages/astro/src/runtime/server/render/util.ts b/packages/astro/src/runtime/server/render/util.ts
index 61caff6cc..5e1fff9fc 100644
--- a/packages/astro/src/runtime/server/render/util.ts
+++ b/packages/astro/src/runtime/server/render/util.ts
@@ -149,6 +149,51 @@ export function renderElement(
return `<${name}${internalSpreadAttributes(props, shouldEscape)}>${children}</${name}>`;
}
+const noop = () => {};
+
+/**
+ * Renders into a buffer until `renderToFinalDestination` is called (which
+ * flushes the buffer)
+ */
+class BufferedRenderer implements RenderDestination {
+ private chunks: RenderDestinationChunk[] = [];
+ private renderPromise: Promise<void> | void;
+ private destination?: RenderDestination;
+
+ public constructor(bufferRenderFunction: RenderFunction) {
+ this.renderPromise = bufferRenderFunction(this);
+ // Catch here in case it throws before `renderToFinalDestination` is called,
+ // to prevent an unhandled rejection.
+ Promise.resolve(this.renderPromise).catch(noop);
+ }
+
+ public write(chunk: RenderDestinationChunk): void {
+ if (this.destination) {
+ this.destination.write(chunk);
+ } else {
+ this.chunks.push(chunk);
+ }
+ }
+
+ public async renderToFinalDestination(destination: RenderDestination) {
+ // Write the buffered chunks to the real destination
+ for (const chunk of this.chunks) {
+ destination.write(chunk);
+ }
+
+ // NOTE: We don't empty `this.chunks` after it's written as benchmarks show
+ // that it causes poorer performance, likely due to forced memory re-allocation,
+ // instead of letting the garbage collector handle it automatically.
+ // (Unsure how this affects on limited memory machines)
+
+ // Re-assign the real destination so `instance.render` will continue and write to the new destination
+ this.destination = destination;
+
+ // Wait for render to finish entirely
+ await this.renderPromise;
+ }
+}
+
/**
* Executes the `bufferRenderFunction` to prerender it into a buffer destination, and return a promise
* with an object containing the `renderToFinalDestination` function to flush the buffer to the final
@@ -171,38 +216,8 @@ export function renderElement(
export function renderToBufferDestination(bufferRenderFunction: RenderFunction): {
renderToFinalDestination: RenderFunction;
} {
- // Keep chunks in memory
- const bufferChunks: RenderDestinationChunk[] = [];
- const bufferDestination: RenderDestination = {
- write: (chunk) => bufferChunks.push(chunk),
- };
-
- // Don't await for the render to finish to not block streaming
- const renderPromise = bufferRenderFunction(bufferDestination);
- // Catch here in case it throws before `renderToFinalDestination` is called,
- // to prevent an unhandled rejection.
- Promise.resolve(renderPromise).catch(() => {});
-
- // Return a closure that writes the buffered chunk
- return {
- async renderToFinalDestination(destination) {
- // Write the buffered chunks to the real destination
- for (const chunk of bufferChunks) {
- destination.write(chunk);
- }
-
- // NOTE: We don't empty `bufferChunks` after it's written as benchmarks show
- // that it causes poorer performance, likely due to forced memory re-allocation,
- // instead of letting the garbage collector handle it automatically.
- // (Unsure how this affects on limited memory machines)
-
- // Re-assign the real destination so `instance.render` will continue and write to the new destination
- bufferDestination.write = (chunk) => destination.write(chunk);
-
- // Wait for render to finish entirely
- await renderPromise;
- },
- };
+ const renderer = new BufferedRenderer(bufferRenderFunction);
+ return renderer;
}
export const isNode =