Skip to content

build(frontend): upgrade rsbuild to 2.2.1 - #2613

Merged
malinskibeniamin merged 4 commits into
masterfrom
ben-malinski/rsbuild-2-2-beta-upgrade
Sep 2, 2026
Merged

build(frontend): upgrade rsbuild to 2.2.1#2613
malinskibeniamin merged 4 commits into
masterfrom
ben-malinski/rsbuild-2-2-beta-upgrade

Conversation

@malinskibeniamin

@malinskibeniamin malinskibeniamin commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Proven impact

Decision rule: prove at least a 20% reduction in the largest emitted JavaScript asset while keeping total emitted JavaScript growth below 1% and preserving production runtime entrypoints.

Lane Metric Merge-base d146e9809 PR head 4954d83e7 Delta
Codebase Largest emitted JavaScript asset 10,284,413 B 5,954,958 B -4,329,455 B (-42.1%)
Product guardrail Total emitted JavaScript 43,187,229 B 42,988,490 B -198,739 B (-0.46%)
Delivery surface Emitted JavaScript files 560 487 -73 (-13.0%)

Selected-cap A/B at exact PR head:

Metric No cap 4 MiB cap Delta
Largest async JavaScript asset 10,059,699 B 5,650,771 B -4,408,928 B (-43.8%)
Async JavaScript files 468 481 +13 (+2.8%)
Total emitted JavaScript 42,935,714 B 42,988,490 B +52,776 B (+0.12%)

A 512 KiB cap emitted 595 async JavaScript files (+127 versus no cap) but did not improve the 5,650,771 B maximum reached at 4 MiB, so 4 MiB is the measured Pareto choice.

Value proven: the primary metric exceeded the 20% threshold, total JavaScript improved rather than regressed, and the final cap captures the chunk-size benefit with 114 fewer async files than 512 KiB.

Method: bun run build-local-test with fixed build variables and independent frozen-lockfile installs for merge-base d146e9809 and exact PR head 4954d83e7; production asset bytes enumerated from clean build/**/*.js outputs. Chunk-cap A/B used createRsbuild().build() at exact head with identical source/dependencies and only maxAsyncSize varied. macOS 26.6.2, Apple M5 Max, AC power, Bun 1.4.0. Exact byte counts are deterministic; build timings are deliberately excluded because the earlier timing sample did not measure the final commit under the controlled paired protocol.

Runtime guardrail: whole-PR dogfood passed overview/topics/SQL/deep-link/invalid routes, 30/30 Module Federation sync assets, and 4/4 Monaco workers; all fetched artifacts returned HTTP 200 and were byte-identical, with zero chunk/worker browser errors. No live Redpanda backend was available.

Summary

  • Upgrade Rsbuild to the 2.2.0 GA release and update every official Rsbuild plugin to its latest compatible release.
  • Replace the Babel React Compiler transform with Rsbuild's native Rust implementation and remove the React 19-unneeded compiler runtime.
  • Enable every generally applicable Rspack experiment: async WebAssembly, future defaults, native watcher, production pure-function analysis, and Rsbuild-provided source imports.
  • Keep feature-specific or incompatible experiments off: remote HTTP builds, unused deferred-import syntax, custom filesystem routing, and the unfinished ESM-only Rspack runtime mode.
  • Remove fasterModuleConcatenation and lazyBarrel, which Rspack removed before GA.
  • Parallelize SVGR and split asynchronous legacy UI and Monaco vendors into reusable chunks with a measured 4 MiB async cap.
  • Exercise the fully resolved production config, including the real Module Federation plugin, in the integration suite.

Why

Adopt Rsbuild 2.2's GA compiler, watcher, forward defaults, and bundling improvements while reducing the worst emitted chunk size. Application behavior and backend integrations are out of scope.

Surface review skipped: build-only change; no rendered UI changed.

Experiment scope

  • asyncWebAssembly: true: current Rspack default, pinned explicitly for the supported async Wasm pipeline.
  • futureDefaults: true: opts into the next-major defaults now so incompatibilities fail during deliberate upgrades.
  • nativeWatcher: true: uses Rust file watching for development rebuilds.
  • pureFunctions: isProd: enables cross-module pure-function analysis only where production tree shaking applies.
  • sourceImport: true: enabled by Rsbuild 2.2 and protected by the config contract.
  • Excluded buildHttp because Console has no remote URL imports and enabling network-backed builds would expand the supply-chain surface.
  • Excluded deferImport because Console uses no deferred-import proposal syntax.
  • Excluded runtimeMode: 'rspack' because it remains under development and targets ESM modern-module output, while Console uses Module Federation's current runtime.
  • Excluded useInputFileSystem because Console does not replace Rspack's input filesystem.

Commits

  • 8df6382bb build(frontend): upgrade rsbuild to 2.2
  • b5e10815c build(frontend): enable rspack experiments
  • 4954d83e7 build(frontend): address rsbuild review feedback

Reviewer guide

Start with frontend/rsbuild.config.ts, then review dependency intent in frontend/package.json, the fully resolved config contract in frontend/tests/rsbuild-config.integration.test.ts, suite routing in the two Vitest configs, and finally the generated lockfiles.

Dogfood evidence

  • Verdict: PASS
  • Entrypoints: bun run start -- --port 57086, bun x rsbuild preview --port 57087, production browser routes, emitted worker files, and Module Federation HTTP artifacts.
  • Actions and break attempts: Triggered native-watcher HMR; navigated and deep-reloaded overview, topics, SQL, and invalid routes; typed into the Monaco SQL editor; fetched all federation sync assets and the editor, JSON, TypeScript, and YAML workers; compared fetched bytes with emitted files; checked worker syntax and async-loader references.
  • Observations: HMR rebuilt in 3.80 s without losing page state. Production routes and invalid-route recovery rendered. All federation artifacts and worker files returned HTTP 200 and matched emitted bytes. All worker files passed syntax checks and contained no async chunk-loader references. No chunk or worker errors appeared in the browser.
  • Repairs and replay: Moved the production-config contract to the integration suite, removed the Module Federation mock and upstream-default assertion, selected the measured 4 MiB cap, rebuilt, and replayed every entrypoint successfully.
  • Limits: No live Redpanda backend, so live cluster data workflows were not exercised; expected Connect/proxy errors rendered existing unavailable states.

Dependency upgrade path

  • Upgrade evidence: Production config contracts were developed RED → GREEN; frozen install, changed-file lint, type checking, unit tests, focused integration, production and RsDoctor builds, bundle analysis, and browser dogfood passed.
  • Packages: @rsbuild/core@2.2.0; React 2.1.0; Sass 2.0.1; SVGR 2.0.5; Tailwind CSS 2.0.3; YAML 2.0.0; Node polyfill remains latest at 1.4.6. Removed @rsbuild/plugin-babel, babel-plugin-react-compiler, and react-compiler-runtime.
  • SemVer confidence: Core is exactly pinned to GA 2.2.0; bun.lock contains exactly one @rspack/core@2.2.0 and one matching @rspack/binding@2.2.0. Official plugin upgrades use their current compatible releases.
  • Risk gate: Exact core pinning contains API drift, the real resolved-config integration contract protects adopted APIs, and development, production, worker, and federation entrypoints were exercised.
  • Security notes: No remote-build experiment was enabled; frozen install reproduced the reviewed lockfiles.

Test plan

  • bun install --frozen-lockfile
  • bun run lint:check:file rsbuild.config.ts tests/rsbuild-config.integration.test.ts vitest.config.integration.mts vitest.config.unit.mts
  • bun run type:check
  • Unit suite: 57 files, 837 tests
  • Real Rsbuild/Module Federation config integration contract
  • Production build and RSDOCTOR=1 production build
  • Async chunk-threshold sweep and emitted bundle analysis
  • Development HMR and production browser dogfood

@malinskibeniamin malinskibeniamin self-assigned this Aug 24, 2026

@malinskibeniamin malinskibeniamin left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated /review: 9 finding(s).

These findings could not be anchored to a changed line:

  • P1 frontend/bun.lock:400Duplicate @rspack/core instance for module federation.

@rsbuild/core@2.2.0-beta.2 resolves @rspack/core@2.2.0-rc.0, but this lock now nests a second copy: @module-federation/rspack/@rspack/core → 2.0.6 (and the same for @rsdoctor/rspack-plugin / @rsdoctor/types). Before this change everything deduped on 2.0.6.

@module-federation/rspack imports @rspack/core directly, so its plugin classes and hook taps come from a different rspack instance — and a different native binding — than the compiler that runs them. Console ships embedded.js as a federated remote consumed by Cloud UI, so this sits on the critical output path, and it also pulls a second rspack native binary into the install.

Correction: align the versions — either hold @rsbuild/core on the 2.0.x line until @module-federation/rsbuild-plugin supports rspack 2.2, or add an override so a single @rspack/core resolves for all consumers.

Verify: cd frontend && bun install && bun pm ls | grep rspack (expect exactly one @rspack/core and one @rspack/binding), then bun run build and load embedded.js from a federation host.

  • P2 frontend/package.json:788A beta build toolchain is pinned for release builds, with a floating prerelease range underneath.

@rsbuild/core: 2.2.0-beta.2 declares @rspack/core: ~2.2.0-beta.1, which the lock satisfied with @rspack/core@2.2.0-rc.0 — a different prerelease than the one named. That range keeps floating across prereleases (beta.3, rc.1, …) on any lock refresh, so the bundler that produces the shipped artifact can change without a reviewed diff, and prereleases carry no compatibility guarantee.

Correction: if the 2.2 features are needed now, state that in the commit body and add an exact override for @rspack/core so the prerelease cannot drift; otherwise wait for @rsbuild/core@2.2.0 stable.

Verify: cd frontend && bun install --frozen-lockfile && bun pm ls | grep '@rspack/core' — the resolved version should equal the pinned one on a clean install.

Comment thread frontend/rsbuild.config.ts
Comment thread frontend/rsbuild.config.ts Outdated
Comment thread frontend/tests/rsbuild-config.test.ts Outdated
Comment thread frontend/tests/rsbuild-config.test.ts Outdated
Comment thread frontend/tests/rsbuild-config.test.ts Outdated
Comment thread frontend/tests/rsbuild-config.integration.test.ts
Comment thread frontend/rsbuild.config.ts Outdated
@malinskibeniamin
malinskibeniamin requested review from a team, Mateoc, SpicyPete, jvorcak and sago2k8 and removed request for a team August 24, 2026 13:18
@malinskibeniamin
malinskibeniamin force-pushed the ben-malinski/rsbuild-2-2-beta-upgrade branch from f20a76e to 8df6382 Compare August 26, 2026 22:36
@malinskibeniamin malinskibeniamin changed the title build(frontend): upgrade rsbuild to 2.2.0-beta.2 build(frontend): upgrade rsbuild to 2.2.0 Aug 26, 2026
@malinskibeniamin
malinskibeniamin marked this pull request as ready for review August 27, 2026 12:36
@malinskibeniamin
malinskibeniamin enabled auto-merge (squash) August 27, 2026 12:36
@malinskibeniamin

Copy link
Copy Markdown
Contributor Author

Review feedback addressed

  • Monaco worker safety: verified all emitted workers are standalone child-compiler assets; production fetches, byte comparisons, syntax checks, loader-reference checks, and SQL-editor browser dogfood passed with no chunk/worker errors.
  • Removed GA experiments: documented that Rspack 2.2 GA removed lazyBarrel and fasterModuleConcatenation; removed the brittle upstream-default assertion.
  • Production config contract: removed the Module Federation mock, moved the contract into the integration suite, and adopted repository-standard test().
  • Chunk threshold: replaced the unsupported 512 KiB cap with a measured 4 MiB cap: largest async chunk 10.06 → 5.65 MB while adding 13 rather than 127 async files.
  • Dependency resolution: GA core is exactly pinned; the lockfile has one @rspack/core@2.2.0 and one matching @rspack/binding@2.2.0.
  • PR evidence: refreshed the description with current impact numbers, commit/read order, tests, dependency risk, and whole-PR dogfood receipt.

All review threads resolved. CI is green, including Enterprise E2E and Snyk.

@malinskibeniamin
malinskibeniamin force-pushed the ben-malinski/rsbuild-2-2-beta-upgrade branch from 4954d83 to cbffb6b Compare September 2, 2026 10:25
@malinskibeniamin malinskibeniamin changed the title build(frontend): upgrade rsbuild to 2.2.0 build(frontend): upgrade rsbuild to 2.2.1 Sep 2, 2026
@malinskibeniamin
malinskibeniamin merged commit 4f95903 into master Sep 2, 2026
17 checks passed
@malinskibeniamin
malinskibeniamin deleted the ben-malinski/rsbuild-2-2-beta-upgrade branch September 2, 2026 22:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants