Skip to content

Commit

Permalink
Address code review comments
Browse files Browse the repository at this point in the history
  • Loading branch information
MatthewLymer committed Feb 10, 2025
1 parent 50b4a7e commit fe82744
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 28 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { markHTMLString } from '../../escape.js';
import { isPromise } from '../../util.js';
import { renderChild } from '../any.js';
import type { RenderDestination, } from '../common.js';
import type { RenderDestination } from '../common.js';
import { createBufferedRenderer } from '../util.js';

const renderTemplateResultSym = Symbol.for('astro.renderTemplateResult');
Expand Down
26 changes: 4 additions & 22 deletions packages/astro/src/runtime/server/render/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ function renderAstroComponent(
const instance = createAstroComponentInstance(result, displayName, Component, props, slots);

return {
render(destination) {
render(destination: RenderDestination): Promise<void> | void {
// NOTE: This render call can't be pre-invoked outside of this function as it'll also initialize the slots
// recursively, which causes each Astro components in the tree to be called bottom-up, and is incorrect.
// The slots are initialized eagerly for head propagation.
Expand All @@ -465,29 +465,11 @@ export function renderComponent(
slots: ComponentSlots = {},
): RenderInstance | Promise<RenderInstance> {
if (isPromise(Component)) {
return Component.catch(handleCancellation).then((x) =>
renderComponentImplFast(result, displayName, x, props, slots),
);
}

return renderComponentImplFast(result, displayName, Component, props, slots);

function handleCancellation(e: unknown) {
if (result.cancelled)
return {
render() {},
};
throw e;
return Component.catch(handleCancellation).then((x) => {
return renderComponent(result, displayName, x, props, slots);
});
}
}

function renderComponentImplFast(
result: SSRResult,
displayName: string,
Component: unknown,
props: Record<string | number, any>,
slots: ComponentSlots = {},
): RenderInstance | Promise<RenderInstance> {
if (isFragmentComponent(Component)) {
return renderFragmentComponent(result, slots).catch(handleCancellation);
}
Expand Down
25 changes: 20 additions & 5 deletions packages/astro/src/runtime/server/render/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,18 @@ export function renderElement(
const noop = () => {};

/**
* Renders into a buffer until `renderToFinalDestination` is called (which
* Renders into a buffer until `flush` is called (which
* flushes the buffer)
*/
class BufferedRenderer implements RenderDestination, RendererFlusher {
private chunks: RenderDestinationChunk[] = [];
private renderPromise: Promise<void> | void;
private destination: RenderDestination;

/**
* Determines whether buffer has been flushed
* to the final destination.
*/
private flushed = false;

public constructor(destination: RenderDestination, renderFunction: RenderFunction) {
Expand All @@ -174,6 +179,11 @@ class BufferedRenderer implements RenderDestination, RendererFlusher {
}

public write(chunk: RenderDestinationChunk): void {
// Before the buffer has been flushed, we want to
// append to the buffer, afterwards we'll write
// to the underlying destination if subsequent
// writes arrive.

if (this.flushed) {
this.destination.write(chunk);
} else {
Expand All @@ -183,7 +193,7 @@ class BufferedRenderer implements RenderDestination, RendererFlusher {

public flush(): void | Promise<void> {
if (this.flushed) {
throw new Error('Already been flushed.');
throw new Error('The render buffer has already been flushed.');
}

this.flushed = true;
Expand All @@ -204,20 +214,20 @@ class BufferedRenderer implements RenderDestination, RendererFlusher {

/**
* 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 `flush` function to flush the buffer to the final
* destination.
*
* @example
* ```ts
* // Render components in parallel ahead of time
* const finalRenders = [ComponentA, ComponentB].map((comp) => {
* return renderToBufferDestination(async (bufferDestination) => {
* return createBufferedRenderer(finalDestination, async (bufferDestination) => {
* await renderComponentToDestination(bufferDestination);
* });
* });
* // Render array of components serially
* for (const finalRender of finalRenders) {
* await finalRender.renderToFinalDestination(finalDestination);
* await finalRender.flush();
* }
* ```
*/
Expand All @@ -229,6 +239,11 @@ export function createBufferedRenderer(
}

export interface RendererFlusher {
/**
* Flushes the current renderer to the underlying renderer.
*
* See example of `createBufferedRenderer` for usage.
*/
flush(): void | Promise<void>;
}

Expand Down

0 comments on commit fe82744

Please sign in to comment.