/** * 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 MonacoEditor, {loader, type Monaco} from '@monaco-editor/react'; import {CompilerErrorDetail} from 'babel-plugin-react-compiler'; import invariant from 'invariant'; import type {editor} from 'monaco-editor'; import * as monaco from 'monaco-editor'; import {Resizable} from 're-resizable'; import {useEffect, useState} from 'react'; import {renderReactCompilerMarkers} from '../../lib/reactCompilerMonacoDiagnostics'; import {useStore, useStoreDispatch} from '../StoreContext'; import {monacoOptions} from './monacoOptions'; // @ts-expect-error TODO: Make TS recognize .d.ts files, in addition to loading them with webpack. import React$Types from '../../node_modules/@types/react/index.d.ts'; loader.config({monaco}); type Props = { errors: Array; language: 'flow' | 'typescript'; }; export default function Input({errors, language}: Props): JSX.Element { const [monaco, setMonaco] = useState(null); const store = useStore(); const dispatchStore = useStoreDispatch(); // Set tab width to 2 spaces for the selected input file. useEffect(() => { if (!monaco) return; 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}); /** * 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]); useEffect(() => { /** * Ignore "can only be used in TypeScript files." errors, since * we want to support syntax highlighting for Flow (*.js) files * and Flow is not a built-in language. */ if (!monaco) return; monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({ diagnosticCodesToIgnore: [ 8002, 8003, 8004, 8005, 8006, 8008, 8009, 8010, 8011, 8012, 8013, ...(language === 'flow' ? [7028 /* unused label */, 6133 /* var declared but not read */] : []), ], noSemanticValidation: true, // Monaco can't validate Flow component syntax noSyntaxValidation: language === 'flow', }); }, [monaco, language]); const handleChange: (value: string | undefined) => void = value => { if (!value) return; dispatchStore({ type: 'updateFile', payload: { source: value, }, }); }; const handleMount: ( _: editor.IStandaloneCodeEditor, monaco: Monaco, ) => void = (_, monaco) => { if (typeof window !== 'undefined') { window['__MONACO_LOADED__'] = true; } setMonaco(monaco); const tscOptions = { allowNonTsExtensions: true, target: monaco.languages.typescript.ScriptTarget.ES2015, moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs, jsx: monaco.languages.typescript.JsxEmit.Preserve, typeRoots: ['node_modules/@types'], allowSyntheticDefaultImports: true, }; monaco.languages.typescript.javascriptDefaults.setCompilerOptions( tscOptions, ); monaco.languages.typescript.typescriptDefaults.setCompilerOptions({ ...tscOptions, checkJs: true, allowJs: true, }); // Add React type declarations to Monaco const reactLib = [ React$Types, 'file:///node_modules/@types/react/index.d.ts', ] as [any, string]; monaco.languages.typescript.javascriptDefaults.addExtraLib(...reactLib); monaco.languages.typescript.typescriptDefaults.addExtraLib(...reactLib); /** * Remeasure the font in case the custom font is loaded only after * Monaco Editor is mounted. * N.B. that this applies also to the output editor as it seems * Monaco Editor instances share the same font config. */ document.fonts.ready.then(() => { monaco.editor.remeasureFonts(); }); }; return (
); }