Skip to content

Resolve React Compiler diagnostics - #4162

Draft
nstepien wants to merge 4 commits into
mainfrom
react-compiler-diagnostics
Draft

nstepien wants to merge 4 commits into
mainfrom
react-compiler-diagnostics

Conversation

@nstepien

@nstepien nstepien commented Aug 29, 2026 •

Copy link
Copy Markdown
Collaborator

vp pack compiles src/ with React Compiler, which skips every component or hook it can't compile. On main, it skips DataGrid, EditCell, GroupedRow, useCalculatedColumns, useColumnWidths, and useViewportRows. After this PR, all of them are compiled, and if a bail-out comes back, oxlint reports it and vp pack fails in CI.

Unsupported syntax

Immutability

  • GroupedRow: let idx was incremented in the mapViewportColumns callback when the first rendered column was the select column, and GroupCell compared it with column.idx. The group-by columns follow the select column in groupBy order, so GroupedRow now compares each column's key with groupBy[row.level] and passes the result to GroupCell as isLevelMatching.

Hoisted functions

  • DataGrid: the useImperativeHandle callback read setPosition, which is declared further down. The handle now exposes setPositionLatest, which also keeps its setActivePosition stable across renders, and the react/immutability suppression is gone.
  • EditCell: the useLayoutEffect cleanup called cancelTask, which was declared after the effect. It moves to module scope and takes the two refs: declared in the component, it would have to be an effect dependency, and the effect must not clean up between re-renders, as the comment there explains. onClose also moves above the useEffectEvent that calls it. The TODO: report React Compiler bug note and its react/invariant suppression are gone.

Build

  • compiler: { reportDiagnostics: true } turns the code React Compiler skips into plugin warnings. vp pack already fails on warnings in CI and agent sessions (failOnWarn: isAgent || 'ci-only'), and vp dev, vp build, and vp test log them. Unlike lint, a suppression comment doesn't hide them. panicThreshold: 'all_errors' would fail every build, including the dev server and tests while code is being edited, and 'critical_errors' misses unsupported syntax like x++.
  • One react() instance now serves both Vite and vp pack. It produces identical lib/ and website builds, and the same dev-server modules. vp pack can't use lazyPlugins, so its plugins were already created on every config load. The plugin keeps some per-run state, like whether to emit development JSX, which is fine because Vite and vp pack never run in the same process.
  • .d.ts files stay excluded: vp pack passes the declaration files it generates through the plugin, and without the exclusion, lib/index.d.ts becomes export {}, which publint doesn't catch. node_modules stays excluded too, as setting exclude replaces the plugin's default.
  • AGENTS.md mentions that vp pack fails on code React Compiler can't compile.

vp run ci passes.

🤖 Generated with Claude Code

@nstepien
nstepien requested a review from amanmahajan7 as a code owner August 29, 2026 15:52
@codecov-commenter

codecov-commenter commented Aug 29, 2026 •

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.13%. Comparing base (5ffecb4) to head (ec1fa41).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4162      +/-   ##
==========================================
+ Coverage   98.96%   99.13%   +0.16%     
==========================================
  Files          46       46              
  Lines        2030     2186     +156     
  Branches      792      961     +169     
==========================================
+ Hits         2009     2167     +158     
+ Misses         21       19       -2     
Files with missing lines Coverage Δ
src/DataGrid.tsx 99.06% <100.00%> (+0.17%) ⬆️
src/EditCell.tsx 98.88% <100.00%> (+1.74%) ⬆️
src/GroupCell.tsx 100.00% <ø> (ø)
src/GroupedRow.tsx 100.00% <100.00%> (ø)
src/hooks/useCalculatedColumns.ts 100.00% <100.00%> (ø)
src/hooks/useColumnWidths.ts 98.68% <100.00%> (+0.05%) ⬆️
src/hooks/useViewportRows.ts 100.00% <100.00%> (+1.63%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@nstepien
nstepien marked this pull request as draft August 29, 2026 16:02
@nstepien nstepien self-assigned this Aug 30, 2026
@nstepien
nstepien force-pushed the react-compiler-diagnostics branch from d62c1db to 5b5e866 Compare August 31, 2026 18:30
Base automatically changed from reapiler to main September 1, 2026 15:00
@nstepien
nstepien force-pushed the react-compiler-diagnostics branch from 5b5e866 to 85c3508 Compare September 1, 2026 15:01
`tsdown` now logs 21 React Compiler diagnostics, each one causing the
compiler to skip optimizing the enclosing component or hook.

Unsupported syntax:
- `useColumnWidths`: replace `||=` with an `if` statement. Note that
  `hasChanges = hasChanges || …` would trip `eslint/logical-assignment-operators`.
- `useCalculatedColumns`/`useViewportRows`: replace `x++` on variables
  captured within lambdas with `x += 1`.
- `useViewportColumns`/`DataGrid`: generators cannot be lowered at all, so
  hoist every `function*` out of the hook/component body to module scope and
  pass what they used to close over as parameters. Two now-unused
  `react/memo-dependencies` suppressions are dropped, as the compiler was
  previously bailing on the whole file.

Immutability:
- `GroupRow`: `let idx` was mutated inside the render `.map()`. The increment
  only ever depended on whether the first rendered column is the select
  column, so materialize the columns once and compute `groupColumnIndex` as a
  constant before the JSX.
- `DataGrid`: `setPosition` was read by `useImperativeHandle`/`useLatestFunc`
  before its declaration, so move the declaration above the `Misc hooks` block.

Invariant:
- `EditCell`: `cancelTask` was a hoisted function referenced by an effect
  declared above it. Extract it to module scope taking the three refs, which
  keeps the effect's `[commitOnOutsideClick]` dependency list correct — adding
  a component-body `cancelTask` as a dependency would make the effect tear
  down between re-renders, which the comment there explicitly guards against.
  `onClose` also moves above the `useEffectEvent` that calls it.

Unreachable hoisted declaration:
- `TreeDataGrid`: `isGroupRow` was declared after a `return`, so move it above
  the early return.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019NZmYxgysVEbw12FnbAf7x
@nstepien
nstepien force-pushed the react-compiler-diagnostics branch from 85c3508 to 831245c Compare September 4, 2026 12:08
nstepien and others added 3 commits October 24, 2026 21:48
…stics

# Conflicts:
#	src/DataGrid.tsx
#	src/EditCell.tsx
#	src/GroupedRow.tsx
#	src/TreeDataGrid.tsx
#	src/hooks/useViewportColumns.ts
- `GroupedRow`: instead of incrementing `idx` in the `mapViewportColumns`
  callback, compare each column with the `groupBy` key of the row's level,
  and pass `isLevelMatching` to `GroupCell`. The group-by columns follow the
  select column in `groupBy` order, so this is equivalent to the column index
  check, without depending on whether the select column is rendered.
- `EditCell`: move `cancelTask` to module scope, taking the two refs, so the
  `useLayoutEffect` cleanup can call it without depending on it, and declare
  `onClose` before the `useEffectEvent` that calls it.
- `DataGrid`: the imperative handle exposes `setPositionLatest` instead of
  `setPosition`. Reading the hoisted `setPosition` in the `useImperativeHandle`
  callback is what stopped the compiler, and the handle's `setActivePosition`
  is now stable across renders.
- Enable `react/todo`, so oxlint reports the syntax React Compiler doesn't
  support yet, like `||=` and `x++` on variables captured by nested functions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…React Compiler diagnostics

- One `react()` instance now serves both Vite and `vp pack`: it produces
  identical `lib/` and website builds, and the same dev-server modules.
  Vite and `vp pack` never run in the same process, so the plugin's
  per-run state isn't shared.
- Keep excluding `.d.ts` files: `vp pack` passes the declaration files it
  generates through the plugin, and without the exclusion, `lib/index.d.ts`
  becomes `export {}`. `node_modules` stays excluded, as replacing the
  default `exclude` would otherwise compile dependencies in Vite.
- `reportDiagnostics: true` turns the code React Compiler skips into plugin
  warnings, which fail `vp pack` in CI and agent sessions through
  `failOnWarn`, and are logged by `vp dev`, `vp build`, and `vp test`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

2 participants