diff --git a/data-extraction/src/getGlobal.ts b/data-extraction/src/getGlobal.ts new file mode 100644 index 0000000..eabfe4f --- /dev/null +++ b/data-extraction/src/getGlobal.ts @@ -0,0 +1,11 @@ +export function getGlobal(): any { + if (typeof globalThis === "object") { + return globalThis; + } else if (typeof global === "object") { + return global; + } else if (typeof window === "object") { + return window; + } + + throw new Error("No global available"); +} diff --git a/data-extraction/src/js/api/DataExtractorApi.ts b/data-extraction/src/js/api/DataExtractorApi.ts index 8f4cfc1..7647462 100644 --- a/data-extraction/src/js/api/DataExtractorApi.ts +++ b/data-extraction/src/js/api/DataExtractorApi.ts @@ -2,6 +2,7 @@ import { VisualizationData, DataExtractionResult, } from "../../DataExtractionResult"; +import { LoadDataExtractorsFn } from "./LoadDataExtractorsFn"; export interface DataExtractorApi { /** @@ -31,6 +32,9 @@ export interface DataExtractorApi { * @preferExisting if `true`, existing extractors with the same id are not overwritten. */ registerDefaultExtractors(preferExisting?: boolean): void; + + registerDataExtractorsSource(id: string, fn: LoadDataExtractorsFn): void; + unregisterDataExtractorsSource(id: string): void; } export type DataResult = @@ -70,7 +74,7 @@ export interface DataExtractorContext { */ evalFn: (expression: string) => TEval; - variablesInScope: Record; + variablesInScope: Record unknown>; } export interface DataExtraction { diff --git a/data-extraction/src/js/api/DataExtractorApiImpl.ts b/data-extraction/src/js/api/DataExtractorApiImpl.ts index ef4c864..efc321d 100644 --- a/data-extraction/src/js/api/DataExtractorApiImpl.ts +++ b/data-extraction/src/js/api/DataExtractorApiImpl.ts @@ -5,20 +5,21 @@ import { DataExtractor, DataExtraction, ExtractionCollector, + DataExtractorContext, } from "./DataExtractorApi"; import { DataExtractorInfo } from "../../DataExtractionResult"; import { registerDefaultExtractors } from "./default-extractors"; +import { LoadDataExtractorsFn } from "./LoadDataExtractorsFn"; +import * as helpers from "../helpers"; /** * @internal */ export class DataExtractorApiImpl implements DataExtractorApi { - public static lastEvalFn: ((expression: string) => T) | undefined; - public static lastVariablesInScope: - | Record - | undefined = undefined; + public static lastContext: DataExtractorContext | undefined = undefined; private readonly extractors = new Map(); + private readonly extractorSources = new Map(); private toJson(data: TData): JSONString { return JSON.stringify(data) as any; @@ -38,7 +39,7 @@ export class DataExtractorApiImpl implements DataExtractorApi { valueFn: () => unknown, evalFn: (expression: string) => T, preferredDataExtractorId: string | undefined, - variablesInScope: Record + variablesInScope: Record unknown> ): JSONString { const extractions = new Array(); const extractionCollector: ExtractionCollector = { @@ -47,19 +48,27 @@ export class DataExtractorApiImpl implements DataExtractorApi { }, }; - DataExtractorApiImpl.lastVariablesInScope = variablesInScope; - DataExtractorApiImpl.lastEvalFn = evalFn; + const context: DataExtractorContext = { + evalFn, + variablesInScope, + }; + + DataExtractorApiImpl.lastContext = context; const value = valueFn(); - for (const e of this.extractors.values()) { - e.getExtractions(value, extractionCollector, { - evalFn, - variablesInScope, - }); + const extractors = new Array(); + + for (const fn of this.extractorSources.values()) { + fn((extractor) => { + extractors.push(extractor); + }, helpers); } - DataExtractorApiImpl.lastVariablesInScope = undefined; - DataExtractorApiImpl.lastEvalFn = undefined; + for (const e of [...this.extractors.values(), ...extractors]) { + e.getExtractions(value, extractionCollector, context); + } + + DataExtractorApiImpl.lastContext = undefined; extractions.sort((a, b) => b.priority - a.priority); let usedExtraction = extractions[0]; @@ -69,7 +78,7 @@ export class DataExtractorApiImpl implements DataExtractorApi { if (preferredDataExtractorId) { const preferred = extractions.find( - e => e.id === preferredDataExtractorId + (e) => e.id === preferredDataExtractorId ); if (preferred) { usedExtraction = preferred; @@ -99,4 +108,15 @@ export class DataExtractorApiImpl implements DataExtractorApi { // TODO consider preferExisting registerDefaultExtractors(this); } + + public registerDataExtractorsSource( + id: string, + fn: LoadDataExtractorsFn + ): void { + this.extractorSources.set(id, fn); + } + + public unregisterDataExtractorsSource(id: string): void { + this.extractorSources.delete(id); + } } diff --git a/data-extraction/src/js/api/default-extractors/InjectedExtractor.ts b/data-extraction/src/js/api/default-extractors/InjectedExtractor.ts deleted file mode 100644 index 68f1d98..0000000 --- a/data-extraction/src/js/api/default-extractors/InjectedExtractor.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { - DataExtractor, - ExtractionCollector, - DataExtractorContext, - LoadDataExtractorsFn, -} from ".."; -import * as helpers from "../../helpers"; - -export class InjectedExtractor implements DataExtractor { - readonly id = "injected-extractor"; - - getExtractions( - data: unknown, - extractionCollector: ExtractionCollector, - context: DataExtractorContext - ): void { - const key = "@hediet/debug-visualizer/injectedScripts"; - const injectedExtractorsFns = Object.values( - (global as any)[key] - ) as LoadDataExtractorsFn[]; - - const extractors = new Array(); - - for (const fn of injectedExtractorsFns) { - fn(extractor => { - extractors.push(extractor); - }, helpers); - } - - for (const extractor of extractors) { - extractor.getExtractions(data, extractionCollector, context); - } - } -} diff --git a/data-extraction/src/js/api/default-extractors/registerDefaultDataExtractors.ts b/data-extraction/src/js/api/default-extractors/registerDefaultDataExtractors.ts index 86a7ab9..f31b151 100644 --- a/data-extraction/src/js/api/default-extractors/registerDefaultDataExtractors.ts +++ b/data-extraction/src/js/api/default-extractors/registerDefaultDataExtractors.ts @@ -9,7 +9,6 @@ import { getDataExtractorApi } from "../injection"; import { GridExtractor } from "./GridExtractor"; import { TableDataExtractor } from "./TableExtractor"; import { StringDiffExtractor } from "./StringDiffExtractor"; -import { InjectedExtractor } from "./InjectedExtractor"; /** * The default data extractors should be registered by VS Code automatically. @@ -28,7 +27,6 @@ export function registerDefaultExtractors( new GridExtractor(), new TableDataExtractor(), new StringDiffExtractor(), - new InjectedExtractor(), ]) { api.registerExtractor(item); } diff --git a/data-extraction/src/js/api/injection.ts b/data-extraction/src/js/api/injection.ts index b8cb175..b37d8d1 100644 --- a/data-extraction/src/js/api/injection.ts +++ b/data-extraction/src/js/api/injection.ts @@ -3,6 +3,7 @@ import { DataExtractorApi } from "./DataExtractorApi"; import { DataExtractorApiImpl } from "./DataExtractorApiImpl"; import * as helpers from "../helpers"; import * as globalHelpers from "../global-helpers"; +import { getGlobal } from "../../getGlobal"; /** * Returns standalone JS code representing an expression that initializes the data extraction API. @@ -28,16 +29,11 @@ export function getExpressionForDataExtractorApi(): string { return `((${selfContainedGetInitializedDataExtractorApi.toString()})())`; } -export function getExpressionToDetectDataExtractorApiPresence(): string { - return `((${selfContainedIsDataExtractorApiInitialized.toString()})())`; -} - -const apiKey = "@hediet/data-extractor-api/v2"; +const apiKey = "@hediet/data-extractor-api/v3"; export function getDataExtractorApi(): DataExtractorApi { installHelpers(); - const globalObj = - typeof window === "object" ? (window as any) : (global as any); + const globalObj = getGlobal(); if (!globalObj[apiKey]) { globalObj[apiKey] = new DataExtractorApiImpl(); } @@ -45,33 +41,31 @@ export function getDataExtractorApi(): DataExtractorApi { } /** + * This code is used to detect if the API has not been initialized yet. * @internal */ -function selfContainedIsDataExtractorApiInitialized(): boolean { - const globalObj = - typeof window === "object" ? (window as any) : (global as any); - const key: typeof apiKey = "@hediet/data-extractor-api/v2"; - let api: DataExtractorApi | undefined = globalObj[key]; - return api !== undefined; -} + export const ApiHasNotBeenInitializedCode = "EgH0cybXij1jYUozyakO" as const; -/** - * @internal - */ -function selfContainedGetInitializedDataExtractorApi(): DataExtractorApi { - const globalObj = - typeof window === "object" ? (window as any) : (global as any); - const key: typeof apiKey = "@hediet/data-extractor-api/v2"; - let api: DataExtractorApi | undefined = globalObj[key]; - if (!api) { - throw new Error(`Data Extractor API has not been initialized.`); - } - return api; -} + /** + * @internal + */ + function selfContainedGetInitializedDataExtractorApi(): DataExtractorApi { + const globalObj = + typeof window === "object" ? (window as any) : (global as any); + const key: typeof apiKey = "@hediet/data-extractor-api/v3"; + let api: DataExtractorApi | undefined = globalObj[key]; + if (!api) { + const code: typeof ApiHasNotBeenInitializedCode = + "EgH0cybXij1jYUozyakO"; + throw new Error( + `Data Extractor API has not been initialized. Code: ${code}` + ); + } + return api; + } export function installHelpers(): void { - const globalObj = - typeof window === "object" ? (window as any) : (global as any); + const globalObj = getGlobal(); // `hediet` as prefix to avoid name collision (I own `hediet.de`). globalObj["hedietDbgVis"] = { ...helpers, ...globalHelpers }; } diff --git a/data-extraction/src/js/helpers/cache.ts b/data-extraction/src/js/helpers/cache.ts index d804554..7f4d3e0 100644 --- a/data-extraction/src/js/helpers/cache.ts +++ b/data-extraction/src/js/helpers/cache.ts @@ -12,8 +12,8 @@ export function cache( let resultFn: () => any; let key: string; if (typeof expression === "string") { - const evalFn = DataExtractorApiImpl.lastEvalFn!; - resultFn = () => evalFn(expression); + const context = DataExtractorApiImpl.lastContext!; + resultFn = () => context.evalFn(expression); key = JSON.stringify({ expr: expression, id }); } else { resultFn = () => expression(); diff --git a/data-extraction/src/js/helpers/find.ts b/data-extraction/src/js/helpers/find.ts index bbb1035..6ef44d9 100644 --- a/data-extraction/src/js/helpers/find.ts +++ b/data-extraction/src/js/helpers/find.ts @@ -2,12 +2,22 @@ import { DataExtractorApiImpl } from "../api"; export function find(predicate: (obj: unknown) => boolean): unknown { const processed = new Set(); - if (!DataExtractorApiImpl.lastVariablesInScope) { - throw new Error("No variables in scope!"); + if (!DataExtractorApiImpl.lastContext) { + throw new Error("No data extractor context!"); } - const values = Object.values(DataExtractorApiImpl.lastVariablesInScope); - const queue = [...values]; + const values = Object.values( + DataExtractorApiImpl.lastContext.variablesInScope + ); + const queue = [ + ...values.map((v) => { + try { + return v(); + } catch (e) { + return undefined; + } + }), + ]; let i = 10000; while (i > 0) { diff --git a/data-extraction/src/js/helpers/tryEval.ts b/data-extraction/src/js/helpers/tryEval.ts index bab659c..eea5149 100644 --- a/data-extraction/src/js/helpers/tryEval.ts +++ b/data-extraction/src/js/helpers/tryEval.ts @@ -30,17 +30,17 @@ export function tryEval( obj: Record | string[] | string ): Record | unknown { const result: Record = {}; - const evalFn = DataExtractorApiImpl.lastEvalFn!; + const context = DataExtractorApiImpl.lastContext!; if (Array.isArray(obj)) { for (const val of obj) { try { - result[val] = evalFn(val); + result[val] = context.evalFn(val); } catch (e) {} } } else { for (const [key, val] of Object.entries(obj)) { try { - result[key] = evalFn(val); + result[key] = context.evalFn(val); } catch (e) {} } } diff --git a/docs/custom-scripts.gif b/docs/custom-scripts.gif new file mode 100644 index 0000000..433537f Binary files /dev/null and b/docs/custom-scripts.gif differ diff --git a/extension/package.json b/extension/package.json index c65ada2..77e61c8 100644 --- a/extension/package.json +++ b/extension/package.json @@ -4,7 +4,7 @@ "displayName": "Debug Visualizer", "description": "A visual watch window that lets you visualize your data structures while debugging.", "icon": "docs/logo.drawio.png", - "version": "2.2.1", + "version": "2.2.3", "license": "GPL-3.0", "engines": { "vscode": "^1.46.0" diff --git a/extension/src/VisualizationBackend/JsVisualizationSupport.ts b/extension/src/VisualizationBackend/JsVisualizationSupport.ts index 58b6823..ae6836d 100644 --- a/extension/src/VisualizationBackend/JsVisualizationSupport.ts +++ b/extension/src/VisualizationBackend/JsVisualizationSupport.ts @@ -1,31 +1,25 @@ import { - getExpressionForDataExtractorApi, - DataResult, - getExpressionToInitializeDataExtractorApi, DataExtractionResult, - getExpressionToDetectDataExtractorApiPresence, + DataResult, + getExpressionForDataExtractorApi, + getExpressionToInitializeDataExtractorApi, + ApiHasNotBeenInitializedCode, } from "@hediet/debug-visualizer-data-extraction"; -import { DebugSessionProxy } from "../proxies/DebugSessionProxy"; -import { - DebugSessionVisualizationSupport, - VisualizationBackend, - GetVisualizationDataArgs, - VisualizationBackendBase, -} from "./VisualizationBackend"; -import { FormattedMessage } from "../webviewContract"; -import { registerUpdateReconciler, hotClass } from "@hediet/node-reload"; +import { hotClass, registerUpdateReconciler } from "@hediet/node-reload"; +import { existsSync, readFileSync, watch } from "fs"; +import { observable, reaction } from "mobx"; +import { window, workspace } from "vscode"; import { Config } from "../Config"; import { DebuggerViewProxy } from "../proxies/DebuggerViewProxy"; -import { readFileSync, existsSync } from "fs"; -import { reaction, observable } from "mobx"; +import { DebugSessionProxy } from "../proxies/DebugSessionProxy"; +import { FormattedMessage } from "../webviewContract"; import { - workspace, - FileSystemWatcher, - CancellationTokenSource, - CancellationToken, - window, -} from "vscode"; -import { ResettableTimeout } from "@hediet/std/timer"; + DebugSessionVisualizationSupport, + GetVisualizationDataArgs, + VisualizationBackend, + VisualizationBackendBase, +} from "./VisualizationBackend"; +import { Disposable } from "@hediet/std/disposable"; registerUpdateReconciler(module); @@ -64,8 +58,7 @@ export class JsEvaluationEngine implements DebugSessionVisualizationSupport { class JsVisualizationBackend extends VisualizationBackendBase { public readonly expressionLanguageId = "javascript"; - private apiInitialized = false; - private initializedPromise: Promise | undefined = undefined; + private initializePromise: Promise | undefined = undefined; constructor( debugSession: DebugSessionProxy, @@ -82,16 +75,41 @@ class JsVisualizationBackend extends VisualizationBackendBase { return "repl"; } - public async getVisualizationData({ + public async getVisualizationData( + args: GetVisualizationDataArgs + ): Promise< + | { kind: "data"; result: DataExtractionResult } + | { kind: "error"; message: FormattedMessage } + > { + const result = await this._getVisualizationData(args); + + if (result.kind === "not-initialized") { + await this.initializeApiSafe(); + + const result2 = await this._getVisualizationData(args); + + if (result2.kind !== "not-initialized") { + return result2; + } else { + return { + kind: "error", + message: "Could not initialize API", + }; + } + } + + return result; + } + + private async _getVisualizationData({ expression, preferredExtractorId, }: GetVisualizationDataArgs): Promise< | { kind: "data"; result: DataExtractionResult } | { kind: "error"; message: FormattedMessage } + | { kind: "not-initialized" } > { try { - await this.ensureRuntimeIsInjected(); - const frameId = this.debuggerView.getActiveStackFrameId( this.debugSession ); @@ -101,8 +119,8 @@ class JsVisualizationBackend extends VisualizationBackendBase { const scopes = await this.debugSession.getScopes({ frameId }); const scopeVariables = await Promise.all( scopes - .filter(s => !s.expensive) - .map(s => + .filter((s) => !s.expensive && s.name !== "Global") + .map((s) => this.debugSession.getVariables({ variablesReference: s.variablesReference, }) @@ -111,13 +129,13 @@ class JsVisualizationBackend extends VisualizationBackendBase { for (const variables of scopeVariables) { variableNames.push( ...variables - .filter(v => v.value !== "undefined") - .map(v => v.name) + .filter((v) => v.value !== "undefined") + .map((v) => v.name) ); } - if (variableNames.length > 100) { - variableNames.length = 100; + if (variableNames.length > 50) { + variableNames.length = 50; } } @@ -129,7 +147,7 @@ class JsVisualizationBackend extends VisualizationBackendBase { e => (${expression}), expr => eval(expr), ${preferredExtractorExpr}, - {${variableNames.map(n => `${n}: ${n}`).join(",")}}, + {${variableNames.map((n) => `${n}: () => ${n}`).join(",")}}, )`; const wrappedExpr = ` @@ -171,6 +189,15 @@ class JsVisualizationBackend extends VisualizationBackendBase { throw new Error("Invalid Data"); } } catch (error) { + if ( + typeof error.message === "string" && + error.message.indexOf(ApiHasNotBeenInitializedCode) !== -1 + ) { + return { + kind: "not-initialized", + }; + } + return { kind: "error", message: error.message, @@ -178,144 +205,148 @@ class JsVisualizationBackend extends VisualizationBackendBase { } } - private ensureRuntimeIsInjected(): Promise | undefined { - if (this.apiInitialized) { - return; - } - - if (this.initializedPromise) { - return this.initializedPromise; - } else { - this.initializedPromise = this._ensureRuntimeIsInjected(); + private initializeApiSafe(): Promise { + if (!this.initializePromise) { + this.initializePromise = this.initializeApi().finally( + () => (this.initializePromise = undefined) + ); } + return this.initializePromise; } - private async _ensureRuntimeIsInjected(): Promise { - const body = getExpressionToDetectDataExtractorApiPresence(); + private async initializeApi(): Promise { + // prefer existing is true, so that manually registered (possibly newer) extractors are not overwritten. + const expression = `${getExpressionToInitializeDataExtractorApi()}.registerDefaultExtractors(true);`; - const reply = await this.debugSession.evaluate({ - expression: body, + await this.debugSession.evaluate({ + expression, frameId: undefined, context: this.getContext(), }); - const isApiInitialized = reply.result === "true"; - - if (!isApiInitialized) { - await this.initializeApi(); - } await this.initializeCustomScripts(); } - private async initializeApi(): Promise { - try { - // prefer existing is true, so that manually registered (possibly newer) extractors are not overwritten. - const expression = `${getExpressionToInitializeDataExtractorApi()}.registerDefaultExtractors(true);`; + private customScripts: undefined | CustomScripts; - await this.debugSession.evaluate({ - expression, - frameId: undefined, - context: this.getContext(), - }); - - return true; - } catch (error) { - return false; - } + private async initializeCustomScripts(): Promise { + this.dispose.untrack(this.customScripts); + this.customScripts?.dispose(); + this.customScripts = this.dispose.track(new CustomScripts(this.debugSession, this.config, () => this.onChangeEmitter.emit())); } +} + +class CustomScripts { + public readonly dispose = Disposable.fn(); @observable private filesVersionId: number = 0; private readonly fileChangeDebouncedScheduler = new DebouncedScheduler(100); - private async initializeCustomScripts(): Promise { - await this.debugSession.evaluate({ - expression: `global["${injectedScriptsKey}"] = {};`, - frameId: undefined, - context: "repl", - }); + constructor(private readonly debugSession: DebugSessionProxy, private readonly config: Config, private readonly changeHandler: () => void) { + this.init(); + } + private init(): void { const watchers = new Map(); this.dispose.track({ dispose: () => { for (const w of watchers.values()) { - w.watcher.dispose(); + w.dispose(); } }, }); - reaction( - () => ({ - customScriptPaths: [...this.config.customScriptPaths], - version: this.filesVersionId, - }), - async ({ customScriptPaths }) => { - const promises = new Array>(); + this.dispose.track({ + dispose: reaction( + () => ({ + customScriptPaths: [...this.config.customScriptPaths], + version: this.filesVersionId, + }), + async ({ customScriptPaths }) => { + const promises = new Array< + Promise<[Result, WatchedFile]> + >(); - const activePaths = new Set(); - for (const fsPath of customScriptPaths) { - activePaths.add(fsPath); - let entry = watchers.get(fsPath); - if (!entry) { - entry = new WatchedFile(fsPath, this.debugSession); - watchers.set(fsPath, entry); - entry.watcher.onDidChange(() => { - this.fileChangeDebouncedScheduler.run(() => { - this.filesVersionId++; - }); - }); + const activePaths = new Set(); + for (const fsPath of customScriptPaths) { + activePaths.add(fsPath); + let entry = watchers.get(fsPath); + if (!entry) { + entry = new WatchedFile( + fsPath, + this.debugSession, + () => { + this.fileChangeDebouncedScheduler.run( + () => { + this.filesVersionId++; + } + ); + } + ); + watchers.set(fsPath, entry); + } + + promises.push(entry.update().then((e) => [e, entry!])); } - promises.push(entry.update().then(e => [e, entry!])); - } - - for (const [fsPath, entry] of watchers) { - if (!activePaths.has(fsPath)) { - promises.push(entry.remove().then(e => [e, entry])); - entry.watcher.dispose(); - watchers.delete(fsPath); + for (const [fsPath, entry] of watchers) { + if (!activePaths.has(fsPath)) { + promises.push( + entry.remove().then((e) => [e, entry]) + ); + entry.dispose(); + watchers.delete(fsPath); + } } - } - const results = await Promise.all(promises); - const errors = results.filter(([r]) => r.error); - for (const [e, watcher] of errors) { - window.showErrorMessage( - `Error while evaluating "${watcher.fsPath}": ${ - e.error!.message - }` - ); - } + const results = await Promise.all(promises); + const errors = results.filter(([r]) => r.error); + for (const [e, watcher] of errors) { + window.showErrorMessage( + `Error while evaluating "${watcher.fsPath}": ${ + e.error!.message + }` + ); + } - if (promises.length > 0) { - this.onChangeEmitter.emit(); + if (promises.length > 0) { + this.changeHandler(); + } + }, + { + fireImmediately: true, } - }, - { - fireImmediately: true, - } - ); + ), + }); } } -const injectedScriptsKey = `@hediet/debug-visualizer/injectedScripts`; - interface Result { error?: { message: string }; } class WatchedFile { public static nextId = 0; - public readonly watcher = workspace.createFileSystemWatcher(this.fsPath); + private readonly watcher = watch( + this.fsPath, + { encoding: "utf-8" }, + this.changeHandler + ); public readonly id = WatchedFile.nextId++; public lastContent: string | undefined = undefined; constructor( public readonly fsPath: string, - private readonly debugSession: DebugSessionProxy + private readonly debugSession: DebugSessionProxy, + private readonly changeHandler: () => void ) {} + public dispose(): void { + this.watcher.close(); + } + public async update(): Promise { if (!existsSync(this.fsPath)) { return { @@ -337,7 +368,9 @@ class WatchedFile { load => { var module = {}; load(module); - global["${injectedScriptsKey}"]["${this.id}"] = module.exports; + ${getExpressionForDataExtractorApi()}.registerDataExtractorsSource("${ + this.id + }", module.exports); } ) ( @@ -357,7 +390,9 @@ class WatchedFile { } public async remove(): Promise { - const expression = `(() => { delete global["${injectedScriptsKey}"]["${this.id}"]; })()`; + const expression = `${getExpressionForDataExtractorApi()}.unregisterDataExtractorsSource("${ + this.id + }")`; try { await this.debugSession.evaluate({ expression, diff --git a/webview/package.json b/webview/package.json index b4b3903..438d8c2 100644 --- a/webview/package.json +++ b/webview/package.json @@ -14,13 +14,13 @@ "classnames": "^2.2.6", "mobx": "^5.15.4", "mobx-react": "^6.1.8", - "monaco-editor": "^0.20.0", + "monaco-editor": "^0.25.2", "popper.js": "^1.16.1", "react": "^16.12.0", "react-dom": "^16.12.0", "react-measure": "^2.3.0", "@hediet/debug-visualizer-data-extraction": "*", - "@hediet/visualization-bundle": "^0.2.1", + "@hediet/visualization-bundle": "^0.2.3", "@hediet/visualization-core": "^0.2.0", "@knuddels/mobx-logger": "^1.1.1", "less-loader": "^6.2.0" @@ -37,7 +37,7 @@ "file-loader": "^5.1.0", "fork-ts-checker-webpack-plugin": "^4.0.4", "html-webpack-plugin": "^3.2.0", - "monaco-editor-webpack-plugin": "^1.9.0", + "monaco-editor-webpack-plugin": "^4.0.0", "sass": "^1.25.0", "raw-loader": "^4.0.0", "sass-loader": "^8.0.2", diff --git a/webview/src/index.tsx b/webview/src/index.tsx index f7a90c5..322011a 100644 --- a/webview/src/index.tsx +++ b/webview/src/index.tsx @@ -2,6 +2,9 @@ import * as React from "react"; import * as ReactDOM from "react-dom"; import "./style.scss"; import { App } from "./components/App"; +import * as monaco from "monaco-editor"; + +(globalThis as any).monaco = monaco; const elem = document.createElement("div"); elem.className = "react-root"; diff --git a/yarn.lock b/yarn.lock index 622c2b6..71c631e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -487,10 +487,10 @@ stacktracey "^1.2.112" ws "^7.0.0" -"@hediet/monaco-editor-react@^0.1.1": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@hediet/monaco-editor-react/-/monaco-editor-react-0.1.1.tgz#041f805aeb188403c7876f0f364d2a429e706031" - integrity sha512-0DZZfLcFNuc3Wrn+2eztnRloCBe6GV8zIrGT3LPdZlWYEigJT+Qh7ANvRHF0tCS5YPAFGQNZJcfzKbBfqRGo4w== +"@hediet/monaco-editor-react@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@hediet/monaco-editor-react/-/monaco-editor-react-0.2.0.tgz#4a98102baf1942f210fc19fd406b6c82da3bf583" + integrity sha512-EA71+iv9sV25+YgBQDJ4Ou17sOJCXQat7r5AOD5STZHcUQdSuQufI2t9ue+wJIm4048P9sQ/ruW3pYmwxF9Amw== "@hediet/node-reload@^0.7.3": version "0.7.3" @@ -550,22 +550,22 @@ "@hediet/std" "^0.6.0" io-ts "^1.8.6" -"@hediet/visualization-bundle@^0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@hediet/visualization-bundle/-/visualization-bundle-0.2.1.tgz#397a6bff0ea12018a7a1cb1c182ef8c5de87328f" - integrity sha512-Qfyn8WbWGwuE5J1z8U7l8E5VKuG3DpS2JJOtKhlW1QW/rhqBWOLd5avifxqjWxiMIfqh3NTShp9gLHpmI2Bysw== +"@hediet/visualization-bundle@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@hediet/visualization-bundle/-/visualization-bundle-0.2.3.tgz#8323706dcaaafc973fdc57da68e6527265e5bfcc" + integrity sha512-RpzAST/K5O9FaEaCshHEY7VeGRVlwoTLLcWPkgxzeuZlRr5CNTw8d7Lznh+RaLD1rbz/UEezq2pI3qFYnq/z4w== dependencies: "@finos/perspective-viewer" "^0.5.2" "@finos/perspective-viewer-d3fc" "^0.5.2" "@finos/perspective-viewer-datagrid" "^0.5.2" - "@hediet/monaco-editor-react" "^0.1.1" + "@hediet/monaco-editor-react" "^0.2.0" "@hediet/semantic-json" "^0.3.15" "@hediet/std" "^0.6.0" classnames "^2.2.6" line-column "^1.0.2" mobx "^5.15.4" mobx-react "^6.1.8" - monaco-editor "^0.20.0" + monaco-editor "^0.25.2" plotly.js "^1.54.7" react "^16.12.0" react-dom "^16.12.0" @@ -6861,17 +6861,17 @@ moment@^2.19.1: resolved "https://registry.yarnpkg.com/moment/-/moment-2.27.0.tgz#8bff4e3e26a236220dfe3e36de756b6ebaa0105d" integrity sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ== -monaco-editor-webpack-plugin@^1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-1.9.0.tgz#5b547281b9f404057dc5d8c5722390df9ac90be6" - integrity sha512-tOiiToc94E1sb50BgZ8q8WK/bxus77SRrwCqIpAB5er3cpX78SULbEBY4YPOB8kDolOzKRt30WIHG/D6gz69Ww== +monaco-editor-webpack-plugin@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-4.0.0.tgz#95be3f48f4220999b909266a9997727f0deab947" + integrity sha512-4BT9XDRQXraMQjxEUjR+uuubRe3RIPkvVoGw8zwWG++s7wq6TAiXaSOMdkdS9TrjCREgSnygCOlVzY6MS8RPuA== dependencies: - loader-utils "^1.2.3" + loader-utils "^2.0.0" -monaco-editor@^0.20.0: - version "0.20.0" - resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.20.0.tgz#5d5009343a550124426cb4d965a4d27a348b4dea" - integrity sha512-hkvf4EtPJRMQlPC3UbMoRs0vTAFAYdzFQ+gpMb8A+9znae1c43q8Mab9iVsgTcg/4PNiLGGn3SlDIa8uvK1FIQ== +monaco-editor@^0.25.2: + version "0.25.2" + resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.25.2.tgz#119e2b15bbd968a1a99c03cac9c329316d7c37e9" + integrity sha512-5iylzSJevCnzJn9UVsW8yOZ3yHjmAs4TfvH3zsbftKiFKmHG0xirGN6DK9Kk04VSWxYCZZAIafYJoNJJMAU1KA== monotone-convex-hull-2d@^1.0.1: version "1.0.1" @@ -8063,10 +8063,10 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= -prettier@^1.19.1: - version "1.19.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" - integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== +prettier@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.1.tgz#76903c3f8c4449bc9ac597acefa24dc5ad4cbea6" + integrity sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA== pretty-error@^2.0.2: version "2.1.1"