From 39b4156760b115a4523ba90289e55eb3c3080946 Mon Sep 17 00:00:00 2001 From: SkateSpots Date: Sat, 26 Sep 2026 16:52:22 +0700 Subject: [PATCH] =?UTF-8?q?fix(build):=20drop=20custom=20manualChunks=20?= =?UTF-8?q?=E2=80=94=20cross-chunk=20TDZ=20crashed=20production=20boot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vendor-* split created circular imports; the production bundle died with 'ReferenceError: Cannot access X before initialization' (vendor-charts, then vendor-bitcoin). Dev mode and vite build do not execute the bundle, so it only surfaced once deployed. Rollup's default splitting is cycle-safe. --- vite.config.ts | 72 +++++++------------------------------------------- 1 file changed, 10 insertions(+), 62 deletions(-) diff --git a/vite.config.ts b/vite.config.ts index 96e0ca6..f94ce60 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -89,68 +89,16 @@ export default defineConfig(() => ({ manualChunks: undefined, inlineDynamicImports: true, } : { - // Split large vendor libraries into separate cacheable chunks. - // Users only re-download a chunk when that specific library updates. - manualChunks(id) { - // Map — Leaflet is large (~150KB), isolate it so non-map pages don't pay for it - if (id.includes('leaflet') || id.includes('ngeohash')) { - return 'vendor-map'; - } - // Media — hls.js is large (~500KB) and only used by the radio/video - // players. Split it so the map homepage never downloads it. - if (id.includes('hls.js')) { - return 'vendor-media'; - } - // QR scanning — html5-qrcode (~300KB) is only used in wallet/scan - // flows; keep it out of the initial payload. - if (id.includes('html5-qrcode') || id.includes('qrcode')) { - return 'vendor-qr'; - } - // Charts — recharts (~400KB) is only used on stats pages. - if (id.includes('recharts') || id.includes('d3-')) { - return 'vendor-charts'; - } - // Nostr core — changes infrequently, worth caching separately - if (id.includes('nostr-tools') || id.includes('@nostrify') || id.includes('@noble')) { - return 'vendor-nostr'; - } - // Bitcoin / Lightning — large, rarely changes - if ( - id.includes('@cashu') || - id.includes('bitcoinjs-lib') || - id.includes('@scure') || - id.includes('@bitcoinerlab') || - id.includes('ecpair') || - id.includes('@getalby') || - id.includes('npubcash') - ) { - return 'vendor-bitcoin'; - } - // React ecosystem — very stable, long cache life. - // IMPORTANT: `react` (core, with useLayoutEffect etc.) MUST live in - // the SAME chunk as react-dom / react-router. If react-dom is split - // into vendor-react but core `react` lands elsewhere, react-dom runs - // before React is defined → "Cannot read properties of undefined - // (reading 'useLayoutEffect')". The `/react/` and scheduler matches - // below keep the whole React runtime together and loaded first. - if ( - id.includes('node_modules/react/') || - id.includes('node_modules/react-dom/') || - id.includes('node_modules/scheduler/') || - id.includes('react-router') || - id.includes('react/jsx-runtime') - ) { - return 'vendor-react'; - } - // Radix UI components — large but stable - if (id.includes('@radix-ui')) { - return 'vendor-radix'; - } - // TanStack Query - if (id.includes('@tanstack')) { - return 'vendor-query'; - } - }, + // Web build: let Rollup decide chunk boundaries. + // + // A custom manualChunks() used to split large vendors into `vendor-*` + // chunks. That produced cross-chunk circular imports and crashed boot + // of the production bundle with + // "ReferenceError: Cannot access '' before initialization" (TDZ) — + // first in vendor-charts, then vendor-bitcoin. Rollup's default + // splitting keeps interdependent modules together and is safe. Only + // re-introduce manual chunks with a runtime smoke test of the built + // bundle (dev mode does NOT catch this). }, }, },