mirror of
https://github.com/zebrajr/react.git
synced 2025-12-06 00:20:04 +01:00
Followup to https://github.com/facebook/react/pull/32758. This moves playground to use the tsup bundled plugin instead of webpack-built `babel-plugin-react-compiler`. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32759). * __->__ #32759 * #32758
160 lines
5.0 KiB
TypeScript
160 lines
5.0 KiB
TypeScript
/**
|
|
* 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<CompilerErrorDetail>;
|
|
language: 'flow' | 'typescript';
|
|
};
|
|
|
|
export default function Input({errors, language}: Props): JSX.Element {
|
|
const [monaco, setMonaco] = useState<Monaco | null>(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 (
|
|
<div className="relative flex flex-col flex-none border-r border-gray-200">
|
|
<Resizable
|
|
minWidth={650}
|
|
enable={{right: true}}
|
|
/**
|
|
* Restrict MonacoEditor's height, since the config autoLayout:true
|
|
* will grow the editor to fit within parent element
|
|
*/
|
|
className="!h-[calc(100vh_-_3.5rem)]">
|
|
<MonacoEditor
|
|
path={'index.js'}
|
|
/**
|
|
* .js and .jsx files are specified to be TS so that Monaco can actually
|
|
* check their syntax using its TS language service. They are still JS files
|
|
* due to their extensions, so TS language features don't work.
|
|
*/
|
|
language={'javascript'}
|
|
value={store.source}
|
|
onMount={handleMount}
|
|
onChange={handleChange}
|
|
options={monacoOptions}
|
|
/>
|
|
</Resizable>
|
|
</div>
|
|
);
|
|
}
|