Spit dependencies into chunks

This commit is contained in:
khalid@traclabs.com
2026-05-23 04:05:03 -05:00
parent 6127e7b781
commit 8294c47f74

View File

@@ -119,6 +119,14 @@ export default defineConfig({
// which precaching would undo. They're served via the
// `sticker-library` runtimeCaching rule below instead.
globIgnores: ['**/stickers/**'],
// Note: we deliberately do NOT raise `maximumFileSizeToCacheInBytes`
// here. The default 2 MiB cap is a useful regression alarm — if
// a future change pushes any chunk past it, the build fails loudly
// and we know to either split further or investigate why a chunk
// grew. Earlier this file overrode the cap to 5 MiB because the
// pre-split bundle was 2.32 MB; that override was removed once
// `build.rollupOptions.output.manualChunks` (below in this config)
// brought every chunk under the default cap.
runtimeCaching: [
{
urlPattern: /^https:\/\/cdn\.huggingface\.co\/.*/i,
@@ -198,5 +206,95 @@ export default defineConfig({
'/exports': { target: 'http://localhost:3001', changeOrigin: true },
},
},
build: { outDir: 'dist' },
build: {
outDir: 'dist',
// ────────────────────────────────────────────────────────────────
// Manual chunk splitting
// ────────────────────────────────────────────────────────────────
//
// Why we split rather than ship one bundle
// ─────────────────────────────────────────
// 1. Workbox precache cap. The PWA plugin's default
// `maximumFileSizeToCacheInBytes` is 2 MiB. A single bundle
// containing Konva + react-konva + @huggingface/transformers +
// react + filerobot + everything else easily clears 2.3 MB,
// which fails the precache step. Splitting keeps every chunk
// comfortably under the cap without needing to override it.
//
// 2. Browser cache reuse across deploys. When app code changes
// (the common case during active development), only the
// `index` chunk's hash flips; vendor chunks (Konva, React,
// transformers) keep their old hash and stay cached in users'
// browsers. That means returning users only re-download the
// relatively-small app chunk, not the 1-MB-each library
// chunks that haven't changed.
//
// 3. Parallel download. Browsers can fetch multiple chunks
// concurrently on HTTP/2, so loading 4 × 600 KB chunks is
// typically faster than 1 × 2.3 MB chunk on broadband, even
// before considering caching.
//
// What goes where
// ───────────────
// transformers/ @huggingface/transformers \u2014 the in-browser
// ML runtime used for background removal.
// Biggest single dependency. Only the
// inference engine is bundled here; the
// actual ML model weights stream from the
// huggingface CDN at runtime (see
// `transformers-models` / `transformers-lfs`
// runtimeCaching rules above for offline
// re-use).
// konva/ konva + react-konva + use-image \u2014 the canvas
// stack. All three are needed together
// because react-konva wraps konva and
// use-image wraps it for React consumption.
// Grouping prevents one from being inlined
// into a chunk that doesn't otherwise need
// the others.
// react-vendor/ react + react-dom \u2014 stable, every visitor
// needs them, perfect for long-term caching.
// filerobot/ react-filerobot-image-editor \u2014 mid-size,
// used only when the user opens the advanced
// image editor. Worth its own chunk so the
// rest of the app doesn't pay for it.
// index/ Everything else: app code, react-select,
// styled-components, zod, uuid, plus all
// the small utilities. This stays the
// "main" chunk that changes with each deploy.
//
// What we deliberately did NOT split
// ──────────────────────────────────
// The smaller deps (react-select, styled-components, zod, uuid,
// @emotion/is-prop-valid) stay in the index chunk on purpose.
// Pulling them out would create chunks under 50 KB each, which
// adds HTTP overhead (per-request connection cost, separate
// hash entries in the precache manifest) without meaningfully
// helping cache reuse. The "many small chunks" antipattern is
// worse than a moderately-sized index chunk that includes the
// long tail.
//
// If a future change makes one of these grow significantly (say,
// a styled-components major upgrade that doubles its size), it
// becomes worth pulling into its own chunk \u2014 but only then.
//
// Maintenance: when adding a new heavy dependency
// ───────────────────────────────────────────────
// 1. Build and check `dist/assets/` for which chunk it landed in.
// 2. If it bloated the `index` chunk past comfort (rule of thumb:
// if index alone is > 1 MiB), add a new entry to manualChunks
// targeting that package name.
// 3. The 2 MiB workbox cap acts as the loud alarm \u2014 if you forget,
// the build fails and tells you exactly which chunk overflowed.
rollupOptions: {
output: {
manualChunks: {
'transformers': ['@huggingface/transformers'],
'konva': ['konva', 'react-konva', 'use-image'],
'react-vendor': ['react', 'react-dom'],
'filerobot': ['react-filerobot-image-editor'],
},
},
},
},
});