Skip to content

Commit

Permalink
perf: migrate monaco-editor to @monaco-editor/react (#8)
Browse files Browse the repository at this point in the history
* perf: migrate monaco-editor to @monaco-editor/react

* chore: update rspack config  option
  • Loading branch information
ZLY201 authored Oct 23, 2023
1 parent da7a72d commit ff0086f
Show file tree
Hide file tree
Showing 10 changed files with 149 additions and 127 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
"varsIgnorePattern": "^[_]*$"
}
],
"max-len": ["error", { "code": 100, "ignoreComments": true }],
"max-len": ["error", { "code": 120, "ignoreComments": true }],
"object-curly-spacing": ["error", "always"],
"prettier/prettier": [
"error",
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,12 @@
"dependencies": {
"@arco-design/theme-line": "^0.0.3",
"@arco-design/web-react": "^2.53.2",
"@monaco-editor/react": "^4.6.0",
"axios": "^1.5.1",
"dayjs": "^1.11.10",
"event-emitter": "^0.3.5",
"lodash.debounce": "^4.0.8",
"monaco-editor": "^0.43.0",
"monaco-editor": "^0.44.0",
"react": "^17.0.2",
"react-copy-to-clipboard": "^5.1.0",
"react-dom": "^17.0.2",
Expand Down
9 changes: 1 addition & 8 deletions rspack.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,9 @@ module.exports = {
maxAsyncRequests: 30,
maxInitialRequests: 30,
cacheGroups: {
monaco: {
chunks: 'async',
test: /[\\/]node_modules[\\/]monaco-editor/,
priority: 1000,
name: 'monaco',
reuseExistingChunk: true,
},
common: {
chunks: 'all',
test: /[\\/]node_modules[\\/](react|react-dom|@arco-design[\\/]web-react)/,
test: /[\\/]node_modules[\\/](react|react-dom|@arco-design[\\/]web-react|@monaco-editor\/react)/,
priority: 100,
name: 'common',
reuseExistingChunk: true,
Expand Down
9 changes: 6 additions & 3 deletions src/modules/Editor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ function Editor() {
}

const updateRaw = useCallback(
debounce(async function (problem: Problem, needDefault = false) {
const raw = await getProblemRaw(problem, needDefault);
debounce(async function (problem: Problem) {
const raw = await getProblemRaw(problem);
setRaw(raw);
setLoading(false);
}, 500),
Expand All @@ -50,7 +50,10 @@ function Editor() {
cancelText: 'cancel',
onOk: async function () {
setLoading(true);
await updateRaw(currentProblem, true);
localCache.setProblemCache(currentProblem.key, {
lastUpdated: null,
});
await updateRaw(currentProblem);
modal.close();
},
});
Expand Down
113 changes: 58 additions & 55 deletions src/modules/Editor/monaco-editor.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// https://github.com/typescript-exercises/typescript-exercises/blob/main/src/components/monaco-editor/index.tsx
import debounce from 'lodash.debounce';
import { editor, languages, Uri } from 'monaco-editor';
import React from 'react';
import { Editor, Monaco } from '@monaco-editor/react';
import type { editor } from 'monaco-editor';
import { Skeleton } from '@arco-design/web-react';
import { decorateWithAutoResize } from '@src/components/AutoResizer';
import {
formatCodeByUpdateTabSize,
Expand All @@ -10,39 +12,8 @@ import {
} from '@src/utils/problems';
import { Setting } from '@src/utils/setting';
import emitter from '@src/utils/emit';
import { validateMonacoModel } from '@src/utils/validate-monaco-model';

self.MonacoEnvironment = {
getWorker: function () {
return new Worker(
new URL(
'monaco-editor/esm/vs/language/typescript/ts.worker',
import.meta.url,
),
);
},
};

languages.typescript.typescriptDefaults.setCompilerOptions({
strict: true,
target: languages.typescript.ScriptTarget.ES2018,
moduleResolution: languages.typescript.ModuleResolutionKind.NodeJs,
typeRoots: ['declarations'],
});

editor.defineTheme('light', {
base: 'vs',
colors: {},
inherit: true,
rules: [],
});

editor.defineTheme('dark', {
base: 'vs-dark',
colors: {},
inherit: true,
rules: [],
});
import { assignMonacoInstance, validateMonacoModel } from '@src/utils/monaco';
import styles from './index.module.less';

export interface MonacoEditorProps {
width?: number | string;
Expand All @@ -61,7 +32,6 @@ interface Models {
const MonacoEditor = decorateWithAutoResize(
class extends React.Component<MonacoEditorProps> {
protected instance: editor.IStandaloneCodeEditor | null = null;
protected instanceDiv: HTMLElement | null = null;
protected models: Models = {};
protected viewStates: { [filename: string]: editor.ICodeEditorViewState } =
{};
Expand All @@ -70,13 +40,33 @@ const MonacoEditor = decorateWithAutoResize(
const model = this.models[ProblemFiles.template];
model.setValue(formatCodeByUpdateTabSize(model.getValue(), prev, next));
};
componentDidMount() {
beforeMount = (monaco: Monaco) => {
assignMonacoInstance(monaco);
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
strict: true,
target: monaco.languages.typescript.ScriptTarget.ES2018,
moduleResolution:
monaco.languages.typescript.ModuleResolutionKind.NodeJs,
typeRoots: ['declarations'],
});
monaco.editor.defineTheme('light', {
base: 'vs',
colors: {},
inherit: true,
rules: [],
});
monaco.editor.defineTheme('dark', {
base: 'vs-dark',
colors: {},
inherit: true,
rules: [],
});
for (const [filename, { content }] of Object.entries(this.props.raw)) {
this.lastUpdates[filename] = content;
const model = editor.createModel(
const model = monaco.editor.createModel(
content,
undefined,
Uri.file(`${this.props.namespace}/${filename}`),
monaco.Uri.file(`${this.props.namespace}/${filename}`),
);
model.onDidChangeContent(
debounce(() => {
Expand All @@ -87,21 +77,11 @@ const MonacoEditor = decorateWithAutoResize(
);
this.models[filename] = model;
}
this.instance = editor.create(this.instanceDiv!, {
...this.props.setting,
model: this.models[this.props.selectedFilename],
readOnly: Boolean(this.props.raw[this.props.selectedFilename].readOnly),
renderValidationDecorations: 'on',
minimap: {
enabled: false,
},
autoIndent: 'advanced',
formatOnPaste: true,
formatOnType: true,
});
this.instance.layout();
};
onMount = (instance: editor.IStandaloneCodeEditor) => {
this.instance = instance;
emitter.on('tabSizeChange', this.updateTabSize);
}
};
componentWillUnmount() {
emitter.off('tabSizeChange', this.updateTabSize);
for (const filename of Object.keys(this.models)) {
Expand All @@ -110,6 +90,9 @@ const MonacoEditor = decorateWithAutoResize(
if (this.instance) {
this.instance.dispose();
}
this.models = {};
this.viewStates = {};
this.lastUpdates = {};
this.instance = null;
}
componentDidUpdate(prevProps: Readonly<MonacoEditorProps>): void {
Expand Down Expand Up @@ -155,9 +138,29 @@ const MonacoEditor = decorateWithAutoResize(
}
render() {
return (
<div
style={{ width: this.props.width, height: this.props.height }}
ref={(newRef: HTMLElement | null) => (this.instanceDiv = newRef)}
<Editor
width={this.props.width}
height={this.props.height}
defaultPath={`${this.props.namespace}/${this.props.selectedFilename}`}
loading={
<Skeleton
className={styles.skeleton}
text={{ rows: 3 }}
animation={true}
/>
}
beforeMount={this.beforeMount}
onMount={this.onMount}
options={{
...this.props.setting,
renderValidationDecorations: 'on',
minimap: {
enabled: false,
},
autoIndent: 'advanced',
formatOnPaste: true,
formatOnType: true,
}}
/>
);
}
Expand Down
81 changes: 39 additions & 42 deletions src/modules/Results/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { editor, Uri } from 'monaco-editor';
import type { editor } from 'monaco-editor';
import dayjs from 'dayjs';
import { useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { Button, Input, Skeleton } from '@arco-design/web-react';
Expand All @@ -15,7 +15,7 @@ import {
ProblemTestReplaceVal,
} from '@src/utils/problems';
import i18nJson from '@config/i18n.json';
import { validateMonacoModel } from '@src/utils/validate-monaco-model';
import { monacoInstance, validateMonacoModel } from '@src/utils/monaco';
import styles from './index.module.less';

const enum MainTab {
Expand All @@ -40,7 +40,6 @@ const Results = function () {
const noCases = useMemo(() => originCases.length === 0, [originCases]);
const [cases, setCases] = useState(noCases ? [NULL_CASE] : originCases);
const [testRaw, setTestRaw] = useState<string | undefined>(undefined);
const [model, setModel] = useState<editor.ITextModel | undefined>(undefined);
const [casesErrors, setCasesErrors] = useState<string[][]>([]);

const updateData = useCallback(
Expand All @@ -64,74 +63,72 @@ const Results = function () {
[currentProblem],
);

useEffect(
function () {
if (testRaw === undefined) {
setModel(undefined);
} else {
const uri = Uri.file(`${currentProblem.key}/${ProblemFiles.test}`);
const m =
editor.getModel(uri) || editor.createModel(testRaw, undefined, uri);
setModel(m);
}
return function () {
model?.dispose();
};
},
[testRaw],
);

async function run() {
if (!monacoInstance || !testRaw) return;
setLoading(true);
setActiveMainTab(MainTab.result);
setStatus([]);
await new Promise(resolve => setTimeout(resolve, 500));
const templateUri = Uri.file(
const templateUri = monacoInstance.Uri.file(
`${currentProblem.key}/${ProblemFiles.template}`,
);
const markers = editor.getModelMarkers({
const markers = monacoInstance.editor.getModelMarkers({
resource: templateUri,
});
const errors = formatErrorFromMarkers(markers);
if (errors.length > 0) {
setStatus(errors);
} else {
} else if (testRaw) {
const e = [];
if (model && testRaw) {
for (const { source, target } of cases) {
const content = testRaw
.replace(ProblemTestReplaceVal.source, source)
.replace(ProblemTestReplaceVal.target, target);
model.setValue(content);
await validateMonacoModel(model);
const markers = editor.getModelMarkers({
resource: Uri.file(`${currentProblem.key}/${ProblemFiles.test}`),
});
const errors = formatErrorFromMarkers(markers);
e.push(errors);
}
setCasesErrors(e);
const uri = monacoInstance.Uri.file(
`${currentProblem.key}/${ProblemFiles.test}`,
);
const model =
monacoInstance.editor.getModel(uri) ||
monacoInstance.editor.createModel(testRaw, undefined, uri);
for (const { source, target } of cases) {
const content = testRaw
.replace(ProblemTestReplaceVal.source, source)
.replace(ProblemTestReplaceVal.target, target);
model.setValue(content);
await validateMonacoModel(model);
const markers = monacoInstance.editor.getModelMarkers({
resource: monacoInstance.Uri.file(
`${currentProblem.key}/${ProblemFiles.test}`,
),
});
const errors = formatErrorFromMarkers(markers);
e.push(errors);
}
model.dispose();
setCasesErrors(e);
}
setLoading(false);
}

async function onSubmit() {
if (!monacoInstance) return;
setLoading(true);
await new Promise(resolve => setTimeout(resolve, 500));
const markers = [
...editor.getModelMarkers({
resource: Uri.file(`${currentProblem.key}/${ProblemFiles.template}`),
...monacoInstance.editor.getModelMarkers({
resource: monacoInstance.Uri.file(
`${currentProblem.key}/${ProblemFiles.template}`,
),
}),
...editor.getModelMarkers({
resource: Uri.file(`${currentProblem.key}/${ProblemFiles.check}`),
...monacoInstance.editor.getModelMarkers({
resource: monacoInstance.Uri.file(
`${currentProblem.key}/${ProblemFiles.check}`,
),
}),
];
const errors = formatErrorFromMarkers(markers);
setStatus(errors.length > 0 ? errors : 'Accept!');
const status =
errors.length > 0 ? PROBLEM_STATUS.unAccepted : PROBLEM_STATUS.accepted;
const code = editor.getModel(Uri.file(`${key}/template.ts`))!.getValue();
const code = monacoInstance.editor
.getModel(monacoInstance.Uri.file(`${key}/template.ts`))!
.getValue();
localCache.setProblemCache(key, {
records: [
{
Expand Down
5 changes: 3 additions & 2 deletions src/utils/local-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export type ProblemRecord = {
export type ProblemCache = {
records?: ProblemRecord[];
status?: PROBLEM_STATUS;
lastUpdated?: string;
lastUpdated?: string | null;
};

export type ProblemsCacheJson = {
Expand Down Expand Up @@ -78,7 +78,8 @@ const localCache = {
} else if (status === PROBLEM_STATUS.accepted) {
newStatus = status;
}
const newLastUpdated = lastUpdated ?? oldLastUpdated ?? '';
const newLastUpdated =
lastUpdated === null ? undefined : lastUpdated ?? oldLastUpdated ?? '';
newCache = {
records: newRecords,
status: newStatus,
Expand Down
Loading

0 comments on commit ff0086f

Please sign in to comment.