Skip to content

Commit c786f5f

Browse files
bearlikemewbo-ai[bot]
andcommitted
⚡️ perf(console): build 85s→11.5s & peak RSS 4.3GB→1.7GB via Vite 8 (Rolldown) (#99)
## New features / improvements - **Production build: 85s → 11.5s wall (vite: 8.2s), peak RSS 4.3GB → 1.72GB.** Migrated `vite` ^5.2.0 → **^8.0.16** (Rolldown-native bundler), `@vitejs/plugin-react` ^4 → ^6, `vite-plugin-pwa` ^1.2 → ^1.3. No app code changes. - **Build script is plain `vite build` again** — the `node --max-old-space-size=4096` OOM guard is obsolete at 1.7GB peak. This also unblocks the console CI `build` job that was OOM-killed (exit 137) on the act runner. - **Hash-stable service-worker precache exclusion**: every chunk containing `@stlite` prebuilt modules now gets a deterministic `stlite-` filename prefix (`build.rollupOptions.output.chunkFileNames`), and the three per-name workbox `globIgnores` patterns collapse to one `**/stlite-*.js`. The old hash-coupled patterns (`PlotlyChart-*` etc.) actually broke under Rolldown's chunking — its renamed stlite index chunk tripped workbox's 2MiB precache hard error. ## Root cause (why the build was slow) App source is tiny (240 files / 2.3MB); the cost was dependency re-bundling, dominated by one package: `@stlite/browser` ships **52MB of prebuilt, already-minified Streamlit JS** (Plotly 7.4MB, index 4.1MB, DeckGL 3.5MB). Importing `@stlite/react` makes the bundler re-parse + re-minify all of it every build — `React.lazy` defers runtime loading, not build work. A control build with `@stlite/react` stub-aliased measured 24s/2.0GB, i.e. stlite alone was ~72% of build time and ~2.3GB of memory. Rolldown absorbs the whole graph natively. ## Stability / traps encoded - `build.cssMinify: "esbuild"` pinned: Vite 8's new lightningcss default hard-errors (`Unexpected token Delim('$')`) on Tailwind's verbatim scan of the `shadow-[var(--${opts.elevation})]` template literal in `composer-shell.tsx`. - vitest 1.6 is insulated from the major bump — it keeps its own nested `vite@5.4.21` dependency; lockfile regenerated cleanly. - `apps/mewbo_console/CLAUDE.md` PWA section updated to the new `stlite-*` globIgnore contract. ## Validation - Build: 11.5s wall / 1.72GB peak RSS (`/usr/bin/time -v`), repeated runs consistent - Dist parity: `index.html`, `sw.js`, `manifest.webmanifest` present; **0** stlite chunks in the precache manifest (204 entries / 7.7MB vs baseline 279 / 13.7MB — the delta is the now-fully-excluded lazy stlite chunks) - `tsc --noEmit` clean, `eslint` clean, **328 vitest tests pass** (4 skipped), dev server ready in 246ms - Caveat: output parity verified structurally (chunks, SW precache, manifest) — a visual pass on a deployed console, especially mounting a stlite widget, is worth doing before redeploy. --------- Co-authored-by: mewbo-ai[bot] <mewbo-ai@mewbo.com> Reviewed-on: https://git.hurricane.home/bearlike/Assistant/pulls/99
1 parent b7ab930 commit c786f5f

4 files changed

Lines changed: 3134 additions & 2656 deletions

File tree

apps/mewbo_console/CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ Belt-and-braces chrome hiding on top of `toolbarMode`: `[&_[data-testid='stHeade
252252
Config lives in `vite.config.ts` under `VitePWA({ workbox: { ... } })`; the update UX lives in `components/UpdatePrompt.tsx` (registered in `src/index.tsx`). Non-trivial rules that MUST hold:
253253

254254
- **`skipWaiting: true` + `clientsClaim: true`.** New SW activates immediately and claims all clients. The opposite config leaves a newly-installed SW in "waiting" state until every open tab closes, which in practice leaves users pinned to the previous deploy's precached `index.html`. That stale `index.html` references hashed bundle names that the server has since deleted → 404 cascade on every lazy chunk → widgets silently fail to mount. This has bitten us hard.
255-
- **Large lazy chunks are in `globIgnores`** (`StliteWidgetPanel-*.js`, `PlotlyChart-*.js`, `DeckGlJsonChart-*.js`) because they exceed Workbox's 2 MiB precache limit. They're served network-only. This HAS to pair with `skipWaiting: true` — stale index.html referencing missing hashed chunks = 404.
255+
- **Large lazy chunks are in `globIgnores`** (`stlite-*.js` — every chunk containing @stlite prebuilt modules gets that deterministic prefix via `build.rollupOptions.output.chunkFileNames` in `vite.config.ts`) because the big ones exceed Workbox's 2 MiB precache limit. They're served network-only. Never globIgnore by upstream chunk name (`PlotlyChart-*`, `DeckGlJsonChart-*`) — those names and hashes drift with every stlite upgrade and bundler chunking change. This HAS to pair with `skipWaiting: true` — stale index.html referencing missing hashed chunks = 404.
256256
- **`sw.js` must be `Cache-Control: no-cache, no-store, must-revalidate`** at nginx (`docker/nginx-console.conf`). If the browser caches `sw.js`, it never sees new versions.
257257
- **`UpdatePrompt.handleReload` is deliberately nuclear**: unregisters every SW via `navigator.serviceWorker.getRegistrations()` + wipes all `caches.keys()` entries, THEN `location.reload()`. Do not trust workbox's `updateServiceWorker(true)` skipWaiting+controllerchange handshake alone — browsers land in weird precache states often enough that brute-force is the only consistent fix.
258258
- **`registerType: "prompt"` + 15-min `registration.update()` tick** (was 60 min). Also re-ticks on `window.focus`. Users see the update notification within ~15 min of a deploy instead of up to an hour.

0 commit comments

Comments
 (0)