UNPKG

fully-react

Version:
251 lines (248 loc) 7.83 kB
import { renderServerComponent } from "./chunk-U26WWCBL.js"; import { sanitize } from "./chunk-NHKA32FP.js"; // src/server/html.tsx import { use, useMemo } from "react"; import { createFromFetch as createElementFromFetch, createFromReadableStream as createElementFromStream } from "react-server-dom-webpack/client.edge"; import { renderToReadableStream as _renderToHTMLStream } from "react-dom/server.edge"; import { jsx, jsxs } from "react/jsx-runtime"; function useServerElement(serverElementStream, flightResponseRef, writable, onChunk = () => { }) { if (flightResponseRef.current !== null) { return flightResponseRef.current; } if (writable && !writable.locked) { let read2 = function() { forwardReader.read().then(({ done, value }) => { if (value) { onChunk(value); } if (done) { flightResponseRef.current = null; writer.close(); } else { writer.write(value); read2(); } }); }; var read = read2; const [renderStream, forwardStream] = serverElementStream.tee(); const res = createElementFromStream(renderStream, { callServer: (method, args) => { throw new Error("Not implemented"); } // moduleMap: isEdgeRuntime // ? clientReferenceManifest.edgeSSRModuleMapping // : clientReferenceManifest.ssrModuleMapping, }); flightResponseRef.current = res; const forwardReader = forwardStream.getReader(); const writer = writable.getWriter(); read2(); return res; } else { return createElementFromStream(serverElementStream, { callServer: (method, args) => { throw new Error("Not implemented"); } // moduleMap: isEdgeRuntime // ? clientReferenceManifest.edgeSSRModuleMapping // : clientReferenceManifest.ssrModuleMapping, }); } } function createServerComponentRenderer(src, { dataStream: writable, ...env }) { const flightResponseRef = { current: null }; return function ServerComponentWrapper(props) { const response = useMemo(() => { if (!writable || writable.locked) { return createElementFromFetch( renderServerComponent(src, props, env).then( (r) => ({ body: r }) ), { callServer: (method, args) => { throw new Error("Not implemented"); } } ); } const res = createElementFromFetch( renderServerComponent(src, props, env).then((r) => { const [renderStream, forwardStream] = r.tee(); const forwardReader = forwardStream.getReader(); const writer = writable.getWriter(); function read() { forwardReader.read().then(({ done, value }) => { if (value) { } if (done) { flightResponseRef.current = null; writer.close(); } else { writer.write(value); read(); } }); } read(); return { body: renderStream }; }), { callServer: (method, args) => { throw new Error("Not implemented"); } } ); flightResponseRef.current = res; return res; }, [writable]); if (!props) { console.log("called with no props"); throw new Error(""); } return use(response); }; } async function renderToHTMLStream(component, props, renderOptions) { const ServerComponent = createServerComponentRenderer( component, renderOptions ); const htmlStream = await _renderToHTMLStream( /* @__PURE__ */ jsx(ServerComponent, { ...props }), renderOptions ); return htmlStream.pipeThrough(bufferedTransformStream()); } async function createHTMLResponse(component, props, renderOptions, responseInit = {}) { const transformStream = new TransformStream(); try { const htmlStream = await renderToHTMLStream(component, props, { ...renderOptions, dataStream: transformStream.writable }); return new Response( htmlStream.pipeThrough( inlineInitialServerComponent(transformStream.readable) ), { ...responseInit, headers: { "Content-Type": "text/html", ...responseInit.headers ?? {} } } ); } catch (e) { const htmlStream = await _renderToHTMLStream( /* @__PURE__ */ jsxs("html", { id: "__error__", children: [ /* @__PURE__ */ jsx("head", {}), /* @__PURE__ */ jsx("body", {}) ] }), renderOptions ); console.log({ error: e }); return new Response( htmlStream.pipeThrough(bufferedTransformStream()).pipeThrough(inlineInitialServerComponent(transformStream.readable)), { headers: { "Content-Type": "text/html" } } ); } } async function nextMacroTask() { return new Promise((resolve) => setTimeout(resolve, 0)); } var closingBodyHtmlText = `</body></html>`; function inlineInitialServerComponent(serverElementStream) { let removedClosingBodyHtmlText = false; let insertingServerElementStreamScripts; let finishedInsertingServerElementStreamScripts = false; const textDecoder = new TextDecoder(); const textEncoder = new TextEncoder(); return new TransformStream({ transform(chunk, controller) { const text = textDecoder.decode(chunk); if (text.endsWith(closingBodyHtmlText) && !finishedInsertingServerElementStreamScripts) { const [withoutClosingBodyHtmlText] = text.split(closingBodyHtmlText); controller.enqueue(textEncoder.encode(withoutClosingBodyHtmlText)); removedClosingBodyHtmlText = true; } else { controller.enqueue(chunk); } if (!insertingServerElementStreamScripts) { const reader = serverElementStream.getReader(); controller.enqueue( textEncoder.encode( `<script>(()=>{const{writable,readable}=new TransformStream();const writer=writable.getWriter();self.init_server=readable;self.chunk=(text)=>writer.write(new TextEncoder().encode(text))})()</script>` ) ); insertingServerElementStreamScripts = new Promise(async (resolve) => { try { while (true) { const result = await reader.read(); if (result.done) { finishedInsertingServerElementStreamScripts = true; if (removedClosingBodyHtmlText) { controller.enqueue(textEncoder.encode(closingBodyHtmlText)); } return resolve(); } await nextMacroTask(); controller.enqueue( textEncoder.encode( `<script>self.chunk(${sanitize( JSON.stringify(textDecoder.decode(result.value)) )});</script>` ) ); } } catch (error) { controller.error(error); } }); } }, async flush() { return insertingServerElementStreamScripts; } }); } function bufferedTransformStream() { let bufferedText = ``; let buffering; return new TransformStream({ transform(chunk, controller) { bufferedText += new TextDecoder().decode(chunk); buffering ||= new Promise(async (resolve) => { await nextMacroTask(); controller.enqueue(new TextEncoder().encode(bufferedText)); bufferedText = ``; buffering = void 0; resolve(); }); }, async flush() { return buffering; } }); } export { useServerElement, createServerComponentRenderer, renderToHTMLStream, createHTMLResponse, inlineInitialServerComponent, bufferedTransformStream };