UNPKG

@mongez/react-atom

Version:
82 lines (80 loc) 2.97 kB
import React from "react"; import { jsx } from "react/jsx-runtime"; //#region ../@mongez/react-atom/src/ssr.tsx /** * The default DOM id used by {@link HydrateAtomsScript} and * {@link readHydration}. Override per-provider if you need to embed * multiple snapshots in one document. */ const DEFAULT_HYDRATION_SCRIPT_ID = "__mongez_atom_state"; /** * Build a JSON string suitable for embedding inside an HTML `<script>` * tag. Two safety steps beyond a plain `JSON.stringify`: * * 1. The closing tag sequence `</` is escaped to `<\/` so an atom value * containing literal HTML cannot break out of the script element. * 2. The U+2028 / U+2029 line separators (which are valid JSON but not * valid JavaScript string literals) are escaped. */ function serializeSnapshot(snapshot, options = {}) { return JSON.stringify(snapshot, options.replacer, options.space).replace(/<\/(script)/gi, "<\\/$1").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029"); } /** * Convenience that snapshots a store and serializes the result in one call. * * const payload = serializeStore(serverStore); * // payload is a script-safe JSON string */ function serializeStore(store, options) { return serializeSnapshot(store.snapshot(), options); } /** * Renders an inline `<script type="application/json">` carrying a store * snapshot for the client to pick up. * * Place this once per `<AtomStoreProvider>` you want to hydrate. The * matching client-side call is {@link readHydration}. * * // server * <AtomStoreProvider store={serverStore}> * <App /> * <HydrateAtomsScript snapshot={serverStore.snapshot()} /> * </AtomStoreProvider> * * // client root * <AtomStoreProvider initialValues={readHydration() ?? undefined}> * <App /> * </AtomStoreProvider> */ function HydrateAtomsScript({ snapshot, id = DEFAULT_HYDRATION_SCRIPT_ID, nonce }) { return /* @__PURE__ */ jsx("script", { id, type: "application/json", nonce, dangerouslySetInnerHTML: { __html: typeof snapshot === "string" ? snapshot : serializeSnapshot(snapshot) } }); } /** * Read a hydration snapshot embedded via {@link HydrateAtomsScript} from * the current document. * * - On the server (no `document`), returns `null`. * - When the script tag is missing, returns `null`. * - When the script body is not valid JSON, returns `null` and logs the * error via `console.error` (so a malformed payload is visible during * development but does not crash hydration). */ function readHydration(id = DEFAULT_HYDRATION_SCRIPT_ID) { if (typeof document === "undefined") return null; const el = document.getElementById(id); if (!el) return null; try { return JSON.parse(el.textContent ?? "null"); } catch (err) { console.error(`[@mongez/react-atom] Could not parse hydration script #${id}:`, err); return null; } } //#endregion export { DEFAULT_HYDRATION_SCRIPT_ID, HydrateAtomsScript, readHydration, serializeSnapshot, serializeStore }; //# sourceMappingURL=ssr.mjs.map