UNPKG

rwsdk

Version:

Build fast, server-driven webapps on Cloudflare with SSR, RSC, and realtime

97 lines (96 loc) 4.18 kB
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime"; import { clientWebpackRequire } from "./imports/client"; // NOTE: `react-server-dom-webpack` uses this global to load modules, // so we need to define it here before importing "react-server-dom-webpack." globalThis.__webpack_require__ = clientWebpackRequire; export const fetchTransport = (transportContext) => { const fetchCallServer = async (id, args) => { const { createFromFetch, encodeReply } = await import("react-server-dom-webpack/client.browser"); const url = new URL(window.location.href); url.searchParams.set("__rsc", ""); if (id != null) { url.searchParams.set("__rsc_action_id", id); } const fetchPromise = fetch(url, { method: "POST", body: args != null ? await encodeReply(args) : null, }); // If there's a response handler, check the response first if (transportContext.handleResponse) { const response = await fetchPromise; const shouldContinue = transportContext.handleResponse(response); if (!shouldContinue) { return; } // Continue with the response if handler returned true const streamData = createFromFetch(Promise.resolve(response), { callServer: fetchCallServer, }); transportContext.setRscPayload(streamData); const result = await streamData; return result.actionResult; } // Original behavior when no handler is present const streamData = createFromFetch(fetchPromise, { callServer: fetchCallServer, }); transportContext.setRscPayload(streamData); const result = await streamData; return result.actionResult; }; return fetchCallServer; }; export const initClient = async ({ transport = fetchTransport, hydrateRootOptions, handleResponse, } = {}) => { const React = await import("react"); const { hydrateRoot } = await import("react-dom/client"); const transportContext = { setRscPayload: () => { }, handleResponse, }; let transportCallServer = transport(transportContext); const callServer = (id, args) => transportCallServer(id, args); const upgradeToRealtime = async ({ key } = {}) => { const { realtimeTransport } = await import("./lib/realtime/client"); const createRealtimeTransport = realtimeTransport({ key }); transportCallServer = createRealtimeTransport(transportContext); }; globalThis.__rsc_callServer = callServer; globalThis.__rw = { callServer, upgradeToRealtime, }; const rootEl = document.getElementById("hydrate-root"); if (!rootEl) { throw new Error('no element with id "hydrate-root"'); } let rscPayload; // context(justinvdm, 18 Jun 2025): We inject the RSC payload // unless render(Document, [...], { rscPayload: false }) was used. if (globalThis.__FLIGHT_DATA) { const { createFromReadableStream } = await import("react-server-dom-webpack/client.browser"); const { rscStream } = await import("rsc-html-stream/client"); rscPayload = createFromReadableStream(rscStream, { callServer, }); } function Content() { const [streamData, setStreamData] = React.useState(rscPayload); const [_isPending, startTransition] = React.useTransition(); transportContext.setRscPayload = (v) => startTransition(() => setStreamData(v)); return (_jsx(_Fragment, { children: streamData ? React.use(streamData).node : null })); } hydrateRoot(rootEl, _jsx(Content, {}), { onUncaughtError: (error, { componentStack }) => { console.error("Uncaught error: %O\n\nComponent stack:%s", error, componentStack); }, ...hydrateRootOptions, }); if (import.meta.hot) { import.meta.hot.on("rsc:update", (e) => { console.log("[rwsdk] hot update", e.file); callServer("__rsc_hot_update", [e.file]); }); } };