Published on

TIL: Vite uses the nearest tsconfig.json and ignores `include`, so JSX silently fell back to the classic runtime

Authors

A test run went red with hundreds of ReferenceError: React is not defined, in components that import no React at all. The production build was fine the whole time.

Two things were going on.

First, Vite and esbuild look for a tsconfig.json by walking up from each file to the nearest one. They do not check whether that file's include list covers the file. A component under the renderer folder landed on the main-process config, which has no jsx setting. esbuild then used the classic transform and wrote React.createElement into files with no React import.

Second, vite build hid it. The build config loaded @vitejs/plugin-react, which sets the JSX runtime itself. The test config did not, so only the tests broke.

The fix was one line in the config the components actually resolve to:

{
  "compilerOptions": {
    "jsx": "react-jsx"
  }
}

The whole suite went from 28 failing files to zero.

To catch it earlier, add a tiny guard test that asserts the setting is present. I also checked that the guard fails if you change the value to "react", so it tracks the behavior and not just the config text.

If a transform behaves differently in tests than in the build, compare which config and which plugins each one loads.