/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ import { CodeIcon, DocumentAddIcon, InformationCircleIcon, } from '@heroicons/react/outline'; import MonacoEditor, {DiffEditor} from '@monaco-editor/react'; import { CompilerErrorDetail, CompilerDiagnostic, type CompilerError, } from 'babel-plugin-react-compiler'; import parserBabel from 'prettier/plugins/babel'; import * as prettierPluginEstree from 'prettier/plugins/estree'; import * as prettier from 'prettier/standalone'; import {type Store} from '../../lib/stores'; import { memo, ReactNode, use, useState, Suspense, unstable_ViewTransition as ViewTransition, unstable_addTransitionType as addTransitionType, startTransition, } from 'react'; import AccordionWindow from '../AccordionWindow'; import TabbedWindow from '../TabbedWindow'; import {monacoOptions} from './monacoOptions'; import {BabelFileResult} from '@babel/core'; import { CONFIG_PANEL_TRANSITION, TOGGLE_INTERNALS_TRANSITION, EXPAND_ACCORDION_TRANSITION, } from '../../lib/transitionTypes'; import {LRUCache} from 'lru-cache'; const MemoizedOutput = memo(Output); export default MemoizedOutput; export const BASIC_OUTPUT_TAB_NAMES = ['Output', 'SourceMap']; const tabifyCache = new LRUCache>>({ max: 5, }); export type PrintedCompilerPipelineValue = | { kind: 'hir'; name: string; fnName: string | null; value: string; } | {kind: 'reactive'; name: string; fnName: string | null; value: string} | {kind: 'debug'; name: string; fnName: string | null; value: string}; export type CompilerTransformOutput = { code: string; sourceMaps: BabelFileResult['map']; language: 'flow' | 'typescript'; }; export type CompilerOutput = | { kind: 'ok'; transformOutput: CompilerTransformOutput; results: Map>; errors: Array; } | { kind: 'err'; results: Map>; error: CompilerError; }; type Props = { store: Store; compilerOutput: CompilerOutput; }; async function tabify( source: string, compilerOutput: CompilerOutput, showInternals: boolean, ): Promise> { const tabs = new Map(); const reorderedTabs = new Map(); const concattedResults = new Map(); // Concat all top level function declaration results into a single tab for each pass for (const [passName, results] of compilerOutput.results) { if (!showInternals && !BASIC_OUTPUT_TAB_NAMES.includes(passName)) { continue; } for (const result of results) { switch (result.kind) { case 'hir': { const prev = concattedResults.get(result.name); const next = result.value; const identName = `function ${result.fnName}`; if (prev != null) { concattedResults.set(passName, `${prev}\n\n${identName}\n${next}`); } else { concattedResults.set(passName, `${identName}\n${next}`); } break; } case 'reactive': { const prev = concattedResults.get(passName); const next = result.value; if (prev != null) { concattedResults.set(passName, `${prev}\n\n${next}`); } else { concattedResults.set(passName, next); } break; } case 'debug': { concattedResults.set(passName, result.value); break; } default: { const _: never = result; throw new Error('Unexpected result kind'); } } } } let lastPassOutput: string | null = null; let nonDiffPasses = ['HIR', 'BuildReactiveFunction', 'EnvironmentConfig']; for (const [passName, text] of concattedResults) { tabs.set( passName, , ); lastPassOutput = text; } // Ensure that JS and the JS source map come first if (compilerOutput.kind === 'ok') { const {transformOutput} = compilerOutput; const sourceMapUrl = getSourceMapUrl( transformOutput.code, JSON.stringify(transformOutput.sourceMaps), ); const code = await prettier.format(transformOutput.code, { semi: true, parser: transformOutput.language === 'flow' ? 'babel-flow' : 'babel-ts', plugins: [parserBabel, prettierPluginEstree], }); let output: string; let language: string; if (compilerOutput.errors.length === 0) { output = code; language = 'javascript'; } else { language = 'markdown'; output = ` # Summary React Compiler compiled this function successfully, but there are lint errors that indicate potential issues with the original code. ## ${compilerOutput.errors.length} Lint Errors ${compilerOutput.errors.map(e => e.printErrorMessage(source, {eslint: false})).join('\n\n')} ## Output \`\`\`js ${code} \`\`\` `.trim(); } reorderedTabs.set( 'Output', , ); if (sourceMapUrl) { reorderedTabs.set( 'SourceMap', <>