mirror of
https://github.com/withastro/astro.git
synced 2024-12-16 21:46:22 -05:00
wip
This commit is contained in:
parent
8f064fa5b9
commit
a1dc4948fc
3 changed files with 55 additions and 10 deletions
|
@ -125,7 +125,7 @@ export async function renderToReadableStream(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Queue error on next microtask to flush the remaining chunks written synchronously
|
// Queue error on next microtask to flush the remaining chunks written synchronously
|
||||||
setTimeout(() => controller.error(e), 0);
|
queueMicrotask(() => controller.error(e));
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
},
|
},
|
||||||
|
@ -229,9 +229,12 @@ export async function renderToAsyncIterable(
|
||||||
// The `next` is an object `{ promise, resolve, reject }` that we use to wait
|
// The `next` is an object `{ promise, resolve, reject }` that we use to wait
|
||||||
// for chunks to be pushed into the buffer.
|
// for chunks to be pushed into the buffer.
|
||||||
let next: ReturnType<typeof promiseWithResolvers<void>> | null = null;
|
let next: ReturnType<typeof promiseWithResolvers<void>> | null = null;
|
||||||
const buffer: Uint8Array[] = []; // []Uint8Array
|
const buffer: Uint8Array[] = [];
|
||||||
let renderingComplete = false;
|
let renderingComplete = false;
|
||||||
|
|
||||||
|
const BATCH_SIZE = 1024 * 1;
|
||||||
|
let currentBatchSize = 0;
|
||||||
|
|
||||||
const iterator: AsyncIterator<Uint8Array> = {
|
const iterator: AsyncIterator<Uint8Array> = {
|
||||||
async next() {
|
async next() {
|
||||||
if (result.cancelled) return { done: true, value: undefined };
|
if (result.cancelled) return { done: true, value: undefined };
|
||||||
|
@ -271,8 +274,9 @@ export async function renderToAsyncIterable(
|
||||||
offset += item.length;
|
offset += item.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty the array. We do this so that we can reuse the same array.
|
// Empty the buffer and reset the batch size
|
||||||
buffer.length = 0;
|
buffer.length = 0;
|
||||||
|
currentBatchSize = 0; // **Reset batch size after sending**
|
||||||
|
|
||||||
const returnValue = {
|
const returnValue = {
|
||||||
// The iterator is done when rendering has finished
|
// The iterator is done when rendering has finished
|
||||||
|
@ -297,7 +301,9 @@ export async function renderToAsyncIterable(
|
||||||
renderedFirstPageChunk = true;
|
renderedFirstPageChunk = true;
|
||||||
if (!result.partial && !DOCTYPE_EXP.test(String(chunk))) {
|
if (!result.partial && !DOCTYPE_EXP.test(String(chunk))) {
|
||||||
const doctype = result.compressHTML ? '<!DOCTYPE html>' : '<!DOCTYPE html>\n';
|
const doctype = result.compressHTML ? '<!DOCTYPE html>' : '<!DOCTYPE html>\n';
|
||||||
buffer.push(encoder.encode(doctype));
|
const doctypeBytes = encoder.encode(doctype);
|
||||||
|
buffer.push(doctypeBytes);
|
||||||
|
currentBatchSize += doctypeBytes.length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (chunk instanceof Response) {
|
if (chunk instanceof Response) {
|
||||||
|
@ -310,7 +316,13 @@ export async function renderToAsyncIterable(
|
||||||
// Push the chunks into the buffer and resolve the promise so that next()
|
// Push the chunks into the buffer and resolve the promise so that next()
|
||||||
// will run.
|
// will run.
|
||||||
buffer.push(bytes);
|
buffer.push(bytes);
|
||||||
next?.resolve();
|
currentBatchSize += bytes.length;
|
||||||
|
|
||||||
|
// Check if batch size threshold is reached
|
||||||
|
if (currentBatchSize >= BATCH_SIZE) {
|
||||||
|
next?.resolve();
|
||||||
|
// Note: Do not reset currentBatchSize here; it will be reset in `next()`
|
||||||
|
}
|
||||||
} else if (buffer.length > 0) {
|
} else if (buffer.length > 0) {
|
||||||
next?.resolve();
|
next?.resolve();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,28 @@ import { renderToAsyncIterable, renderToReadableStream, renderToString } from '.
|
||||||
import { encoder } from './common.js';
|
import { encoder } from './common.js';
|
||||||
import { isDeno, isNode } from './util.js';
|
import { isDeno, isNode } from './util.js';
|
||||||
|
|
||||||
|
function readableStreamFromAsyncIterable(
|
||||||
|
asyncIterable: AsyncIterable<Uint8Array>,
|
||||||
|
) {
|
||||||
|
const iterator = asyncIterable[Symbol.asyncIterator]();
|
||||||
|
return new ReadableStream(
|
||||||
|
{
|
||||||
|
async pull(controller) {
|
||||||
|
try {
|
||||||
|
const { value, done } = await iterator.next();
|
||||||
|
if (done) {
|
||||||
|
controller.close();
|
||||||
|
} else {
|
||||||
|
controller.enqueue(value);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
controller.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export async function renderPage(
|
export async function renderPage(
|
||||||
result: SSRResult,
|
result: SSRResult,
|
||||||
componentFactory: AstroComponentFactory | NonAstroPageComponent,
|
componentFactory: AstroComponentFactory | NonAstroPageComponent,
|
||||||
|
@ -59,9 +81,7 @@ export async function renderPage(
|
||||||
true,
|
true,
|
||||||
route,
|
route,
|
||||||
);
|
);
|
||||||
// Node.js allows passing in an AsyncIterable to the Response constructor.
|
body = nodeBody instanceof Response ? nodeBody : readableStreamFromAsyncIterable(nodeBody);
|
||||||
// This is non-standard so using `any` here to preserve types everywhere else.
|
|
||||||
body = nodeBody as any;
|
|
||||||
} else {
|
} else {
|
||||||
body = await renderToReadableStream(result, componentFactory, props, children, true, route);
|
body = await renderToReadableStream(result, componentFactory, props, children, true, route);
|
||||||
}
|
}
|
||||||
|
|
|
@ -232,9 +232,22 @@ export type PromiseWithResolvers<T> = {
|
||||||
reject: (reason?: any) => void;
|
reject: (reason?: any) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
// This is an implementation of Promise.withResolvers(), which we can't yet rely on.
|
interface PromiseConstructorWithResolvers<T> extends PromiseConstructor {
|
||||||
// We can remove this once the native function is available in Node.js
|
withResolvers: () => PromiseWithResolvers<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasWithResolvers<T>(
|
||||||
|
promiseConstructor: PromiseConstructor | PromiseConstructorWithResolvers<T>,
|
||||||
|
): promiseConstructor is PromiseConstructorWithResolvers<T> {
|
||||||
|
return 'withResolvers' in promiseConstructor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is an implementation of Promise.withResolvers(), which was added in Node v22
|
||||||
|
// We can remove this once we drop support for Node <22.0.0
|
||||||
export function promiseWithResolvers<T = any>(): PromiseWithResolvers<T> {
|
export function promiseWithResolvers<T = any>(): PromiseWithResolvers<T> {
|
||||||
|
if (hasWithResolvers<T>(Promise)) {
|
||||||
|
return Promise.withResolvers();
|
||||||
|
}
|
||||||
let resolve: any, reject: any;
|
let resolve: any, reject: any;
|
||||||
const promise = new Promise<T>((_resolve, _reject) => {
|
const promise = new Promise<T>((_resolve, _reject) => {
|
||||||
resolve = _resolve;
|
resolve = _resolve;
|
||||||
|
|
Loading…
Reference in a new issue