0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2025-02-17 22:44:24 -05:00

feat: rework child rendering to use class (#10624)

This commit is contained in:
James Garbutt 2024-04-01 14:25:20 +01:00 committed by GitHub
parent ad50784adc
commit 0be26d8b10
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 51 additions and 33 deletions

View file

@ -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) {
if (isPromise(child)) {
child = await child;
}
if (child instanceof SlotString) {
destination.write(child);
} else if (isHTMLString(child)) {

View file

@ -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 =