123 行
3.1 KiB
TypeScript
123 行
3.1 KiB
TypeScript
import React from "react";
|
|
import ReactDOM from "react-dom/client";
|
|
import { QueryClientProvider } from "@tanstack/react-query";
|
|
import { RouterProvider } from "react-router-dom";
|
|
import { ErrorBoundary } from "./components/common/ErrorBoundary";
|
|
import { Toaster } from "./components/ui/sonner";
|
|
import { queryClient } from "./query/queryClient";
|
|
import { appRouter } from "./router";
|
|
import { clearPwaRuntimeCaches, installBuildFreshnessMonitor, registerPwaIfEnabled } from "./pwa";
|
|
import "./index.css";
|
|
|
|
document.documentElement.dataset.buildSha = String(import.meta.env.VITE_BUILD_SHA || "development").slice(0, 12);
|
|
|
|
const CHUNK_RELOAD_ATTEMPTED_KEY = "hyb_oa_chunk_reload_attempted";
|
|
const CHUNK_LOAD_ERROR_PATTERNS = [
|
|
"chunkloaderror",
|
|
"loading chunk",
|
|
"failed to fetch dynamically imported module",
|
|
"importing a module script failed",
|
|
"error loading dynamically imported module",
|
|
];
|
|
|
|
function extractErrorMessages(value: unknown): string[] {
|
|
if (!value) {
|
|
return [];
|
|
}
|
|
|
|
if (typeof value === "string") {
|
|
return [value];
|
|
}
|
|
|
|
if (value instanceof Error) {
|
|
return [value.name, value.message];
|
|
}
|
|
|
|
if (typeof value !== "object") {
|
|
return [];
|
|
}
|
|
|
|
const record = value as {
|
|
message?: unknown;
|
|
reason?: unknown;
|
|
payload?: unknown;
|
|
error?: unknown;
|
|
};
|
|
|
|
return [
|
|
...extractErrorMessages(record.message),
|
|
...extractErrorMessages(record.reason),
|
|
...extractErrorMessages(record.payload),
|
|
...extractErrorMessages(record.error),
|
|
];
|
|
}
|
|
|
|
function isChunkLoadFailure(value: unknown) {
|
|
return extractErrorMessages(value).some((message) => {
|
|
const normalized = message.toLowerCase();
|
|
return CHUNK_LOAD_ERROR_PATTERNS.some((pattern) => normalized.includes(pattern));
|
|
});
|
|
}
|
|
|
|
function installChunkLoadRecovery() {
|
|
if (typeof window === "undefined") {
|
|
return;
|
|
}
|
|
|
|
let reloadTriggered = false;
|
|
const reloadForChunkFailure = () => {
|
|
if (reloadTriggered || window.sessionStorage.getItem(CHUNK_RELOAD_ATTEMPTED_KEY)) {
|
|
return;
|
|
}
|
|
|
|
reloadTriggered = true;
|
|
window.sessionStorage.setItem(CHUNK_RELOAD_ATTEMPTED_KEY, "1");
|
|
void clearPwaRuntimeCaches()
|
|
.catch(() => undefined)
|
|
.finally(() => {
|
|
window.location.reload();
|
|
});
|
|
};
|
|
|
|
window.addEventListener("vite:preloadError", (event) => {
|
|
if (!isChunkLoadFailure(event.payload)) {
|
|
return;
|
|
}
|
|
|
|
event.preventDefault();
|
|
reloadForChunkFailure();
|
|
});
|
|
|
|
window.addEventListener("error", (event) => {
|
|
if (!isChunkLoadFailure(event.error ?? event.message)) {
|
|
return;
|
|
}
|
|
|
|
reloadForChunkFailure();
|
|
});
|
|
|
|
window.addEventListener("unhandledrejection", (event) => {
|
|
if (!isChunkLoadFailure(event.reason)) {
|
|
return;
|
|
}
|
|
|
|
event.preventDefault();
|
|
reloadForChunkFailure();
|
|
});
|
|
}
|
|
|
|
installChunkLoadRecovery();
|
|
installBuildFreshnessMonitor();
|
|
registerPwaIfEnabled();
|
|
|
|
ReactDOM.createRoot(document.getElementById("root")!).render(
|
|
<React.StrictMode>
|
|
<ErrorBoundary>
|
|
<QueryClientProvider client={queryClient}>
|
|
<RouterProvider router={appRouter} />
|
|
</QueryClientProvider>
|
|
</ErrorBoundary>
|
|
<Toaster />
|
|
</React.StrictMode>,
|
|
);
|