Lazy loaded Monaco (#720)

Lazy loaded Monaco
This commit is contained in:
Jordi Bunster 2021-04-25 21:31:10 -07:00 committed by GitHub
parent 71e7ad4547
commit c7b9ff6794
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 40 additions and 90 deletions

View File

@ -1,6 +1,6 @@
import * as ViewModels from "../../../Contracts/ViewModels";
import { loadMonaco, monaco } from "../../LazyMonaco";
import template from "./diff-editor-component.html";
import * as monaco from "monaco-editor";
/**
* Helper class for ko component registration
@ -92,7 +92,7 @@ export class DiffEditorViewModel {
/**
* Create the monaco editor on diff mode and attach to DOM
*/
protected createDiffEditor(
protected async createDiffEditor(
originalContent: string,
modifiedContent: string,
createCallback: (e: monaco.editor.IStandaloneDiffEditor) => void
@ -111,7 +111,7 @@ export class DiffEditorViewModel {
}
const language = this.params.editorLanguage || "json";
const monaco = await loadMonaco();
const originalModel = monaco.editor.createModel(originalContent, language);
const modifiedModel = monaco.editor.createModel(modifiedContent, language);
const diffEditor: monaco.editor.IStandaloneDiffEditor = monaco.editor.createDiffEditor(

View File

@ -1,7 +1,6 @@
import { loadMonaco, monaco } from "../../LazyMonaco";
import { JsonEditorParams, JsonEditorViewModel } from "../JsonEditor/JsonEditorComponent";
import template from "./editor-component.html";
import * as monaco from "monaco-editor";
import { SqlCompletionItemProvider, ErrorMarkProvider } from "@azure/cosmos-language-service";
/**
* Helper class for ko component registration
@ -49,15 +48,17 @@ class EditorViewModel extends JsonEditorViewModel {
return this.params.contentType;
}
protected registerCompletionItemProvider() {
let sqlCompletionItemProvider = new SqlCompletionItemProvider();
protected async registerCompletionItemProvider() {
if (EditorViewModel.providerRegistered.indexOf("sql") < 0) {
monaco.languages.registerCompletionItemProvider("sql", sqlCompletionItemProvider);
const { SqlCompletionItemProvider } = await import("@azure/cosmos-language-service");
const monaco = await loadMonaco();
monaco.languages.registerCompletionItemProvider("sql", new SqlCompletionItemProvider());
EditorViewModel.providerRegistered.push("sql");
}
}
protected getErrorMarkers(input: string): Q.Promise<monaco.editor.IMarkerData[]> {
protected async getErrorMarkers(input: string): Promise<monaco.editor.IMarkerData[]> {
const { ErrorMarkProvider } = await import("@azure/cosmos-language-service");
return ErrorMarkProvider.getErrorMark(input);
}
}

View File

@ -1,5 +1,5 @@
import * as React from "react";
import * as monaco from "monaco-editor";
import { loadMonaco, monaco } from "../../LazyMonaco";
export interface EditorReactProps {
language: string;
@ -61,7 +61,7 @@ export class EditorReact extends React.Component<EditorReactProps> {
/**
* Create the monaco editor and attach to DOM
*/
private createEditor(createCallback: (e: monaco.editor.IStandaloneCodeEditor) => void) {
private async createEditor(createCallback: (e: monaco.editor.IStandaloneCodeEditor) => void) {
const options: monaco.editor.IEditorConstructionOptions = {
value: this.props.content,
language: this.props.language,
@ -74,6 +74,7 @@ export class EditorReact extends React.Component<EditorReactProps> {
};
this.rootNode.innerHTML = "";
const monaco = await loadMonaco();
createCallback(monaco.editor.create(this.rootNode, options));
}

View File

@ -1,6 +1,5 @@
import Q from "q";
import * as monaco from "monaco-editor";
import * as ViewModels from "../../../Contracts/ViewModels";
import { loadMonaco, monaco } from "../../LazyMonaco";
import { WaitsForTemplateViewModel } from "../../WaitsForTemplateViewModel";
import template from "./json-editor-component.html";
@ -88,7 +87,7 @@ export class JsonEditorViewModel extends WaitsForTemplateViewModel {
/**
* Create the monaco editor and attach to DOM
*/
protected createEditor(content: string, createCallback: (e: monaco.editor.IStandaloneCodeEditor) => void) {
protected async createEditor(content: string, createCallback: (e: monaco.editor.IStandaloneCodeEditor) => void) {
this.registerCompletionItemProvider();
this.editorContainer = document.getElementById(this.getEditorId());
const options: monaco.editor.IEditorConstructionOptions = {
@ -102,6 +101,7 @@ export class JsonEditorViewModel extends WaitsForTemplateViewModel {
};
this.editorContainer.innerHTML = "";
const monaco = await loadMonaco();
createCallback(monaco.editor.create(this.editorContainer, options));
}
@ -109,15 +109,16 @@ export class JsonEditorViewModel extends WaitsForTemplateViewModel {
protected registerCompletionItemProvider() {}
// Interface. Will be implemented in children editor view model such as EditorViewModel.
protected getErrorMarkers(input: string): Q.Promise<monaco.editor.IMarkerData[]> {
return Q.Promise(() => {});
protected async getErrorMarkers(_: string): Promise<monaco.editor.IMarkerData[]> {
return [];
}
protected getEditorLanguage(): string {
return "json";
}
protected configureEditor(editor: monaco.editor.IStandaloneCodeEditor) {
protected async configureEditor(editor: monaco.editor.IStandaloneCodeEditor) {
const monaco = await loadMonaco();
this.editor = editor;
const queryEditorModel = this.editor.getModel();
if (!this.params.isReadOnly && this.params.updatedContent) {

View File

@ -1,9 +1,9 @@
import { MessageBar, MessageBarType, Stack } from "office-ui-fabric-react";
import * as React from "react";
import * as DataModels from "../../../../Contracts/DataModels";
import * as monaco from "monaco-editor";
import { isDirty, isIndexTransforming } from "../SettingsUtils";
import { MessageBar, MessageBarType, Stack } from "office-ui-fabric-react";
import { loadMonaco, monaco } from "../../../LazyMonaco";
import { indexingPolicynUnsavedWarningMessage, titleAndInputStackProps } from "../SettingsRenderUtils";
import { isDirty, isIndexTransforming } from "../SettingsUtils";
import { IndexingPolicyRefreshComponent } from "./IndexingPolicyRefresh/IndexingPolicyRefreshComponent";
export interface IndexingPolicyComponentProps {
@ -84,9 +84,9 @@ export class IndexingPolicyComponent extends React.Component<
return false;
};
private createIndexingPolicyEditor = (): void => {
private async createIndexingPolicyEditor(): Promise<void> {
const value: string = JSON.stringify(this.props.indexingPolicyContent, undefined, 4);
const monaco = await loadMonaco();
this.indexingPolicyEditor = monaco.editor.create(this.indexingPolicyDiv.current, {
value: value,
language: "json",
@ -98,7 +98,7 @@ export class IndexingPolicyComponent extends React.Component<
indexingPolicyEditorModel.onDidChangeContent(this.onEditorContentChange.bind(this));
this.props.logIndexingPolicySuccessMessage();
}
};
}
private onEditorContentChange = (): void => {
const indexingPolicyEditorModel = this.indexingPolicyEditor.getModel();

View File

@ -0,0 +1,5 @@
import type * as monaco from "monaco-editor/esm/vs/editor/editor.api";
export type { monaco };
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export const loadMonaco = () => import(/* webpackChunkName: "lazy-monaco" */ "monaco-editor/esm/vs/editor/editor.api");

View File

@ -1,5 +1,4 @@
import * as ko from "knockout";
import * as monaco from "monaco-editor";
import Q from "q";
import DiscardIcon from "../../../images/discard.svg";
import SaveIcon from "../../../images/save-cosmos.svg";
@ -8,6 +7,7 @@ import editable from "../../Common/EditableUtility";
import * as DataModels from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels";
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
import { loadMonaco, monaco } from "../LazyMonaco";
import TabsBase from "./TabsBase";
export default abstract class ScriptTabBase extends TabsBase implements ViewModels.WaitsForTemplate {
@ -299,38 +299,7 @@ export default abstract class ScriptTabBase extends TabsBase implements ViewMode
return !!value;
}
private static _toSeverity(severity: string): monaco.MarkerSeverity {
switch (severity.toLowerCase()) {
case "error":
return monaco.MarkerSeverity.Error;
case "warning":
return monaco.MarkerSeverity.Warning;
case "info":
return monaco.MarkerSeverity.Info;
case "ignore":
default:
return monaco.MarkerSeverity.Hint;
}
}
private static _toEditorPosition(target: number, lines: string[]): ViewModels.EditorPosition {
let cursor: number = 0;
let previousCursor: number = 0;
let i: number = 0;
while (target > cursor + lines[i].length) {
cursor += lines[i].length + 2;
i++;
}
const editorPosition: ViewModels.EditorPosition = {
line: i + 1,
column: target - cursor + 1,
};
return editorPosition;
}
protected _createBodyEditor() {
protected async _createBodyEditor() {
const id = this.editorId;
const container = document.getElementById(id);
const options = {
@ -341,7 +310,7 @@ export default abstract class ScriptTabBase extends TabsBase implements ViewMode
};
container.innerHTML = "";
const monaco = await loadMonaco();
const editor = monaco.editor.create(container, options);
this.editor(editor);
@ -353,32 +322,4 @@ export default abstract class ScriptTabBase extends TabsBase implements ViewMode
const editorModel = this.editor().getModel();
this.editorContent(editorModel.getValue());
}
private _setModelMarkers(errors: ViewModels.QueryError[]) {
const markers: monaco.editor.IMarkerData[] = errors.map((e) => this._toMarker(e));
const editorModel = this.editor().getModel();
monaco.editor.setModelMarkers(editorModel, this.tabId, markers);
}
private _resetModelMarkers() {
const queryEditorModel = this.editor().getModel();
monaco.editor.setModelMarkers(queryEditorModel, this.tabId, []);
}
private _toMarker(error: ViewModels.QueryError): monaco.editor.IMarkerData {
const editorModel = this.editor().getModel();
const lines: string[] = editorModel.getLinesContent();
const start: ViewModels.EditorPosition = ScriptTabBase._toEditorPosition(Number(error.start), lines);
const end: ViewModels.EditorPosition = ScriptTabBase._toEditorPosition(Number(error.end), lines);
return {
severity: ScriptTabBase._toSeverity(error.severity),
message: error.message,
startLineNumber: start.line,
startColumn: start.column,
endLineNumber: end.line,
endColumn: end.column,
code: error.code,
};
}
}

View File

@ -1,3 +1,5 @@
/* eslint-disable @typescript-eslint/no-var-requires */
/* eslint-disable no-undef */
require("dotenv/config");
const path = require("path");
const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin");
@ -83,7 +85,8 @@ const typescriptRule = {
exclude: /node_modules/,
};
module.exports = function (env = {}, argv = {}) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
module.exports = function (_env = {}, argv = {}) {
const mode = argv.mode || "development";
const rules = [fontRule, lessRule, imagesRule, cssRule, htmlRule, typescriptRule];
const envVars = {
@ -211,6 +214,7 @@ module.exports = function (env = {}, argv = {}) {
util: true,
tls: "empty",
net: "empty",
fs: "empty",
},
output: {
chunkFilename: "[name].[chunkhash:6].js",
@ -264,7 +268,7 @@ module.exports = function (env = {}, argv = {}) {
target: "https://main.documentdb.ext.azure.com",
changeOrigin: true,
logLevel: "debug",
bypass: function (req, res, proxyOptions) {
bypass: (req, res) => {
if (req.method === "OPTIONS") {
res.statusCode = 200;
res.send();
@ -304,8 +308,5 @@ module.exports = function (env = {}, argv = {}) {
},
},
stats: "minimal",
node: {
fs: "empty",
},
};
};