UNPKG

@wayneintacart/react-hydration-overlay

Version:

Fork of React utility for descriptive hydration mismatch errors.

143 lines (140 loc) 5.3 kB
'use client'; import beautify from 'beautify'; import { createPortal } from 'react-dom'; import React, { useState, useEffect } from 'react'; import ReactDiffViewer, { DiffMethod } from 'react-diff-viewer-continued'; // Remove emotion-inserted style tags from the HTML string from the server // as they get removed before hydration, so they shouldn't show in the diff // (pollutes the diff and makes it unusable) function removeEmotionStyleTags(htmlString = "") { const regex = /<style\s+data-emotion(?:="[^"]*"|[^>])*>[\s\S]*?<\/style>/g; return htmlString.replace(regex, ""); } const DiffViewer = ReactDiffViewer.default ? ReactDiffViewer.default : ReactDiffViewer; function Overlay({ integrations }) { const [SSRHtml, setSSRHtml] = useState(""); const [CSRHtml, setCSRHtml] = useState(""); const [showModal, setShowModal] = useState(true); const [hasHydrationMismatch, setHasHydrationMismatch] = useState(false); useEffect(()=>{ if (!window.BUILDER_HYDRATION_OVERLAY) { console.warn("[ReactHydrationOverlay]: No `window.BUILDER_HYDRATION_OVERLAY` found. Make sure the initializer script is properly injected into your app's entry point."); return; } const ssrHtml = removeEmotionStyleTags(window.BUILDER_HYDRATION_OVERLAY.SSR_HTML); const newCSRHtml = window.BUILDER_HYDRATION_OVERLAY.CSR_HTML; if (!ssrHtml || !newCSRHtml) return; const newSSR = beautify(ssrHtml, { format: "html" }); setSSRHtml(newSSR); const newCSR = beautify(newCSRHtml, { format: "html" }); setCSRHtml(newCSR); setShowModal(true); if (window.BUILDER_HYDRATION_OVERLAY.ERROR) { setHasHydrationMismatch(true); } }, []); const hideModal = ()=>{ setShowModal(false); }; const renderModal = showModal && hasHydrationMismatch && typeof document !== "undefined"; if (!renderModal) { return null; } const renderOverlay = ()=>{ return /*#__PURE__*/ React.createElement("div", { style: { all: "initial", position: "absolute", top: 0, left: 0, right: 0, bottom: 0, zIndex: 999998, background: "rgba(0,0,0,0.5)", cursor: "pointer", display: "flex", flexDirection: "column", fontFamily: "monospace", direction: "ltr" }, onClick: hideModal }, /*#__PURE__*/ React.createElement("div", { style: { all: "initial", zIndex: 999999, margin: "4rem 6rem", backgroundColor: "white", borderRadius: "0.5rem", overflow: "auto", cursor: "auto", color: "#212529" }, onClick: (e)=>{ e.stopPropagation(); } }, /*#__PURE__*/ React.createElement("div", { style: { display: "flex", flexDirection: "column" } }, /*#__PURE__*/ React.createElement("div", { style: { all: "initial", display: "flex", justifyContent: "space-between", borderBottom: "1px solid black", alignItems: "center" } }, /*#__PURE__*/ React.createElement("div", { style: { all: "initial", fontSize: "2rem", fontWeight: "bold", padding: "1rem", fontFamily: 'sans-serif' } }, "Hydration Mismatch Occurred"), /*#__PURE__*/ React.createElement("button", { style: { all: "unset", cursor: "pointer", padding: "0.5rem", marginRight: "1rem", backgroundColor: "#212529", borderRadius: "0.25rem", color: "white", fontFamily: 'sans-serif' }, onClick: hideModal }, "CLOSE")), /*#__PURE__*/ React.createElement("div", { style: { position: "relative", width: "100%" } }, /*#__PURE__*/ React.createElement(DiffViewer, { oldValue: SSRHtml, newValue: CSRHtml, leftTitle: "Server-Side Render", rightTitle: "Client-Side Render", compareMethod: DiffMethod.WORDS }))))); }; if (integrations?.spotlight) { // Spotlight Integration import('@spotlightjs/spotlight').then((Spotlight)=>{ if (Spotlight && Spotlight.openSpotlight) { Spotlight.openSpotlight("/hydration-error"); } else { console.error("[ReactHydrationOverlay]: Spotlight package is not available."); } }).catch((error)=>{ console.error("[ReactHydrationOverlay]: Error importing spotlight package:", error); }); } else { return /*#__PURE__*/ createPortal(renderOverlay(), document.body); } } export { Overlay as O };