Files
SnowLuma/.pnpm-store/v11/files/3f/a45c148a9a87d5363473f363dd831042dce7afb2df0cb9e5d3e12f233c30f229d284f1f75ea2cf4131fe13d421fe21c4b05a29a305553ab4d2b642cabe09ca
TanTan 2a69b23b47
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
docs: 仓库链接指向本镜像仓库
2026-11-13 09:27:07 +08:00

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;