chore(KNO-14479): upgrade to Vite 8, Vitest 4, and drop React 16 - #1054
Conversation
Vite 8 swaps Rollup for rolldown and esbuild for oxc, so this is a toolchain change rather than a version bump. The JSX runtime is the notable part. packages/react and react-core compiled with the classic runtime plus babel-plugin-react-require, which auto-inserted the React import. Under @vitejs/plugin-react 6 the JSX transform is delegated to oxc, which runs before Babel, so the plugin never saw any JSX to react to and every component failed with 'React is not defined'. Moving to the automatic runtime fixes it, but the output then imports react/jsx-runtime, which only exists in React 17+. The react and react-dom peers narrow accordingly. Vitest also had to move in lockstep: vitest 3 does not support Vite 8 and vitest 4 does not support Vite 5, so neither half could land alone without yarn nesting a second Vite and splitting the tree. Build config changes rolldown required: - assetFileNames matched the stylesheet by the literal name 'style.css', which rolldown names after the lib entry instead, so dist/index.css was never emitted and guide-example failed to resolve it - rollup-plugin-execute does not run under rolldown, so the step moving index.css to the dist root silently never fired; replaced with a writeBundle hook. Its find -delete steps are dropped as dead code since rolldown does not emit the empty .css proxy chunks Rollup did - output.interop is rejected outright by rolldown; removed from all five package configs Vitest 4 also stopped accepting arrow functions as constructor mock implementations, which accounted for every remaining test failure, and now warns on vi.mock calls written inside helper functions.
🦋 Changeset detectedLatest commit: a4ecac5 The changes in this PR will be included in the next version bump. This PR includes changesets to release 10 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Bundle ReportChanges will decrease total bundle size by 20.47kB (-3.28%) ⬇️. This is within the configured threshold ✅ Detailed changes
Affected Assets, Files, and Routes:view changes for bundle: @knocklabs/react-react-esmAssets Changed:
view changes for bundle: @knocklabs/expo-expo-esmAssets Changed:
view changes for bundle: @knocklabs/react-react-cjsAssets Changed:
view changes for bundle: @knocklabs/client-client-esmAssets Changed:
view changes for bundle: @knocklabs/expo-expo-cjsAssets Changed:
view changes for bundle: @knocklabs/client-client-cjsAssets Changed:
view changes for bundle: @knocklabs/react-core-react-core-esmAssets Changed:
view changes for bundle: @knocklabs/react-core-react-core-cjsAssets Changed:
view changes for bundle: @knocklabs/react-native-react-native-cjsAssets Changed:
view changes for bundle: @knocklabs/react-native-react-native-esmAssets Changed:
|
Rolldown defaults output.strict to "auto", which only emits the directive when the source literally contains one. TypeScript and ESM sources never do, so every published CJS file lost "use strict" (144/144 files had it on Vite 5, 0/209 after the upgrade). Rollup defaulted this to true. Sloppy mode changes real semantics: assignments to non-writable properties fail silently instead of throwing, and `this` is globalThis rather than undefined in plain calls. The code is authored under strict semantics, so it should ship that way. Setting output.strict restores it across all five package configs: 209/209 CJS files now carry the directive.
Two build-output regressions surfaced by comparing the Vite 8 artifacts against a Vite 5 baseline build of the same commit. Require cycles in @knocklabs/react-core. Rollup tree-shook the source barrel files away; rolldown keeps them and emits bare side-effect requires for them, so requiring the CJS entry printed 13 'Accessing non-existent property ... inside circular dependency' warnings on every process start. @knocklabs/react inherited all 13 through its dependency on react-core. react-core has no side-effectful modules and no CSS imports, so declaring sideEffects:false lets the barrels be dropped: 22 barrel chunks become 2, and the warnings go to 0 in both packages. Public exports are unchanged at 47 and 73, none undefined. Vite's internal hash marker leaking into the published stylesheet. Vite appends /*$vite$:1*/ in finalizeCss() and strips it again at the end of the same generateBundle hook, but the stylesheet is added to the bundle via emitFile() inside that hook and so misses the strip. The hook that moves index.css now strips it from both published copies.
The three-line explanation was repeated verbatim in all five configs. The reasoning now lives in the PR description instead.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 410e0f1. Configure here.
The comment said the empty .css proxy chunks get deleted below. Nothing deletes them anymore, because the find -delete steps went away with rollup-plugin-execute. Vite removes those chunks from the bundle itself, so they never reach disk under either bundler.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1054 +/- ##
==========================================
+ Coverage 66.58% 73.00% +6.42%
==========================================
Files 214 139 -75
Lines 10514 4342 -6172
Branches 1529 1275 -254
==========================================
- Hits 7001 3170 -3831
+ Misses 3487 1055 -2432
- Partials 26 117 +91 |

Closes KNO-14479. Supersedes #1051.
What changed
vite5.4.19 to 8.1.5,vitest3.2.4 to 4.1.10,@vitejs/plugin-react4.5.1 to 6.0.4.@knocklabs/reactand@knocklabs/react-coreno longer support React 16. Theirreactandreact-dompeers narrow to^17 || ^18 || ^19.jsxRuntime: "classic"and thebabel-plugin-react-requiredependency.assetFileNamesinpackages/reactmatches the.cssextension instead of the exact namestyle.css.writeBundlehook replacesrollup-plugin-execute. The hook movesdist/index.cssand strips Vite's/*$vite$:1*/marker.output.interopand addoutput.strict: true.@knocklabs/react-coredeclares"sideEffects": false.functionexpressions for constructor mocks. Twovi.mockcalls move to the top level.appearance. Users on Safari below 15.4 see native button styling on.rnf-button,.rnf-notification-cell__inner, and.rnf-archive-notification-btn.Why
Vite 8 replaces Rollup with rolldown and esbuild with oxc. This changes the toolchain, so several settings move with it.
React 16.
@vitejs/plugin-react6 gives the JSX transform to oxc, which runs before Babel.babel-plugin-react-requirethen finds no JSX and never inserts theimport Reactline. Every component fails withReact is not defined. The automatic runtime fixes this, but its output importsreact/jsx-runtime. That entry point needs React 17 or later.Vitest. Vitest 3 does not support Vite 8. Vitest 4 does not support Vite 5. Either half alone makes yarn nest a second Vite, so the tests and the builds run different versions.
Build config. Rolldown names the stylesheet after the lib entry, not
style.css. The old rename never fired, so the build never wrotedist/index.css.rollup-plugin-executedoes not run under rolldown, so the step that moved that file never fired either. Rolldown rejectsoutput.interop.output.strict. Rolldown defaultsstrictto"auto". That emits"use strict"only when the source file already contains it. TypeScript and ESM sources never write one, so every published CJS file lost the directive. Rollup defaulted this totrue.sideEffects. Rollup removed the source barrel files during tree shaking. Rolldown keeps them and emits bare side-effect requires, which closes a require cycle. Requiring the CJS entry printed 13 circular dependency warnings per process.@knocklabs/reactinherited all 13 from react-core.Tests. Vitest 4 rejects arrow functions as constructor mock implementations.
Vendor prefixes. Neither config sets a browser target, so the output follows the Vite default. Vite 8 raised that default, and its CSS minifier drops prefixes the new target no longer needs.
Validation
I built every package twice. Once on this branch, and once on a Vite 5 baseline of the same commit.
Rolldown is a different bundler, so the emitted JavaScript cannot match the old output byte for byte. Every one of the 286 JS files differs, down to the minified variable names. Comparing bytes therefore proves nothing. What matters is the contract a consumer depends on, so that is what I compared:
undefined..d.tsfile. All 222 are byte-identical."use client"directive. Still the first statement in every file that needs one.package.json, and the full file inventory of eachdist.A difference counts as a regression only when the baseline works and this branch does not. Three met that bar: the missing
"use strict"directive, the require cycles, and the leaked CSS marker. The sections above describe each one, and this PR fixes all three.1007 tests pass.
yarn type:checkandyarn lintare clean. All 7 example apps build on both trees.