Files
siftlode/frontend/src/compilerCoverage.contract.test.ts
T
peter 63a0fecc8b refactor(frontend): the flat components/ folder moves into shell/, modules/<page>/ and features/<name>/
A pure move (git mv, history kept): 333 files leave components/, which now holds only the platform
layer (ui/) and the legal pages. The shell is the frame (App, header, nav rail, palette, boot); each
page lives in modules/<page>/ with its rail, store and tests; what several pages share sits in
features/<name>/; the shared-link page is watch/. Domain-free helpers went straight to ui/
(ViewSwitcher, FilterRail, the confirm dialog, the language switcher, the copy rows).

Every relative specifier was rewritten (imports, dynamic imports, vi.mock, new URL), and the
contract tests that scan sources now scan the whole app source, not only components/ — each glob
was counted before and after so none went green over fewer files. The locale-format rule matched
`/src/components/` only, so the move would have taken ~300 files out from under its i18n-singleton
check; it now covers every folder that holds components, with a test per folder.
2026-10-22 23:12:45 +02:00

71 lines
3.8 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { compileReport, shippedSources, type CompileReport } from "./compilerSkips.testkit";
// 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. 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. An audit then found 95 such functions across src.
//
// A RATCHET over everything the build compiles. KNOWN_SKIPS is the exact set the compiler
// skips today, `file → function → the compiler's category`; everything else must compile. Both
// directions are red:
// • a NEW skip (or a known one changing category) — fix the function; if it truly cannot be
// compiled, adding it here is a decision for the owner, with the reason in docs/gates.md;
// • a skip that is GONE — delete its entry, so the list only ever shrinks.
// A cleanup burned it down from 93 to the one function below: it read the popover engine through
// destructuring, moved every render-phase ref write to `useEffectEvent` / `useLatest`, took
// try/finally and every conditional, loop or optional call out of try blocks (the compiler cannot
// lower them yet — and `react-hooks/todo` does not see the latter, so THIS test is what catches them)
// and replaced the two exhaustive-deps suppressions, each of which skipped its whole function.
// VirtualGrid stays: TanStack Virtual's API cannot be memoized safely (IncompatibleLibrary).
const KNOWN_SKIPS: CompileReport["skipped"] = {
"components/ui/VirtualGrid.tsx": { VirtualGrid: "IncompatibleLibrary" },
};
// The files a regression hurt most: compiled, and they must stay so (a positive check too, so a
// harness that compiled nothing could not pass as "no skips").
const MUST_COMPILE: Record<string, string[]> = {
"modules/channels/Channels.tsx": ["Channels"],
"components/ui/DataTable.tsx": ["DataTable"],
// Under every button and every popover in the app.
"components/ui/FocusSafeButton.tsx": ["FocusSafeButton"],
"components/ui/anchoredPopoverHooks.ts": ["useAnchoredPopover"],
"components/ui/AnchoredPopover.tsx": ["PopoverSurfaceInner"],
// The two players — the largest components here, and the last to compile.
"modules/plex/PlexPlayer.tsx": ["PlexPlayer"],
"features/player/PlayerModal.tsx": ["PlayerModal"],
"lib/playerModalHooks.ts": ["useYtProgress"],
};
type Row = [file: string, fn: string, category: string];
const rows = (skips: CompileReport["skipped"]): Row[] =>
Object.entries(skips).flatMap(([file, fns]) =>
Object.entries(fns).map(([fn, category]): Row => [file, fn, category]),
);
describe("the React Compiler", () => {
// ~8 s for the whole tree, once, shared by the checks below.
const report = compileReport(shippedSources());
const actual = rows(report.skipped);
const known = rows(KNOWN_SKIPS);
const key = ([f, fn]: Row) => `${f} → ${fn}`;
it("skips nothing new (fix it, or the owner decides to list it)", () => {
const knownAs = new Map(known.map((r) => [key(r), r[2]]));
expect(
actual.filter((r) => knownAs.get(key(r)) !== r[2]).map((r) => `${key(r)}: ${r[2]}`),
).toEqual([]);
});
it("lists no skip that has gone (delete it — the list only shrinks)", () => {
const actualAs = new Map(actual.map((r) => [key(r), r[2]]));
expect(known.filter((r) => !actualAs.has(key(r))).map(key)).toEqual([]);
});
it.each(Object.entries(MUST_COMPILE))("compiles %s", (file, fns) => {
expect(report.skipped[file] ?? {}).toEqual({});
for (const fn of fns) expect(report.compiled[file]).toContain(fn);
});
});