fix(build): drop custom manualChunks — cross-chunk TDZ crashed production boot

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.
This commit is contained in:
SkateSpots 2026-09-26 16:52:22 +07:00
parent 43d4773ce0
commit 39b4156760

View file

@ -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 '<x>' 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).
},
},
},