From d24e963fd99db6e3452ba02011af4ffa7f98ad13 Mon Sep 17 00:00:00 2001 From: SkateSpots Date: Sat, 26 Sep 2026 17:33:20 +0700 Subject: [PATCH] fix(boot): auto-recover from stale chunks after a deploy Deploys replace hashed chunk filenames; a tab that was already open can load a chunk that no longer exists (404) and hit the error boundary ('THIS PAGE SLAMMED'). Listen for Vite's vite:preloadError (and dynamic-import failures) and reload once, guarded against loops. --- src/main.tsx | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/main.tsx b/src/main.tsx index b733dfa..3079840 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -76,6 +76,40 @@ import '@fontsource/barlow-condensed/700.css'; })(); +/** + * STALE-DEPLOY SELF-HEAL + * + * Deploys replace hashed chunk filenames and delete the old ones. A tab that + * was already open still holds the previous manifest, so navigating to a lazy + * route loads a chunk that no longer exists (404) and the error boundary shows + * "THIS PAGE SLAMMED". Vite emits `vite:preloadError` for exactly this case. + * + * Reload once to pick up the new assets. The timestamp guard prevents a reload + * loop if a build is genuinely broken (the error boundary will show instead). + */ +(function recoverFromStaleChunks() { + const KEY = 'skatespots:chunk-reload-at'; + const reload = () => { + try { + const last = Number(sessionStorage.getItem(KEY) || 0); + if (Date.now() - last < 15000) return; // already reloaded recently — don't loop + sessionStorage.setItem(KEY, String(Date.now())); + } catch { + /* sessionStorage unavailable — fall through and reload anyway */ + } + window.location.reload(); + }; + + window.addEventListener('vite:preloadError', reload); + window.addEventListener('unhandledrejection', (e) => { + const msg = String((e as PromiseRejectionEvent).reason?.message ?? ''); + if (/dynamically imported module|Importing a module script failed|Loading chunk/i.test(msg)) { + reload(); + } + }); +})(); + + createRoot(document.getElementById("root")!).render(