diff --git a/compiler/apps/playground/components/Editor/EditorImpl.tsx b/compiler/apps/playground/components/Editor/EditorImpl.tsx index 02813a8d2fd01..27d2fd79890aa 100644 --- a/compiler/apps/playground/components/Editor/EditorImpl.tsx +++ b/compiler/apps/playground/components/Editor/EditorImpl.tsx @@ -11,6 +11,7 @@ import * as t from '@babel/types'; import BabelPluginReactCompiler, { CompilerError, CompilerErrorDetail, + CompilerDiagnostic, Effect, ErrorSeverity, parseConfigPragmaForTests, @@ -144,7 +145,7 @@ const COMMON_HOOKS: Array<[string, Hook]> = [ function compile(source: string): [CompilerOutput, 'flow' | 'typescript'] { const results = new Map>(); const error = new CompilerError(); - const otherErrors: Array = []; + const otherErrors: Array = []; const upsert: (result: PrintedCompilerPipelineValue) => void = result => { const entry = results.get(result.name); if (Array.isArray(entry)) { @@ -214,7 +215,7 @@ function compile(source: string): [CompilerOutput, 'flow' | 'typescript'] { debugLogIRs: logIR, logEvent: (_filename: string | null, event: LoggerEvent) => { if (event.kind === 'CompileError') { - otherErrors.push(new CompilerErrorDetail(event.detail)); + otherErrors.push(event.detail); } }, }, @@ -226,7 +227,7 @@ function compile(source: string): [CompilerOutput, 'flow' | 'typescript'] { * (i.e. object shape that is not CompilerError) */ if (err instanceof CompilerError && err.details.length > 0) { - error.details.push(...err.details); + error.merge(err); } else { /** * Handle unexpected failures by logging (to get a stack trace) @@ -245,7 +246,7 @@ function compile(source: string): [CompilerOutput, 'flow' | 'typescript'] { } // Only include logger errors if there weren't other errors if (!error.hasErrors() && otherErrors.length !== 0) { - otherErrors.forEach(e => error.push(e)); + otherErrors.forEach(e => error.details.push(e)); } if (error.hasErrors()) { return [{kind: 'err', results, error: error}, language]; diff --git a/compiler/apps/playground/components/Editor/Input.tsx b/compiler/apps/playground/components/Editor/Input.tsx index 0992591183c15..5cfa56d77f743 100644 --- a/compiler/apps/playground/components/Editor/Input.tsx +++ b/compiler/apps/playground/components/Editor/Input.tsx @@ -36,13 +36,18 @@ export default function Input({errors, language}: Props): JSX.Element { const uri = monaco.Uri.parse(`file:///index.js`); const model = monaco.editor.getModel(uri); invariant(model, 'Model must exist for the selected input file.'); - renderReactCompilerMarkers({monaco, model, details: errors}); + renderReactCompilerMarkers({ + monaco, + model, + details: errors, + source: store.source, + }); /** * N.B. that `tabSize` is a model property, not an editor property. * So, the tab size has to be set per model. */ model.updateOptions({tabSize: 2}); - }, [monaco, errors]); + }, [monaco, errors, store.source]); useEffect(() => { /** diff --git a/compiler/apps/playground/components/Editor/Output.tsx b/compiler/apps/playground/components/Editor/Output.tsx index 7886f11e62370..36d20a0125e73 100644 --- a/compiler/apps/playground/components/Editor/Output.tsx +++ b/compiler/apps/playground/components/Editor/Output.tsx @@ -142,6 +142,17 @@ async function tabify( , ); } + } else if (compilerOutput.kind === 'err') { + const errors = compilerOutput.error.printErrorMessage(source, { + eslint: false, + }); + reorderedTabs.set( + 'Errors', + , + ); } tabs.forEach((tab, name) => { reorderedTabs.set(name, tab); @@ -166,6 +177,19 @@ function Output({store, compilerOutput}: Props): JSX.Element { const [tabs, setTabs] = useState>( () => new Map(), ); + + /* + * Update the active tab back to the output or errors tab when the compilation state + * changes between success/failure. + */ + const [previousOutputKind, setPreviousOutputKind] = useState( + compilerOutput.kind, + ); + if (compilerOutput.kind !== previousOutputKind) { + setPreviousOutputKind(compilerOutput.kind); + setTabsOpen(new Set([compilerOutput.kind === 'ok' ? 'JS' : 'Errors'])); + } + useEffect(() => { tabify(store.source, compilerOutput).then(tabs => { setTabs(tabs); @@ -196,20 +220,6 @@ function Output({store, compilerOutput}: Props): JSX.Element { tabs={tabs} changedPasses={changedPasses} /> - {compilerOutput.kind === 'err' ? ( -
-
-

COMPILER ERRORS

-
-
-            {compilerOutput.error.toString()}
-          
-
- ) : null} ); } diff --git a/compiler/apps/playground/lib/reactCompilerMonacoDiagnostics.ts b/compiler/apps/playground/lib/reactCompilerMonacoDiagnostics.ts index a800e25773295..cece2fa7c6dd3 100644 --- a/compiler/apps/playground/lib/reactCompilerMonacoDiagnostics.ts +++ b/compiler/apps/playground/lib/reactCompilerMonacoDiagnostics.ts @@ -6,7 +6,11 @@ */ import {Monaco} from '@monaco-editor/react'; -import {CompilerErrorDetail, ErrorSeverity} from 'babel-plugin-react-compiler'; +import { + CompilerDiagnostic, + CompilerErrorDetail, + ErrorSeverity, +} from 'babel-plugin-react-compiler'; import {MarkerSeverity, type editor} from 'monaco-editor'; function mapReactCompilerSeverityToMonaco( @@ -22,38 +26,46 @@ function mapReactCompilerSeverityToMonaco( } function mapReactCompilerDiagnosticToMonacoMarker( - detail: CompilerErrorDetail, + detail: CompilerErrorDetail | CompilerDiagnostic, monaco: Monaco, + source: string, ): editor.IMarkerData | null { - if (detail.loc == null || typeof detail.loc === 'symbol') { + const loc = detail.primaryLocation(); + if (loc == null || typeof loc === 'symbol') { return null; } const severity = mapReactCompilerSeverityToMonaco(detail.severity, monaco); - let message = detail.printErrorMessage(); + let message = detail.printErrorMessage(source, {eslint: true}); return { severity, message, - startLineNumber: detail.loc.start.line, - startColumn: detail.loc.start.column + 1, - endLineNumber: detail.loc.end.line, - endColumn: detail.loc.end.column + 1, + startLineNumber: loc.start.line, + startColumn: loc.start.column + 1, + endLineNumber: loc.end.line, + endColumn: loc.end.column + 1, }; } type ReactCompilerMarkerConfig = { monaco: Monaco; model: editor.ITextModel; - details: Array; + details: Array; + source: string; }; let decorations: Array = []; export function renderReactCompilerMarkers({ monaco, model, details, + source, }: ReactCompilerMarkerConfig): void { const markers: Array = []; for (const detail of details) { - const marker = mapReactCompilerDiagnosticToMonacoMarker(detail, monaco); + const marker = mapReactCompilerDiagnosticToMonacoMarker( + detail, + monaco, + source, + ); if (marker == null) { continue; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Babel/BabelPlugin.ts b/compiler/packages/babel-plugin-react-compiler/src/Babel/BabelPlugin.ts index 4b59a22e802ff..ed74f4664953b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Babel/BabelPlugin.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Babel/BabelPlugin.ts @@ -84,9 +84,7 @@ export default function BabelPluginReactCompiler( } } catch (e) { if (e instanceof CompilerError) { - throw new Error( - e.printErrorMessage(pass.file.code, {eslint: false}), - ); + throw e.withPrintedMessage(pass.file.code, {eslint: false}); } throw e; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts b/compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts index b337f0c724dda..ee0d0f74c5f3c 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts @@ -262,6 +262,7 @@ export class CompilerErrorDetail { export class CompilerError extends Error { details: Array = []; + printedMessage: string | null = null; static invariant( condition: unknown, @@ -347,18 +348,29 @@ export class CompilerError extends Error { } override get message(): string { - return this.toString(); + return this.printedMessage ?? this.toString(); } override set message(_message: string) {} override toString(): string { + if (this.printedMessage) { + return this.printedMessage; + } if (Array.isArray(this.details)) { return this.details.map(detail => detail.toString()).join('\n\n'); } return this.name; } + withPrintedMessage( + source: string, + options: PrintErrorMessageOptions, + ): CompilerError { + this.printedMessage = this.printErrorMessage(source, options); + return this; + } + printErrorMessage(source: string, options: PrintErrorMessageOptions): string { if (options.eslint && this.details.length === 1) { return this.details[0].printErrorMessage(source, options);