Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/react-compiler-todo-title.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"react-doctor": patch
---

Title `react-hooks-js/todo` diagnostics "React Compiler doesn't support this syntax" instead of the generic "React Compiler can't optimize this" headline. The `todo` rule fires when the compiler bails out on syntax it doesn't handle yet, so the headline now says what actually happened.
10 changes: 8 additions & 2 deletions packages/core/src/runners/oxlint/parse-output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ const FILEPATH_WITH_LOCATION_PATTERN = /\S+\.\w+:\d+:\d+[\s\S]*$/;
// `react-hooks-js/todo` id. Give them a human headline & an impact-first
// message; the specific bail-out reason stays in `help`.
const REACT_COMPILER_TITLE = "React Compiler can't optimize this";
// The compiler's `todo` rule fires on syntax it doesn't handle yet —
// an unsupported-syntax bail-out, not an optimization miss in the
// user's code, so it gets its own headline.
const REACT_COMPILER_TODO_TITLE = "React Compiler doesn't support this syntax";
const REACT_COMPILER_MESSAGE =
"This component misses React Compiler's automatic memoization & re-renders more than it should. Rewrite the flagged code so the compiler can optimize it.";

Expand Down Expand Up @@ -88,8 +92,10 @@ const getRuleTitle = (ruleName: string): string | undefined =>

// react-doctor rules carry their own `title`; adopted React Compiler
// diagnostics get a fixed human headline instead of their bare id.
const resolveDiagnosticTitle = (plugin: string, rule: string): string | undefined =>
plugin === "react-hooks-js" ? REACT_COMPILER_TITLE : getRuleTitle(rule);
const resolveDiagnosticTitle = (plugin: string, rule: string): string | undefined => {
if (plugin !== "react-hooks-js") return getRuleTitle(rule);
return rule === "todo" ? REACT_COMPILER_TODO_TITLE : REACT_COMPILER_TITLE;
};

const cleanDiagnosticMessage = (
message: unknown,
Expand Down
84 changes: 84 additions & 0 deletions packages/core/tests/react-compiler-diagnostic-title.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { describe, expect, it } from "vite-plus/test";
import type { ProjectInfo } from "@react-doctor/core";
import { parseOxlintOutput } from "../src/runners/oxlint/parse-output.js";

const ROOT_DIRECTORY = "/home/user/app";

const buildProject = (): ProjectInfo => ({
rootDirectory: ROOT_DIRECTORY,
projectName: "app",
reactVersion: "19.2.0",
reactMajorVersion: 19,
tailwindVersion: null,
zodVersion: null,
zodMajorVersion: null,
framework: "nextjs",
hasTypeScript: true,
hasReactCompiler: true,
hasTanStackQuery: false,
nextjsVersion: "15.0.0",
nextjsMajorVersion: 15,
hasReactNativeWorkspace: false,
expoVersion: null,
shopifyFlashListVersion: null,
shopifyFlashListMajorVersion: null,
hasReanimated: false,
isPreES2023Target: false,
preactVersion: null,
preactMajorVersion: null,
sourceFileCount: 10,
});

const buildOxlintStdout = (code: string, message: string): string =>
JSON.stringify({
diagnostics: [
{
message,
code,
severity: "error",
causes: [],
url: "",
help: "",
filename: "src/components/widget.tsx",
labels: [{ label: "", span: { offset: 0, length: 1, line: 12, column: 3 } }],
related: [],
},
],
number_of_files: 1,
number_of_rules: 1,
});

describe("parseOxlintOutput react-hooks-js diagnostic titles", () => {
it("titles `todo` diagnostics as unsupported syntax", () => {
const stdout = buildOxlintStdout(
"react-hooks-js(todo)",
"(BuildHIR::lowerExpression) Handle TaggedTemplateExpression expressions",
);
const [diagnostic] = parseOxlintOutput(stdout, buildProject(), ROOT_DIRECTORY);

expect(diagnostic).toMatchInlineSnapshot(`
{
"category": "Performance",
"column": 3,
"filePath": "src/components/widget.tsx",
"help": "(BuildHIR::lowerExpression) Handle TaggedTemplateExpression expressions",
"length": 1,
"line": 12,
"message": "This component misses React Compiler's automatic memoization & re-renders more than it should. Rewrite the flagged code so the compiler can optimize it.",
"offset": 0,
"plugin": "react-hooks-js",
"rule": "todo",
"severity": "error",
"title": "React Compiler doesn't support this syntax",
"url": "",
}
`);
});

it("keeps the generic headline for other react-hooks-js rules", () => {
const stdout = buildOxlintStdout("react-hooks-js(refs)", "Cannot access ref during render");
const [diagnostic] = parseOxlintOutput(stdout, buildProject(), ROOT_DIRECTORY);

expect(diagnostic.title).toBe("React Compiler can't optimize this");
});
});
Loading