Skip to content

Upgrade to Vite 8.1 repo-wide (Rolldown/Oxc)#1543

Merged
srid merged 9 commits into
masterfrom
vite81
Jun 23, 2026
Merged

Upgrade to Vite 8.1 repo-wide (Rolldown/Oxc)#1543
srid merged 9 commits into
masterfrom
vite81

Conversation

@srid

@srid srid commented Jun 23, 2026

Copy link
Copy Markdown
Member

Every Vite-built surface moves from Vite 6.4 to Vite 8.1 — a two-major jump that swaps the bundler from Rollup/esbuild to Rolldown/Oxc. All surfaces build, the full test suite passes, just dev runs correctly (verified end-to-end via chrome-devtools — kolu loads, connects, creates a working terminal), and the production build is ~3× faster (full numbers below).

Scope correction: an earlier revision of this PR also enabled Vite 8.1's experimental bundled dev mode. It crashes kolu's client (Uncaught ReferenceError: __reExport is not defined, app never mounts) — filed upstream as vitejs/vite#22756 (same family as #22419 / rolldown-vite#563). It is dropped; standard Vite 8 dev works. The config carries a NOTE to re-enable once the upstream bug is fixed.

Performance

Measured head-to-head on one ephemeral box, vite81 (Vite 8.1) vs master (Vite 6.4), same machine, warm caches. Medians; raw runs in footnotes.

Build time — the win

Metric Vite 6.4 (Rollup/esbuild) Vite 8.1 (Rolldown/Oxc) Δ
Client vite build (3 runs) ~6.4 s ¹ ~2.1 s ² −67% (~3×)
nix build .#default (forced real build) 27 s 23 s −15% (~4 s)

vite build is the same step the Nix client derivation runs, so the ~4 s saving flows straight into nix build. The packaged build only improves ~15% because the rest of it (pnpm install, node-gyp native rebuild, server packaging, NAR) is Vite-independent.

Bundle size — a small tradeoff

Rolldown's output is slightly larger in aggregate, though the first-load entry chunk is flat:

Metric Vite 6.4 Vite 8.1 Δ
Main entry chunk (gzip) 751 KB 743 KB −1% (flat)
Total JS, gzip 2.36 MB 2.58 MB +9%
Total JS, raw 11.9 MB 12.5 MB +5%
JS chunk count 309 310 +1
Total dist/ 14.2 MB 14.8 MB +4%

The +9% gzip is in the lazy-loaded chunks (the 300+ Shiki grammar chunks, fetched on demand), not the startup path — so it doesn't move first-load (next table).

Runtime — flat (as expected)

Metric Vite 6.4 Vite 8.1 Δ
Client load + mount (prod bundle, headless Chrome) ~246 ms ~285 ms +~40 ms
Production server boot → /api/health 1.61 s 1.52 s flat ³
Unit suite (vitest, client, 457 tests) ~7–8 s ~7–8 s flat
Dev cold startup (standard, ready in) ~474 ms ~501 ms flat

³ The server runs under tsx, not Vite, so its boot time is Vite-independent — measured to confirm no regression. Client load+mount is marginally slower, tracking the slightly larger bundle; the experimental bundled-dev startup win is dropped (scope correction above).

¹ 6.35 / 6.32 / 6.96 s   ² 2.17 / 2.16 / 2.09 s   · nix build forced to actually build (source change) so neither side substitutes from cache.

Surfaces moved

Surface Before → after How
packages/client, packages/pulam-web, 4 @kolu/* examples vite ^6.4.3^8.1.0 direct devDep bump; peers (vite-plugin-solid, @tailwindcss/vite, vitest) re-resolved clean
docs/atlas, website (Astro 7) vite 8.0.168.1.0 Vite is transitive under Astro 7; pinned to 8.1 via the existing pnpm.overrides mechanism (Astro declares ^8.0.13)

Node is already 24.13 in the Nix toolchain, above Vite 8.1's 20.19/22.12 floor — no toolchain bump. pnpmDeps / website-pnpm-deps FOD hashes regenerated. Vite 8.1.0 ≥ 8.0.16 also clears the 8.0.0–8.0.15 advisory.

Migration fallout cleaned up

  • Dropped optimizeDeps.esbuildOptions across all six configs. It dodged an esbuild ≥0.27.7 destructuring-lowering bug (kolu#1387); Vite 8 optimizes deps with Rolldown/Oxc, not esbuild, so the workaround is obsolete and deprecated. build.target: esnext is retained.
  • @kolu/solid-pierre now exposes its pure path helpers via a ./paths subpath. Vite 8's Rolldown/Oxc SSR transform won't parse raw JSX from an externalized source .tsx the way Vite 6's esbuild did. Three DOM-free client logic tests pulled CodeView.tsx into their graph only because ancestorDirectoryPaths was imported from the component barrel; routing pure consumers through ./paths decouples them (dead barrel re-export then removed — lens-debate).

What was deliberately not adopted

  • Experimental bundled dev mode — crashes the client (vitejs/vite#22756).
  • Build-output experiments (chunk import map, Wasm-as-build) — left off so the reproducible nix build artifact never depends on an experimental flag.
  • Stable features (import.meta.glob caseSensitive, Wasm ESM, html.additionalAssetSources) — no call sites, so wiring them in would be dead config.

Verification

  • All six Vite surfaces build; pnpm typecheck green; full unit suite green (client 457 tests, all packages).
  • website + atlas build on Astro 7 + Vite 8.1.
  • just dev + just dev-auto run; chrome-devtools confirms kolu loads, connects, and creates a working terminal — no app-breaking console errors (the only [error] is the pre-existing dev-only /sw.js no-op, present on master).
  • Full CI green on x86_64-linux + aarch64-darwin (32 contexts).

Try it locally

nix run github:juspay/kolu/vite81

Generated by /be on Claude Code (model claude-opus-4-8).

srid and others added 7 commits June 23, 2026 14:59
Vite 8 pre-bundles deps with Rolldown/Oxc, not esbuild, so the esbuild
destructuring-lowering workaround (kolu#1387) is obsolete and the option
is deprecated. build.target esnext is retained for production output.
Vite 8 (Rolldown/Oxc) cannot parse raw JSX from an externalized source .tsx
the way Vite 6's esbuild did. Pure-logic client tests pulled solid-pierre's
CodeView.tsx into their graph only because ancestorDirectoryPaths was imported
from the component barrel. Expose pathReconcile via a ./paths subpath so pure
consumers (fileSearch, lineRef) no longer drag in a Solid component.
Drop the barrel re-export so the pure helper is reachable only via @kolu/solid-pierre/paths, making the pure-consumers-don't-pull-in-JSX property structural.

Agreed by the lowy ⇄ hickey lens debate (finding lowy-2, raised by lowy). Not pushed or merged.
…figs

Keep only the terse build.target line in the four example vite configs; the single authoritative rationale stays in packages/client/vite.config.ts.

Agreed by the lowy ⇄ hickey lens debate (finding hickey-3, raised by hickey). Not pushed or merged.
@srid

srid commented Jun 23, 2026

Copy link
Copy Markdown
Member Author

⚖️ Lowy ⇄ Hickey lens debate

Consensus after 1 round(s) · lowy + hickey · base e6bde5d82f09

Independent findings: lowy=4, hickey=3

Applied (3)

  • lowy-2 ancestorDirectoryPaths now has two public entry points (duplicated receptacle face) — commit 37abe6d41
  • hickey-1 ancestorDirectoryPaths now reachable through two public import paths — (uncommitted)
  • hickey-3 Identical esbuild-removal rationale comment copy-pasted across four example configs — commit 9e2ab61c9

Agreed — no change (4)

  • lowy-1 /paths subpath is a bundler-driven seam, not a volatility axis (packages/solid-pierre/package.json:12)
  • lowy-3 experimental.bundledDev is an enabled experimental knob (philosophy tension, correctly bounded) (packages/client/vite.config.ts:79)
  • lowy-4 Twin bundledDev blocks duplicated across client and pulam-web with no shared config (packages/pulam-web/vite.config.ts:70)
  • hickey-2 bundledDev experimental flag and its rationale duplicated across two vite configs (packages/client/vite.config.ts:71-80 and packages/pulam-web/vite.config.ts:66-73)

@srid

srid commented Jun 23, 2026

Copy link
Copy Markdown
Member Author

Codex ⇄ Claude debate

Consensus after 1 round(s) · codex reviewed at xhigh reasoning effort · base e6bde5d82f09

Round 1

codex — approved: true

No findings. I reviewed the diff against e6bde5d, including the Vite 8.1 package and lockfile updates, the bundledDev config changes, the @kolu/solid-pierre/paths export split, and the Atlas/website/Nix hash updates. I also ran git diff --check, tsc --noEmit for packages/client and packages/solid-pierre, and a standalone typecheck over the edited Vite config files; all passed. I could not run Vite's resolveConfig in this read-only pass because Vite's config loader tries to create .vite-temp.

Findings:

  • (none)

claude(no author turn this round)

(no author turn — the debate ended this round)

@srid

srid commented Jun 23, 2026

Copy link
Copy Markdown
Member Author

👮 Code-police

Pass Issues Result
Rules 0 Clean
Fact-check 0 Clean
Elegance Skipped (--no-elegance; /simplify ran this gauntlet)

All clear. The source-level changes are the Vite 8 config migration (optimizeDeps.esbuildOptions removed, experimental.bundledDev added), the @kolu/solid-pierre/paths subpath split, and the two consumer import updates — no logic errors, no rule violations.

@srid

srid commented Jun 23, 2026

Copy link
Copy Markdown
Member Author

Evidence

This is a dev-tooling upgrade: the experimental bundled dev mode is dev-only and the production vite build output is byte-for-byte unchanged, so there is no on-screen UI diff to show. The substantive, observable result is the dev-server benefit — measured cold on an ephemeral pu box, toggling only experimental.bundledDev (same Vite 8.1.0, 3 runs each, headless Chromium navigating the real client at a random dev-auto port).

Cold page load — bundled dev mode on vs off

bundledDev cold page load (load event) dev requests (JS) dev server ready in
on (shipped) 348 / 348 / 417 ms 4 (2 JS) 504 / 465 / 382 ms
off 2503 / 2351 / 2629 ms 411 (402 JS) 366 / 366 / 367 ms

~7× faster cold page load and ~100× fewer dev requests — Rolldown serves the app as one bundle instead of streaming 400+ unbundled ES modules. The modest upfront ready in cost (~100 ms) is repaid on every reload.

Both dev entrypoints serve the client over Vite 8.1

$ just dev          → server :7681  client :5173   VITE v8.1.0  ready in 509 ms   HTTP 200
$ just dev-auto     → server :45029 client :58917  VITE v8.1.0  ready in 535 ms   HTTP 200

The served shell references /assets/index.js (a Rolldown-bundled artifact) rather than raw source modules — confirming bundled dev mode is engaged.

Build & test verification (pu box, all green)

  • All six Vite surfaces build (client vite build ~2.3s; pulam-web + examples sub-300ms).
  • pnpm typecheck green; full unit suite green — client 457 tests, all packages pass.
  • website + atlas build on Astro 7 + Vite 8.1 (website 12 pages, atlas 57 pages; committed atlas dist/ is byte-identical).

Captured on an ephemeral pu box per the project's off-machine evidence policy.

@srid

srid commented Jun 23, 2026

Copy link
Copy Markdown
Member Author

🧪 CI metrics — leased pool box

The x86_64-linux lane ran on no pool box — resolved via hosts.json — commit ?, verdict unknown (no per-node timing)

  • Lane wall (pipeline): 0s

(no per-recipe timing in .ci/pc.log)

Pool status (8 boxes)

box location state
kolu-ci-1 dev-x86-64-linux-04 ✓ idle
kolu-ci-2 dev-x86-64-linux-04 ✓ idle
kolu-ci-3 dev-x86-64-linux-03 ✓ idle
kolu-ci-4 dev-x86-64-linux-08 ✓ idle
kolu-ci-5 idliv2-02 ✓ idle
kolu-ci-6 dev-x86-64-linux-03 🔒 leased
kolu-ci-7 dev-x86-64-linux-05 ✓ idle
kolu-ci-8 dev-x86-64-linux-08 ✓ idle

Posted by ci/pu/report.sh. Lane timings from .ci/pc.log; pool state is a live flock probe.

srid added 2 commits June 23, 2026 17:21
experimental.bundledDev:true emits a fatal `__reExport is not defined` at
runtime (a Rolldown dev-bundling CJS-interop helper 8.1.0 references but does
not emit), so the client never mounts under `just dev` — verified via
chrome-devtools. CI never caught it because the e2e lane exercises the
production build (koluBin), not the dev server. Standard Vite 8 dev works:
chrome-devtools confirms kolu loads, connects (SRV/KAVAL healthy), and creates
a working terminal. The Vite 6→8.1 upgrade itself stands; only the broken
experimental dev flag is dropped.
@srid srid changed the title Upgrade to Vite 8.1 repo-wide; adopt experimental bundled dev mode Upgrade to Vite 8.1 repo-wide (Rolldown/Oxc) Jun 23, 2026
@srid

srid commented Jun 23, 2026

Copy link
Copy Markdown
Member Author

Evidence — corrected (chrome-devtools)

⚠️ Retraction of the perf table in my earlier ## Evidence comment. That A/B compared bundledDev on vs off by page-load time and request count. Driving the client in a real browser revealed bundledDev: true crashes koluUncaught ReferenceError: __reExport is not defined, the app never mounts. So the "on" column was measuring a bundle that loaded fast because it threw and never rendered — the comparison is invalid and is withdrawn. bundledDev is dropped from this PR (commit fa8b344).

What chrome-devtools confirms at 4f0d5d646 (standard Vite 8.1 dev, on a pu box, tunneled to local Chrome)

  • kolu loads and mountsdocument.title === "kolu", app body renders (15 KB), status bar shows SRV v1.1.0 dev / CLIENT / KAVAL all healthy (no "Connecting…").
  • Creating a terminal works — clicked New terminal → In current directory → ⏎; a live ~ bash xterm tile opened with a working [Incus] ❯ prompt (screenshot below).
  • No app-breaking console errors. The only [error] is the pre-existing, documented dev-only /sw.js registration no-op (the config comment explains /sw.js is prod-served-only; registerServiceWorker() no-ops in dev — present on master). Remaining messages are benign Solid dev warnings and headless-Chrome software-WebGL notices.

kolu running a terminal under Vite 8.1

Captured on an ephemeral pu box (kolu dev server) tunneled to a local headless Chrome via the chrome-devtools MCP.

@srid

srid commented Jun 23, 2026

Copy link
Copy Markdown
Member Author

🧪 CI metrics — leased pool box

The x86_64-linux lane ran on kolu-ci-8 (dev-x86-64-linux-08) — commit 4f0d5d646, passed

  • Lane wall (pipeline): 3m25s
recipe duration
ci::e2e 2m41s
ci::home-manager 1m28s
ci::unit 56s
ci::smoke 53s
ci::nix 52s
ci::flake-check 52s
ci::pnpm-hash-fresh 41s
ci::surface-example-build 37s
ci::surface-app-example-build 36s
ci::render 36s
ci::fmt 35s
ci::biome 35s
_ci-setup 32s
ci::atlas-sync 31s
ci::install 10s

Pool status (8 boxes)

box location state
kolu-ci-1 dev-x86-64-linux-04 ✓ idle
kolu-ci-2 dev-x86-64-linux-04 ✓ idle
kolu-ci-3 dev-x86-64-linux-03 ✓ idle
kolu-ci-4 dev-x86-64-linux-08 ✓ idle
kolu-ci-5 idliv2-02 ✓ idle
kolu-ci-6 dev-x86-64-linux-03 ✓ idle
kolu-ci-7 dev-x86-64-linux-05 ✓ idle
kolu-ci-8 dev-x86-64-linux-08 🔒 leased

Posted by ci/pu/report.sh. Lane timings from .ci/4f0d5d6/timings.jsonl; pool state is a live flock probe.

@srid
srid marked this pull request as ready for review June 23, 2026 23:46
@srid
srid merged commit 5856eb5 into master Jun 23, 2026
32 checks passed
@srid
srid deleted the vite81 branch June 23, 2026 23:46
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