Skip to content

dx: halve studio's dev boot with vite 8 bundling - #101

Merged
mutewinter merged 2 commits into
mainfrom
vite-8-bundling-perf
Aug 7, 2026
Merged

dx: halve studio's dev boot with vite 8 bundling#101
mutewinter merged 2 commits into
mainfrom
vite-8-bundling-perf

Conversation

@mutewinter

@mutewinter mutewinter commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Cuts Studio's dev boot roughly in half and its build to under a third, by moving the bundling onto Vite 8 (Rolldown/Oxc) and giving the dev server something to do while Electron starts.

Measured on an M5, 3 runs each. Boot is studio-drive boot, i.e. spawn pnpm dev and wait until the renderer is drivable, which is what an agent validating a change actually pays.

boot to drivable prod-shaped build
before 9.6-9.9s 28.7s
after dx: move studio's bundling… 5.10s 7.9s
after studio: warm the route modules… 4.62-4.86s 7.9s

Where it went, per boot:

stage before after
main-process bundle (1709 modules) 4,303ms ~310ms
preload 82ms 3ms
Electron spawn to first main log ~250ms ~250ms
boot steps (setupBinDirectorycreateMainWindow) ~70ms ~70ms
renderer load ~5,400ms ~3,500ms

The boot steps were already fast. Essentially all of it was bundling and the dev server.

The two commits

dx: move studio's bundling onto vite 8 and electron-vite 6 — Rolldown replaces Rollup for the main and preload bundles, which every dev boot rebuilds from scratch since there is no persistent cache. plugin-react 6 drops Babel for the refresh transform in favour of Oxc, and keeps React Compiler on @rolldown/plugin-babel, whose reactCompilerPreset filter keeps most modules away from Babel entirely.

studio: warm the route modules while electron boots — the dev server is listening about a second before the renderer asks for anything, and routeTree.gen.ts imports all 40 route files eagerly, so a boot pulls ~625 modules through the browser's six-connection limit. Transforming the route files server-side during that idle window is worth ~0.5s.

Two things worth a look

Vite is pinned to ^8.2.0, not 8.2.1. minimumReleaseAge: 10080 rejected 8.2.1 as too fresh. 8.2.0 measured ~0.2s slower on boot than 8.2.1 did in a scratch worktree. Nothing to do, just noting the policy is why.

electron-vite is a beta. 6.0.0-beta.1 (April 2026, no stable since) is the only release that accepts Vite 8. It has a real bug: it deep-clones the config before resolving async plugin factories, so @rolldown/plugin-babel dies with Cannot deep clone non-plain object. Hence the hoisted await babel(...) in electron.vite.config.ts, with a comment saying why. Worth an upstream issue if we would rather not carry the workaround.

Tried and rejected

  • Vite 8.1 bundled dev mode (experimental.bundledDev): 4.89s, no gain, and it cancels out the warmup. The app code-splits heavily, so unbundled dev only pays for the initial route while bundled dev pays for everything.
  • Warming all client files instead of just routes: 7.45s. The backlog starves the requests on the critical path.
  • Dropping dev sourcemaps: 9.7s to 9.35s on the old stack. Not worth losing sourcemaps.

Testing

check:types, check:build, check:unused and test:ci pass. Booted the app and opened the PDF viewer, which exercises pdfium WASM plus the module-worker optimizeDeps workarounds — the riskiest part of this config.

Not verified: test:browser, Windows, packaged electron-builder output, and the HMR edit loop.

check:lint still fails on two things that fail identically on main and are untouched here: root package.json script ordering, and a Tailwind class-order error at composer-frame.tsx:58.

Still on the table

The remaining ~3.5s is renderer-side and structural: the route tree's eager imports are by design (critical route config must load statically), and ~15 of the 40 routes are dev-only /debug/ pages. Options for later, none done here: trim the eager route imports, serve dev over HTTP/2 to lift the six-connection cap, or for booting several copies of the same code, build once and electron-vite preview per instance so instances 2..N start in ~1.5s.

Summary by CodeRabbit

  • Performance

    • Improved development startup responsiveness by preloading commonly used application and navigation resources.
    • Updated the application’s build tooling to support the latest React optimization capabilities.
  • Chores

    • Upgraded core development tooling and compiler integrations to improve reliability and maintainability.

Rolldown replaces rollup for the main and preload bundles, which every dev
boot rebuilds from scratch: 4.3s to 0.3s, and a prod-shaped build 28.7s to
7.9s. plugin-react 6 drops babel for the refresh transform in favour of oxc
and keeps the react compiler on `@rolldown/plugin-babel`, whose preset
filters out most modules before babel sees them.

vite is held at 8.2.0 because 8.2.1 is inside the minimumReleaseAge window.
electron-vite 6 is still a beta; it is the only release that accepts vite 8.
The dev server is listening about a second before the renderer asks for
anything, and the generated route tree imports all 40 route files eagerly, so
a boot pulls ~625 modules through the browser's six-connection limit.
Transforming the route files on the server during that idle window takes a
boot from 5.1s to 4.6s.

Warming every client file instead is worse (7.5s): the backlog starves the
requests actually on the critical path.
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The Studio build setup separates React Compiler Babel integration from the React plugin, updates Vite and Electron tooling versions, and adds renderer server warmup for the main client and route modules.

Changes

Studio build tooling

Layer / File(s) Summary
Vite and Babel toolchain updates
apps/studio/package.json, pnpm-workspace.yaml
Adds Babel dependencies, updates electron-vite, and updates the workspace versions for Vite and @vitejs/plugin-react.
Web Babel configuration
apps/studio/web/vite.config.ts
Uses the default React plugin and a separate Babel plugin configured with reactCompilerPreset().
Electron renderer configuration
apps/studio/electron.vite.config.ts
Initializes shared React Compiler Babel settings, applies them through the Babel plugin, and adds renderer warmup entries for the main client and route modules.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the Vite 8 bundling change and its development boot-time performance goal.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch vite-8-bundling-perf

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mutewinter
mutewinter merged commit 79b7631 into main Aug 7, 2026
1 of 3 checks passed
@mutewinter
mutewinter deleted the vite-8-bundling-perf branch August 7, 2026 16:59

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/studio/package.json (1)

103-103: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use workspace catalog references for the toolchain dependencies.

At Lines 103, 149, and 186, apps/studio/package.json uses literal versions. Add these versions to the catalog in pnpm-workspace.yaml, then use "catalog:" here.

Proposed fix
+# pnpm-workspace.yaml
+  "`@babel/core`": ^7.29.0
+  "`@rolldown/plugin-babel`": ^0.2.0
+  electron-vite: 6.0.0-beta.1

-    "`@babel/core`": "^7.29.0",
+    "`@babel/core`": "catalog:",
-    "`@rolldown/plugin-babel`": "^0.2.0",
+    "`@rolldown/plugin-babel`": "catalog:",
-    "electron-vite": "6.0.0-beta.1",
+    "electron-vite": "catalog:",

Verify the manifest and catalog:

#!/usr/bin/env bash
set -euo pipefail

rg -n '"(`@babel/core`|`@rolldown/plugin-babel`|electron-vite)": "catalog:"' apps/studio/package.json
rg -n '^  ("`@babel/core`"|"`@rolldown/plugin-babel`"|electron-vite):' pnpm-workspace.yaml

As per coding guidelines: **/package.json: Package manifests use catalog: dependency versions; resolve actual catalog versions from the root pnpm-workspace.yaml.

Also applies to: 149-149, 186-186

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/studio/package.json` at line 103, Replace the literal versions for
`@babel/core`, `@rolldown/plugin-babel`, and electron-vite in
apps/studio/package.json with catalog: references, and add their existing
versions to the root pnpm-workspace.yaml catalog. Preserve the resolved
dependency versions and verify all three manifest entries and catalog keys are
present.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pnpm-workspace.yaml`:
- Line 36: Update the dependency versions in the workspace configuration to
resolvable published releases: replace `@vitejs/plugin-react` ^6.0.5 with the
latest stable published version and Vite ^8.2.0 with the latest stable published
version, or use the exact published beta version only if the beta toolchain is
intentionally required.

---

Nitpick comments:
In `@apps/studio/package.json`:
- Line 103: Replace the literal versions for `@babel/core`,
`@rolldown/plugin-babel`, and electron-vite in apps/studio/package.json with
catalog: references, and add their existing versions to the root
pnpm-workspace.yaml catalog. Preserve the resolved dependency versions and
verify all three manifest entries and catalog keys are present.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2a7235cf-3af3-45e1-9b62-658c3c77bc29

📥 Commits

Reviewing files that changed from the base of the PR and between 0292b1a and 639246b.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (4)
  • apps/studio/electron.vite.config.ts
  • apps/studio/package.json
  • apps/studio/web/vite.config.ts
  • pnpm-workspace.yaml

Comment thread pnpm-workspace.yaml
"@types/react-dom": ^19.2.3
"@typescript/native-preview": 7.0.0-dev.20260702.3
"@vitejs/plugin-react": ^5.1.2
"@vitejs/plugin-react": ^6.0.5

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Use published dependency versions.

Line 36 requests @vitejs/plugin-react ^6.0.5, but the public registry currently lists 6.0.4 as latest. Line 44 requests Vite ^8.2.0, while the public registry lists 8.1.5 as latest stable and 8.2.0-beta.0 as the prerelease. Unless CI uses a private registry, a normal install cannot resolve these ranges. (npmjs.com)

Use published stable versions, or declare the exact prerelease version if the beta toolchain is required.

-  "`@vitejs/plugin-react`": ^6.0.5
+  "`@vitejs/plugin-react`": ^6.0.4
...
-  vite: ^8.2.0
+  vite: ^8.1.5

Verify the requested versions:

#!/usr/bin/env bash
set -euo pipefail

npm view `@vitejs/plugin-react`@6.0.5 version
npm view vite@8.2.0 version

Also applies to: 44-44

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pnpm-workspace.yaml` at line 36, Update the dependency versions in the
workspace configuration to resolvable published releases: replace
`@vitejs/plugin-react` ^6.0.5 with the latest stable published version and Vite
^8.2.0 with the latest stable published version, or use the exact published beta
version only if the beta toolchain is intentionally required.

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