Files
siftlode/frontend/src/components/compilerCoverage.contract.test.ts
T
peter cdfe7a8986 perf(channels): the row menu opens and closes without re-rendering the table
The React Compiler silently skipped Channels and DataTable (a ref written
during render; the refs lint is off), and even once compiled it dropped the
memo of the rows and columns (they reach filterRows/optionsFromColumns past
the page's hooks) and the columns read the open row. Every open, close and
reposition of the right-click menu rebuilt the whole table: on "All" rows
~0.9 s to open and ~0.6 s to close (dev). Now: effect events instead of the
render-written refs, explicit memos for rows, columns and their inputs, a
module-scope keyOf, and the open row reaches its "…" through a small store.
Measured on 397 rows (dev): ~75 ms to open, ~35 ms to close.

A contract test runs the real compiler over both files and fails on any
skipped function. The dropped memos have no automated guard: they need
the full page rendered with its providers; the measurement above is the
evidence.
2026-10-03 05:10:52 +02:00

67 lines
2.6 KiB
TypeScript

import { readFileSync } from "node:fs";
import { transformSync } from "@babel/core";
import reactCompiler from "babel-plugin-react-compiler";
import { describe, expect, it } from "vitest";
// The React Compiler bails out of a component it cannot prove safe WITHOUT a word: the component
// still works, it just re-renders everything under it on every render. Its main tripwire, a ref read
// or written during render, is not linted here (`react-hooks/refs` is off, see eslint.config.js), so
// nothing caught it. The channel manager is where it hurt (the owner, 2026-10-03): Channels and
// DataTable both bailed out, every open, close and reposition of a row's right-click menu rebuilt
// the rows and columns, and on "All" rows the menu took ~0.9 s to open and ~0.6 s to close.
//
// This runs the real compiler (the options reactPlugin.ts gives it) over the files that must stay
// compiled and fails on any function it skips, with the compiler's own reason.
const MUST_COMPILE = ["components/Channels.tsx", "components/ui/DataTable.tsx"];
function bailouts(path: string): string[] {
const source = readFileSync(new URL(`../${path}`, import.meta.url), "utf8");
const found: string[] = [];
transformSync(source, {
filename: path,
babelrc: false,
configFile: false,
parserOpts: { plugins: ["jsx", "typescript"] },
plugins: [
[
reactCompiler,
{
target: "19",
logger: {
logEvent: (
_file: string | null,
e: { kind: string; fnLoc?: unknown; detail?: unknown },
) => {
if (
e.kind !== "CompileError" &&
e.kind !== "CompileSkip" &&
e.kind !== "PipelineError"
)
return;
const loc = e.fnLoc as { start?: { line?: number } } | undefined;
const d = e.detail as
| { options?: { reason?: string; details?: { message?: string }[] } }
| { reason?: string; details?: { message?: string }[] }
| undefined;
const o =
d && "options" in d
? d.options
: (d as { reason?: string; details?: { message?: string }[] });
found.push(
`line ${loc?.start?.line}: ${o?.details?.[0]?.message ?? o?.reason ?? e.kind}`,
);
},
},
},
],
],
});
return found;
}
describe("the React Compiler", () => {
it.each(MUST_COMPILE)("compiles every function in %s", (path) => {
expect(bailouts(path)).toEqual([]);
});
});