mirror of
https://github.com/hanzoai/react.git
synced 2026-09-24 17:02:27 +00:00
[compiler] Restore code frames in ESLint compiler error messages (#36901)
## Summary The Rust port (#36173) changed `CompileError` `LoggerEvent`s to carry plain serialized detail objects instead of `CompilerError`/`CompilerDiagnostic` class instances. As a result, the ESLint integrations could no longer call `detail.printErrorMessage(source, {eslint: true})` and were given a replacement `printErrorMessage()` helper that only emitted the `reason` and `description`. This regressed error printing: the **source code frame(s) and `file:line:column` location** that used to appear for each error detail were dropped from lint output. This PR restores the previous behaviour: - Export `printCodeFrame` from `CompilerError` and reuse it from both ESLint integrations instead of duplicating it. - Rebuild the full message (reason, description, per-detail code frames, and hints) in `printErrorMessage`. - Handle **both** detail shapes that flow through `LoggerEvent`s: - a `details` array (`CompilerDiagnostic` and the **Rust** compiler), and - a legacy flat `loc` (deprecated `CompilerErrorDetail`). `formatDetailForLogging` emits one or the other, so the previous unconditional iteration over `error.details` would have thrown `TypeError: not iterable` on the flat-`loc` path. Normalizing to a list fixes that and keeps the code working with both the TypeScript and Rust compilers. ## Test plan - `tsc --noEmit` on both ESLint packages is clean (no new errors vs. baseline). - Verified the detail loop handles the Rust compiler's `details` array shape and the legacy flat `loc` shape.
This commit is contained in:
@@ -522,7 +522,7 @@ export class CompilerError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
function printCodeFrame(
|
||||
export function printCodeFrame(
|
||||
source: string,
|
||||
loc: t.SourceLocation,
|
||||
message: string,
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
ErrorSeverity,
|
||||
LintRulePreset,
|
||||
LintRules,
|
||||
printCodeFrame,
|
||||
type LintRule,
|
||||
} from 'babel-plugin-react-compiler/src/CompilerError';
|
||||
|
||||
@@ -43,10 +44,46 @@ function primaryLocation(
|
||||
* Format an error message from a CompileErrorDetail, matching the old
|
||||
* CompilerErrorDetail.printErrorMessage() / CompilerDiagnostic.printErrorMessage() behavior.
|
||||
*/
|
||||
function printErrorMessage(detail: CompileErrorDetail): string {
|
||||
const buffer = [`[ReactCompilerError] ${detail.reason}`];
|
||||
if (detail.description != null) {
|
||||
buffer.push(`\n\n${detail.description}.`);
|
||||
function printErrorMessage(source: string, error: CompileErrorDetail): string {
|
||||
const buffer = [`[ReactCompilerError] ${error.reason}`];
|
||||
if (error.description != null) {
|
||||
buffer.push(`\n\n${error.description}.`);
|
||||
}
|
||||
/*
|
||||
* A CompileError's source location(s) may be provided either as a `details`
|
||||
* array (CompilerDiagnostic and the Rust compiler) or as a single flat `loc`
|
||||
* (legacy CompilerErrorDetail). Normalize to a list so both shapes render
|
||||
* code frames identically.
|
||||
*/
|
||||
const details =
|
||||
error.details ??
|
||||
(error.loc != null
|
||||
? [{kind: 'error' as const, loc: error.loc, message: error.reason}]
|
||||
: []);
|
||||
for (const detail of details) {
|
||||
if (detail.kind === 'error') {
|
||||
const loc = detail.loc;
|
||||
if (loc == null || typeof loc === 'symbol') {
|
||||
continue;
|
||||
}
|
||||
let codeFrame: string;
|
||||
try {
|
||||
codeFrame = printCodeFrame(source, loc, detail.message ?? '');
|
||||
} catch {
|
||||
codeFrame = detail.message ?? '';
|
||||
}
|
||||
buffer.push('\n\n');
|
||||
if (loc.filename != null) {
|
||||
// ESLint uses 1-indexed columns
|
||||
buffer.push(
|
||||
`${loc.filename}:${loc.start.line}:${loc.start.column + 1}\n`,
|
||||
);
|
||||
}
|
||||
buffer.push(codeFrame);
|
||||
} else if (detail.kind === 'hint') {
|
||||
buffer.push('\n\n');
|
||||
buffer.push(detail.message ?? '');
|
||||
}
|
||||
}
|
||||
return buffer.join('');
|
||||
}
|
||||
@@ -161,7 +198,7 @@ function makeRule(rule: LintRule): Rule.RuleModule {
|
||||
* we should deduplicate them with a "reported" set
|
||||
*/
|
||||
context.report({
|
||||
message: printErrorMessage(detail),
|
||||
message: printErrorMessage(result.sourceCode, detail),
|
||||
loc,
|
||||
suggest: makeSuggestions(detail),
|
||||
});
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
LintRulePreset,
|
||||
} from 'babel-plugin-react-compiler';
|
||||
import type {CompileErrorDetail} from 'babel-plugin-react-compiler/src/Entrypoint';
|
||||
import {printCodeFrame} from 'babel-plugin-react-compiler/src/CompilerError';
|
||||
import {type Linter, type Rule} from 'eslint';
|
||||
import runReactCompiler, {RunCacheEntry} from './RunReactCompiler';
|
||||
|
||||
@@ -42,10 +43,46 @@ function primaryLocation(
|
||||
/**
|
||||
* Format an error message from a CompileErrorDetail.
|
||||
*/
|
||||
function printErrorMessage(detail: CompileErrorDetail): string {
|
||||
const buffer = [`[ReactCompilerError] ${detail.reason}`];
|
||||
if (detail.description != null) {
|
||||
buffer.push(`\n\n${detail.description}.`);
|
||||
function printErrorMessage(source: string, error: CompileErrorDetail): string {
|
||||
const buffer = [`[ReactCompilerError] ${error.reason}`];
|
||||
if (error.description != null) {
|
||||
buffer.push(`\n\n${error.description}.`);
|
||||
}
|
||||
/*
|
||||
* A CompileError's source location(s) may be provided either as a `details`
|
||||
* array (CompilerDiagnostic and the Rust compiler) or as a single flat `loc`
|
||||
* (legacy CompilerErrorDetail). Normalize to a list so both shapes render
|
||||
* code frames identically.
|
||||
*/
|
||||
const details =
|
||||
error.details ??
|
||||
(error.loc != null
|
||||
? [{kind: 'error' as const, loc: error.loc, message: error.reason}]
|
||||
: []);
|
||||
for (const detail of details) {
|
||||
if (detail.kind === 'error') {
|
||||
const loc = detail.loc;
|
||||
if (loc == null || typeof loc === 'symbol') {
|
||||
continue;
|
||||
}
|
||||
let codeFrame: string;
|
||||
try {
|
||||
codeFrame = printCodeFrame(source, loc, detail.message ?? '');
|
||||
} catch {
|
||||
codeFrame = detail.message ?? '';
|
||||
}
|
||||
buffer.push('\n\n');
|
||||
if (loc.filename != null) {
|
||||
// ESLint uses 1-indexed columns
|
||||
buffer.push(
|
||||
`${loc.filename}:${loc.start.line}:${loc.start.column + 1}\n`,
|
||||
);
|
||||
}
|
||||
buffer.push(codeFrame);
|
||||
} else if (detail.kind === 'hint') {
|
||||
buffer.push('\n\n');
|
||||
buffer.push(detail.message ?? '');
|
||||
}
|
||||
}
|
||||
return buffer.join('');
|
||||
}
|
||||
@@ -160,7 +197,7 @@ function makeRule(rule: LintRule): Rule.RuleModule {
|
||||
* we should deduplicate them with a "reported" set
|
||||
*/
|
||||
context.report({
|
||||
message: printErrorMessage(detail),
|
||||
message: printErrorMessage(result.sourceCode, detail),
|
||||
loc,
|
||||
suggest: makeSuggestions(detail),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user