fix: make Vite and Rsbuild build-time metrics apples-to-apples - #54
Conversation
Vite production builds reported `buildEnd - buildStart`. Rollup's buildEnd fires when the module graph is complete, before renderChunk, generateBundle and writeBundle, so minification and emitting assets were excluded entirely — 18% of the build under esbuild, 64% under terser. Rsbuild's `stats.endTime - stats.startTime` already covered the full pipeline, so any chart comparing the two flattered Vite. Measure through closeBundle, and keep the transform phase as a new `transformTimeMs` field rather than as the headline number. Rsbuild's clientReady existed only as an entry in `devFeedback[]`, timed with performance.now() — from page navigation, excluding everything before the browser opened the page. Vite's `clientready` runs from dev server start. Emit clientready as a first-class command event on Vite's origin; the browser-relative values stay in `devFeedback[]` and ride along as domContentLoadedMs / firstContentfulPaintMs. Vite's `prebundled` flag sampled the deps metadata mtime at `listening`, but prebundling is triggered by the first browser request and has not run by then — it reported false on a warm cache and nothing on a cold one, never observing the cost it exists to measure. Sample it on the clientready report instead, where it can actually be true. Also restores agoda-devfeedback-rsbuild to its real version. It was pinned to the published 2.0.9 so changesets would skip it while NPM_TOKEN returned E403 (#51); the token is fixed and the other packages published successfully. Fixes #53 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The repo had no contributor-facing docs for changesets at all, so the two-merge release flow (PR -> Version Packages PR -> npm) was folklore, and the generated `adjective-noun-verb` filenames read as if they meant something. They don't — human-id picks them to avoid filename collisions. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
It explains why any of these metrics are collected, so it belongs before the bundler setup instructions rather than below them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
So a Vite number could look much faster simply because it stopped the clock earlier, while Rsbuild’s clock kept running through the actual end of the build. PR #54 makes the comparisons apple-to-apple:
In short: the headline metrics now answer the same question:
|
Fixes #53. Also undoes the #51 workaround, as agreed.
Undoing the #51 workaround (first commit hunk)
packages/rspack-plugin/package.jsonwas pinned to the published2.0.9so changesets would skip it whileNPM_TOKENreturned E403. The token is fixed — the master run at31151648453publishedcommon@2.1.0,vite2@2.1.0andwebpack@2.0.10— and the only thing the pin still does is keepagoda-devfeedback-rsbuildoff npm while its CHANGELOG already claims 2.1.0.Restored to
2.1.0. Note it will land on npm as 2.2.0, since the changeset in this PR bumps it before it ever gets published; 2.1.0 is skipped on the registry.1. Vite production builds under-report
The reported
timeTakenwasbuildEnd - buildStart. Rollup'sbuildEndfires when the module graph is complete — beforerenderStart,renderChunk,generateBundleandwriteBundle— so everything after transform was excluded, even though the value is sent later fromcloseBundle. Rsbuild'sstats.endTime - stats.startTimealready covered the full pipeline, sotype: 'vite'andtype: 'rsbuild'were never measuring the same span.Now measured through
closeBundle(Date.now() - buildStartat report time). The transform phase is kept as a newtransformTimeMsfield onViteBuildDatarather than being narrowed into the headline number.2. Rsbuild
clientreadyclientReadywas only ever an entry insidedevFeedback[]on theRspackBuildDatapayload, timed withperformance.now()— i.e. from page navigation, excluding everything before the browser opened the page. Vite'sphase: 'clientready'runs from dev server start. The two numbers could not be compared even after digging the value out.The Rsbuild plugin now emits a
CommandBuildDatawithphase: 'clientready',timeTakenmeasured fromdevServerStart, and the browser-relative values attached asdomContentLoadedMs/firstContentfulPaintMs. Reported once per dev server run — a page reload is not a new start.The client script now sends one consolidated message instead of three, but the server still pushes the same three
clientReady/domContentLoaded/firstContentfulPaintentries intodevFeedback[], so nothing that consumes the existing payload changes shape.Did not touch the
devserverphase or add a Vite counterpart to Rsbuild's first full dev compile — per the issue, Vite has no whole-app compile in dev and should not grow a fake one. Documented in the README instead thatclientreadyis the cross-bundler dev-startup metric anddevserver("socket is up") is not comparable across bundlers.3. Vite
prebundledcould never betrueThe flag compared the mtime of
node_modules/.vite/deps/_metadata.jsonbefore config resolution against its mtime atlistening. Prebundling is request-triggered, so atlisteningit has not run: warm cache reportedfalse, cold cache reported nothing.Sampled on the
clientreadyreport instead, which moves the field from thedevserverevent to theclientreadyevent. Cases where the browser never connects now report nothing rather than a confidentfalse.Tests
Two new tests, both verified to fail when the corresponding fix is reverted:
reports client ready from dev server start, exactly once per run— connects a real WebSocket client to the plugin's server through the injected script's port, sends the message twice, and asserts one command event plus unchangeddevFeedback[]entries.reports prebundling on client ready, not on the dev server event— cold metadata at startup, present at client ready.The three existing Vite build tests now mock a third
Date.now()(buildStart 0, buildEnd 100, closeBundle 150) and asserttimeTaken150 withtransformTimeMs100.pnpm testandpnpm check-typespass. The two pre-existing Prettier warnings (README.md,packages/vite-plugin/src/index.ts) are on master already and left alone.🤖 Generated with Claude Code