/** * Classic inline script for the home loading screen. * * It has to run when the client bundle never runs, so the generated string is * ES5: no modules, no React, no reload until the button is clicked. * The timer is longer than the 20s in-bundle bootstrap slow threshold, so a * normal hydrate does not flash this copy. */ import { HOME_BOOTSTRAP_FAILURE, sanitizeBootstrapBuildId, } from "./home-bootstrap-failure.ts"; export const FIRST_PAINT_FALLBACK_TIMEOUT_MS = 25_000; export const FIRST_PAINT_FALLBACK_TITLE = "这个页面没能加载完"; export const FIRST_PAINT_FALLBACK_BODY = "网络中断或浏览器版本过旧都会这样。可以重新加载试一次;如果反复出现,请在 Safari 设置里清除本站数据,或把系统升级到 iOS 16.4 以上。"; export const FIRST_PAINT_FALLBACK_ACTION = "重新加载"; function jsDoubleQuoted(value: string): string { let escaped = ""; for (let index = 0; index < value.length; index += 1) { const char = value.charAt(index); const code = value.charCodeAt(index); if (char === "\\") escaped += "\\\\"; else if (char === "\"") escaped += "\\\""; else if (char === "\n") escaped += "\\n"; else if (char === "\r") escaped += "\\r"; else if (char === "<") escaped += "\\u003c"; else if (code === 0x2028 || code === 0x2029) escaped += `\\u${code.toString(16)}`; else escaped += char; } return `"${escaped}"`; } export function firstPaintFallbackBootScript(buildId?: string): string { const safeBuild = sanitizeBootstrapBuildId(buildId ?? process.env.NEXT_PUBLIC_GIT_COMMIT); const timeout = String(FIRST_PAINT_FALLBACK_TIMEOUT_MS); const hydrateTimeout = jsDoubleQuoted(HOME_BOOTSTRAP_FAILURE.hydrateTimeout); const chunk404 = jsDoubleQuoted(HOME_BOOTSTRAP_FAILURE.chunk404); const chunkParse = jsDoubleQuoted(HOME_BOOTSTRAP_FAILURE.chunkParse); const staleCache = jsDoubleQuoted(HOME_BOOTSTRAP_FAILURE.staleCache); const weakNetwork = jsDoubleQuoted(HOME_BOOTSTRAP_FAILURE.weakNetwork); return `(function () { var started = Date.now(); var buildId = ${jsDoubleQuoted(safeBuild)}; var category = ${hydrateTimeout}; function fromCache(source) { try { if (!source || !window.performance || !window.performance.getEntriesByName) return false; var entries = window.performance.getEntriesByName(source); var i; for (i = 0; i < entries.length; i++) { var entry = entries[i]; if (entry && entry.decodedBodySize > 0 && entry.transferSize === 0) return true; } } catch (ignore) {} return false; } function classify(message, source) { var text = String(message || ""); var from = String(source || ""); var combined = text + " " + from; if (/SyntaxError|Unexpected token|Invalid regular expression/i.test(text)) { return fromCache(from) ? ${staleCache} : ${chunkParse}; } if (/ChunkLoadError|Loading chunk|Failed to fetch dynamically imported module|Failed to load/i.test(combined) || /\\/_next\\/static\\//.test(from)) { if (fromCache(from)) return ${staleCache}; if (navigator && navigator.onLine === false) return ${weakNetwork}; return ${chunk404}; } return ""; } window.addEventListener("error", function (event) { var message = ""; var source = ""; if (event) { message = event.message || ""; source = event.filename || ""; if (!source && event.target && event.target.src) source = event.target.src; } var next = classify(message, source); if (next) category = next; }, true); window.setTimeout(function () { var root = document.documentElement; if (!root || root.getAttribute("data-hydrated") === "1") return; var loading = document.querySelector(".app-loading"); if (!loading) return; if ((category === ${hydrateTimeout} || category === ${chunk404}) && navigator && navigator.onLine === false) { category = ${weakNetwork}; } var slot = loading.querySelector(".app-loading-content"); if (!slot) return; if (loading.getAttribute("data-bootstrap-fallback") === "1") return; loading.setAttribute("data-bootstrap-fallback", "1"); loading.setAttribute("aria-busy", "false"); if (loading.className.indexOf("app-loading-error") === -1) { loading.className += " app-loading-error"; } slot.setAttribute("role", "alert"); slot.innerHTML = ""; var title = document.createElement("strong"); title.appendChild(document.createTextNode(${jsDoubleQuoted(FIRST_PAINT_FALLBACK_TITLE)})); var body = document.createElement("span"); body.appendChild(document.createTextNode(${jsDoubleQuoted(FIRST_PAINT_FALLBACK_BODY)})); var actions = document.createElement("div"); actions.className = "app-loading-actions"; var button = document.createElement("button"); button.className = "button-primary"; button.type = "button"; button.appendChild(document.createTextNode(${jsDoubleQuoted(FIRST_PAINT_FALLBACK_ACTION)})); button.onclick = function () { window.location.reload(); }; actions.appendChild(button); slot.appendChild(title); slot.appendChild(body); slot.appendChild(actions); var duration = Date.now() - started; root.setAttribute("data-bootstrap-failure", category); root.setAttribute("data-bootstrap-build", buildId); root.setAttribute("data-bootstrap-duration", String(duration)); window.__jyotishaBootstrapObservation = { category: category, buildId: buildId, durationMs: duration }; }, ${timeout}); })();`; }