diff --git a/web/src/app/error.tsx b/web/src/app/error.tsx index f227396..e3aeeb6 100644 --- a/web/src/app/error.tsx +++ b/web/src/app/error.tsx @@ -1,8 +1,22 @@ "use client"; +import { useEffect } from "react"; import { PageHeader } from "@/components/general/PageHeader"; +import { isChunkLoadError } from "@/lib/chunkError"; + +const RELOAD_KEY = "chunk-error-reload"; +const RELOAD_COOLDOWN_MS = 10_000; + +function ErrorPage({ error }: { error: Error & { digest?: string } }) { + useEffect(() => { + if (!isChunkLoadError(error)) return; + // No reload loop if the chunk is gone for good + const last = Number(sessionStorage.getItem(RELOAD_KEY) ?? 0); + if (Date.now() - last < RELOAD_COOLDOWN_MS) return; + sessionStorage.setItem(RELOAD_KEY, String(Date.now())); + window.location.reload(); + }, [error]); -function ErrorPage() { return (
diff --git a/web/src/lib/chunkError.test.ts b/web/src/lib/chunkError.test.ts new file mode 100644 index 0000000..094271e --- /dev/null +++ b/web/src/lib/chunkError.test.ts @@ -0,0 +1,30 @@ +import { describe, expect, it } from "vitest"; + +import { isChunkLoadError } from "./chunkError.ts"; + +describe("isChunkLoadError", () => { + it("matches the turbopack chunk loader error", () => { + const error = new Error( + "Failed to load chunk /_next/static/chunks/0xd_vh2ud8yl-.js from module 64893" + ); + error.name = "ChunkLoadError"; + expect(isChunkLoadError(error)).toBe(true); + }); + + it("matches by message when the name is generic", () => { + expect(isChunkLoadError(new Error("Loading chunk 123 failed."))).toBe(true); + expect( + isChunkLoadError( + new TypeError("Failed to fetch dynamically imported module: https://neuf.no/x.js") + ) + ).toBe(true); + expect(isChunkLoadError(new TypeError("Importing a module script failed."))).toBe(true); + }); + + it("ignores other errors", () => { + expect(isChunkLoadError(new Error("boom"))).toBe(false); + expect( + isChunkLoadError(new TypeError("Cannot read properties of undefined (reading 'slug')")) + ).toBe(false); + }); +}); diff --git a/web/src/lib/chunkError.ts b/web/src/lib/chunkError.ts new file mode 100644 index 0000000..d5015bb --- /dev/null +++ b/web/src/lib/chunkError.ts @@ -0,0 +1,9 @@ +// A tab opened before a deploy may reference chunks the new build no longer ships +export function isChunkLoadError(error: Error): boolean { + return ( + error.name === "ChunkLoadError" || + /failed to load chunk|loading chunk .* failed|dynamically imported module|importing a module script failed/i.test( + error.message + ) + ); +}