Skip to content

Chat: React.lazy/React.Suspense with secureboot integration, build improvements, dependency cleanup#156

Open
velkoff wants to merge 12 commits intomeganz:masterfrom
velkoff:chat-secureboot-lazy
Open

Chat: React.lazy/React.Suspense with secureboot integration, build improvements, dependency cleanup#156
velkoff wants to merge 12 commits intomeganz:masterfrom
velkoff:chat-secureboot-lazy

Conversation

@velkoff
Copy link
Contributor

@velkoff velkoff commented Jan 28, 2026

Currently, the chat bundle is loaded entirely for all MEGA users, but non-trivial portion of them may never interact with the chat. This means that the webclient loads the 1.25 MB chat bundle upfront irrespective of whether given user opens conversation, starts meeting or uses any of the other chat features.

This pull request reduces the initial bundle size to 394 KB (-68%) by introducing lazy-loading via React.lazy/React.Suspense that integrates with the webclient’s secureboot pipeline, ensuring all lazy-loaded chunks go through XHR + SHA-256 hash verification. Additional improvements include deprecating CommonJS instances in favor of ES modules for improved tree-shaking, removal of deprecated/unused dependencies.


Main bundle
Initial bundle: 1,249 KB -> 394 KB (-68%)
Lazy-loaded chunks: 901 KB (7 chunks, loaded on-demand)
Total bundle size: 1,249 KB -> 1,291 KB (+3% overhead)

Note: measured from build artifacts, e.g. local build and uncompressed; live sizes differ due to the release packaging, hashing and compression. Relative reduction is assumed to hold, though.

Lazy-loaded chunks

  • core-ui (373 KB) — main chat/meetings UI
  • call (206 KB) — call UI, join workflow, loading states
  • contacts-panel (105 KB) — contacts panel
  • cloud-browser (85 KB) — cloud browser dialog
  • schedule-meeting (76 KB) — schedule meeting, recurring meetings behavior, edit occurrences
  • start-conversation (39 KB) — start chat/meeting, contact selectors, group chat wizard
  • waiting-room (39 KB) — waiting rooms behavior, admit dialog

The current chunk boundaries are flexible and can be adjusted -- related chunks can be consolidated to reduce the total count and/or split differently to optimize for specific user flows. The current splitting prioritizes grouping components by user flow, as to avoid micro-splitting and network waterfalls. Also, the main bundle can be further shaved off (to ~150 KB), but it requires wider code changes.

React.lazy/React.Supsense + secureboot

  1. React.lazy() triggers __webpack_require__.l(url)
  2. megaChunkLoader.jsx intercepts and extracts the chunk name from the URL
  3. Converts to jsl2 key, ex.: call -> chat:call_js
  4. Loads the specific chunk via the webclient’s M.require() and delegates to it the XHR + hash verification behavior
  5. Resolves on success, the React.Suspense boundary renders the component; fails explicitly if no jsl2 entry is present (default loader is intentionally not preserved as fallback)

[seemegaChunkLoader.jsx for further details]

Additional build improvements and dependency cleanup

  • disabled splitChunks: we want to enforce deterministic and predictable chunks; chunk boundaries are defined manually with webpackChunkName, which allow us to ensure each chunk is known and verified
  • disabled Hot Module Replacement (HMR, hot): hot updates bypass hash verification; ensures consistent behavior with the rest of the webclient and allows us to avoid dynamically injected modules that are unverified at runtime, incl. during development
  • __webpack_require__.l not preserved as default fallback: chunks without jsl2 entries fail hard rather than falling back to unverified script tag injection
  • CommonJS to ES modules migration: all require() -> import statements throughout the chat codebase are migrated, as to improve webpack’s static analysis for tree-shaking
  • ErrorBoundary: wraps the whole chat and catches any uncaught errors in the component tree, not just chunk failures; provides retry/reload recovery behavior and prevents crashes from propagating and breaking the entire chat UI
  • deprecated react-hot-loader: unused (HMR was handling hot reloads); additionally, the upstream is deprecated with the library in maintenance mode; removes 15 transitive dependencies from the build
  • deprecated sass-loader: redundant; the SCSS files are compiled via standalone CLI
  • removed style loaders: unused; style-loader, css-loader and less-loader are not needed as the chat doesn't use dynamic CSS imports
  • updated build.sh: extended build pipeline for the lazy chunks, replaces React.createElement calls and applies JSX_ alias (~2,879 calls, ~45 KB total reduction in total); validates the chunk registry
  • extracted shared constants and utilities: previously, shared constants were attached as static class properties (e.g., LeftPanel.NAMESPACE, ContactsPanel.isVerified), which required the entire component to be imported just to access a constant -- this defeated tree-shaking since the whole class would be pulled into any module needing the constant

Testing

  • verify lazy chunks load correctly (main chat UI, calls, contacts, scheduling flows, interacting with chat components outside its UI -- flyout menu, file manager)
  • block chunk request and confirm ErrorBoundary recovery works
  • tamper chunk and/or remove jsl2 entry and confirm it's rejected by secureboot
  • test behavior with slow network

…ild improvements, dependency cleanup

- introduced code-splitting via `React.lazy`/`React.Suspense` with 68% reduction of the initial bundle size
- introduced `megaChunkLoader` mechanism to route lazy-loaded chunks through the `secureboot` integrity pipeline
- migrated outstanding CommonJS to ES modules for better tree-shaking
- introduced `ErrorBoundary` wrapping the chat to capture any uncaught errors in the component tree
- disabled `splitChunks` and HMR to enforce deterministic, verified builds
- removed unused `react-hot-loader` and style loaders (incl. 15 transitive build dependencies)
- updated `build.sh` with extended build pipeline for the chat bundles
- replaced shared static class properties in favor of shared constants/utilities for better tree-shaking
# Conflicts:
#	js/chat/bundle.js
#	js/chat/ui/conversations.jsx
#	package-lock.json
#	package.json
# Conflicts:
#	js/chat/bundle.js
# Conflicts:
#	js/chat/bundle.js
# Conflicts:
#	package-lock.json
#	package.json
# Conflicts:
#	js/chat/bundle.js
# Conflicts:
#	js/chat/bundle.js
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant