136 lines
5.7 KiB
Plaintext
136 lines
5.7 KiB
Plaintext
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
const require_utils_html = require("../utils/html.js");
|
|
const require_jsx_constants = require("./constants.js");
|
|
const require_jsx_context = require("./context.js");
|
|
const require_jsx_base = require("./base.js");
|
|
const require_jsx_dom_render = require("./dom/render.js");
|
|
const require_jsx_dom_components = require("./dom/components.js");
|
|
const require_jsx_components = require("./components.js");
|
|
//#region src/jsx/streaming.ts
|
|
/**
|
|
* @module
|
|
* This module enables JSX to supports streaming Response.
|
|
*/
|
|
/**
|
|
* Used to specify nonce for scripts generated by `Suspense` and `ErrorBoundary`.
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* <StreamingContext.Provider value={{ scriptNonce: 'test-nonce' }}>
|
|
* <Suspense fallback={<p>Loading...</p>}>
|
|
* <Content />
|
|
* </Suspense>
|
|
* </StreamingContext.Provider>
|
|
* ```
|
|
*/
|
|
const StreamingContext = require_jsx_context.createContext(null);
|
|
let suspenseCounter = 0;
|
|
const serializeBoundaryChild = (child) => typeof child === "string" || Array.isArray(child) ? require_jsx_base.renderChildren([child]) : child == null || typeof child === "boolean" ? "" : require_jsx_base.isUntrustedObject(child) ? require_jsx_base.renderUntrustedObject(child) : child.toString();
|
|
/**
|
|
* @experimental
|
|
* `Suspense` is an experimental feature.
|
|
* The API might be changed.
|
|
*/
|
|
const Suspense = async ({ children, fallback }) => {
|
|
if (!Array.isArray(children)) children = [children];
|
|
const nonce = require_jsx_context.useContext(StreamingContext)?.scriptNonce;
|
|
let resArray = [];
|
|
const stackNode = { [require_jsx_constants.DOM_STASH]: [0, []] };
|
|
const popNodeStack = () => {
|
|
require_jsx_dom_render.buildDataStack.pop();
|
|
};
|
|
try {
|
|
stackNode[require_jsx_constants.DOM_STASH][0] = 0;
|
|
require_jsx_dom_render.buildDataStack.push([[], stackNode]);
|
|
resArray = children.map(serializeBoundaryChild);
|
|
} catch (e) {
|
|
if (e instanceof Promise) {
|
|
const resume = require_jsx_context.captureRenderContext();
|
|
resArray = [e.then(() => resume(() => {
|
|
stackNode[require_jsx_constants.DOM_STASH][0] = 0;
|
|
require_jsx_dom_render.buildDataStack.push([[], stackNode]);
|
|
return require_jsx_components.childrenToString(children).finally(popNodeStack);
|
|
}))];
|
|
} else throw e;
|
|
} finally {
|
|
popNodeStack();
|
|
}
|
|
if (resArray.some((res) => res instanceof Promise)) {
|
|
const index = suspenseCounter++;
|
|
const fallbackStr = await (typeof fallback === "string" || Array.isArray(fallback) ? require_jsx_base.renderChildren([fallback]) : require_jsx_base.isUntrustedObject(fallback) ? require_jsx_base.renderUntrustedObject(fallback) : fallback.toString());
|
|
return require_utils_html.raw(`<template id="H:${index}"></template>${fallbackStr}<!--/$-->`, [...fallbackStr.callbacks || [], ({ phase, buffer, context }) => {
|
|
if (phase === require_utils_html.HtmlEscapedCallbackPhase.BeforeStream) return;
|
|
return Promise.all(resArray).then(async (htmlArray) => {
|
|
htmlArray = htmlArray.flat();
|
|
const content = htmlArray.join("");
|
|
if (buffer) buffer[0] = buffer[0].replace(new RegExp(`<template id="H:${index}"></template>.*?<!--/\\$-->`, "s"), () => content);
|
|
let html = buffer ? "" : `<template data-hono-target="H:${index}">${content}</template><script${nonce ? ` nonce="${nonce}"` : ""}>
|
|
((d,c,n) => {
|
|
c=d.currentScript.previousSibling
|
|
d=d.getElementById('H:${index}')
|
|
if(!d)return
|
|
do{n=d.nextSibling;n.remove()}while(n.nodeType!=8||n.nodeValue!='/$')
|
|
d.replaceWith(c.content)
|
|
})(document)
|
|
<\/script>`;
|
|
const callbacks = htmlArray.map((html) => html.callbacks || []).flat();
|
|
if (!callbacks.length) return html;
|
|
if (phase === require_utils_html.HtmlEscapedCallbackPhase.Stream) html = await require_utils_html.resolveCallback(html, require_utils_html.HtmlEscapedCallbackPhase.BeforeStream, true, context);
|
|
return require_utils_html.raw(html, callbacks);
|
|
});
|
|
}]);
|
|
} else return require_utils_html.raw(resArray.join(""));
|
|
};
|
|
Suspense[require_jsx_constants.DOM_RENDERER] = require_jsx_dom_components.Suspense;
|
|
const textEncoder = new TextEncoder();
|
|
/**
|
|
* @experimental
|
|
* `renderToReadableStream()` is an experimental feature.
|
|
* The API might be changed.
|
|
*/
|
|
const renderToReadableStream = (content, onError = console.trace) => {
|
|
let cancelled = false;
|
|
return new ReadableStream({
|
|
async start(controller) {
|
|
try {
|
|
if (content instanceof require_jsx_base.JSXNode) content = content.toString();
|
|
const context = typeof content === "object" ? content : {};
|
|
const resolved = await require_utils_html.resolveCallback(content, require_utils_html.HtmlEscapedCallbackPhase.BeforeStream, true, context);
|
|
if (!cancelled) controller.enqueue(textEncoder.encode(resolved));
|
|
let resolvedCount = 0;
|
|
const callbacks = [];
|
|
const then = (promise) => {
|
|
callbacks.push(promise.catch((err) => {
|
|
console.log(err);
|
|
onError(err);
|
|
return "";
|
|
}).then(async (res) => {
|
|
res = await require_utils_html.resolveCallback(res, require_utils_html.HtmlEscapedCallbackPhase.BeforeStream, true, context);
|
|
res.callbacks?.map((c) => c({
|
|
phase: require_utils_html.HtmlEscapedCallbackPhase.Stream,
|
|
context
|
|
})).filter(Boolean).forEach(then);
|
|
resolvedCount++;
|
|
if (!cancelled) controller.enqueue(textEncoder.encode(res));
|
|
}));
|
|
};
|
|
resolved.callbacks?.map((c) => c({
|
|
phase: require_utils_html.HtmlEscapedCallbackPhase.Stream,
|
|
context
|
|
})).filter(Boolean).forEach(then);
|
|
while (resolvedCount !== callbacks.length) await Promise.all(callbacks);
|
|
} catch (e) {
|
|
onError(e);
|
|
}
|
|
if (!cancelled) controller.close();
|
|
},
|
|
cancel() {
|
|
cancelled = true;
|
|
}
|
|
});
|
|
};
|
|
//#endregion
|
|
exports.StreamingContext = StreamingContext;
|
|
exports.Suspense = Suspense;
|
|
exports.renderToReadableStream = renderToReadableStream;
|