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:
parent
ad50784adc
commit
0be26d8b10
2 changed files with 51 additions and 33 deletions
|
@ -1,11 +1,14 @@
|
||||||
import { escapeHTML, isHTMLString, markHTMLString } from '../escape.js';
|
import { escapeHTML, isHTMLString, markHTMLString } from '../escape.js';
|
||||||
|
import { isPromise } from '../util.js';
|
||||||
import { isAstroComponentInstance, isRenderTemplateResult } from './astro/index.js';
|
import { isAstroComponentInstance, isRenderTemplateResult } from './astro/index.js';
|
||||||
import { type RenderDestination, isRenderInstance } from './common.js';
|
import { type RenderDestination, isRenderInstance } from './common.js';
|
||||||
import { SlotString } from './slot.js';
|
import { SlotString } from './slot.js';
|
||||||
import { renderToBufferDestination } from './util.js';
|
import { renderToBufferDestination } from './util.js';
|
||||||
|
|
||||||
export async function renderChild(destination: RenderDestination, child: any) {
|
export async function renderChild(destination: RenderDestination, child: any) {
|
||||||
|
if (isPromise(child)) {
|
||||||
child = await child;
|
child = await child;
|
||||||
|
}
|
||||||
if (child instanceof SlotString) {
|
if (child instanceof SlotString) {
|
||||||
destination.write(child);
|
destination.write(child);
|
||||||
} else if (isHTMLString(child)) {
|
} else if (isHTMLString(child)) {
|
||||||
|
|
|
@ -149,6 +149,51 @@ export function renderElement(
|
||||||
return `<${name}${internalSpreadAttributes(props, shouldEscape)}>${children}</${name}>`;
|
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
|
* 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
|
* 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): {
|
export function renderToBufferDestination(bufferRenderFunction: RenderFunction): {
|
||||||
renderToFinalDestination: RenderFunction;
|
renderToFinalDestination: RenderFunction;
|
||||||
} {
|
} {
|
||||||
// Keep chunks in memory
|
const renderer = new BufferedRenderer(bufferRenderFunction);
|
||||||
const bufferChunks: RenderDestinationChunk[] = [];
|
return renderer;
|
||||||
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;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isNode =
|
export const isNode =
|
||||||
|
|
Loading…
Add table
Reference in a new issue