* Updates Dependencies

* Uses webpack to bundle runtime library into a single file
* Inject the entire file into the Debugee
* Register runtime helpers (hediet_dbgVis)
This commit is contained in:
Henning Dieterichs
2020-02-12 14:20:45 +01:00
parent 1a2d5b9af0
commit d4c6e942d0
29 changed files with 809 additions and 669 deletions

View File

@@ -3,6 +3,8 @@
[![](https://img.shields.io/twitter/follow/hediet_dev.svg?style=social)](https://twitter.com/intent/follow?screen_name=hediet_dev)
A library that helps implementing data extractors for the Debug Visualizer VS Code extension.
It will automatically be injected by the extension when the debugger attaches.
Compatible with NodeJS and browsers.
# Installation
@@ -14,15 +16,10 @@ yarn add @hediet/debug-visualizer-data-extraction
# Usage
## `createGraph` / `createGraphFromPointers` Helper
## `createGraphFromPointers` Helper
```ts
import {
registerDefaultDataExtractors,
createGraphFromPointers,
} from "@hediet/debug-visualizer-data-extraction";
registerDefaultDataExtractors();
import { createGraphFromPointers } from "@hediet/debug-visualizer-data-extraction";
setTimeout(() => {
new Main().run();
@@ -85,14 +82,7 @@ class DoublyLinkedList {
## Registering Custom Data Extractors
```ts
import {
registerDefaultDataExtractors,
getDataExtractorApi,
} from "@hediet/debug-visualizer-data-extraction";
// The default data extractors are usually also registered by the VS Code extension.
// Registered them manually ensures that they are up to date.
registerDefaultDataExtractors();
import { getDataExtractorApi } from "@hediet/debug-visualizer-data-extraction";
getDataExtractorApi().registerExtractor({
id: "my-foo-extractor",

View File

@@ -22,11 +22,18 @@
"registry": "https://registry.npmjs.org/"
},
"scripts": {
"dev": "tsc --watch",
"build": "tsc"
"dev": "webpack --watch --mode development",
"build": "webpack --mode production"
},
"dependencies": {},
"devDependencies": {
"typescript": "^"
"typescript": "^3.7.5",
"@types/express": "^4.17.2",
"@types/serve-static": "^1.13.3",
"@types/node": "^13.7.1",
"tslint": "^6.0.0",
"webpack": "^4.41.6",
"webpack-cli": "^3.3.11",
"ts-loader": "^6.2.1"
}
}

View File

@@ -1,60 +0,0 @@
import { ExtractedData } from "../DataExtractionResult";
export interface DataExtractor<T extends ExtractedData> {
/**
* Must be unique among all data extractors.
*/
id: string;
getExtractions(
data: unknown,
extractionCollector: ExtractionCollector<T>,
context: DataExtractorContext
): void;
}
export interface ExtractionCollector<T extends ExtractedData> {
/**
* Suggests a possible extraction.
*/
addExtraction(extraction: DataExtraction<T>): void;
}
export interface DataExtractorContext {
/**
* Evaluates an expression in the context of the active stack frame.
*/
evalFn: <TEval>(expression: string) => TEval;
}
export interface DataExtraction<TData extends ExtractedData> {
/**
* Higher priorities are preferred.
*/
priority: number;
/**
* A unique id identifying this extraction among all extractions.
* Required to express extraction preferences.
*/
id: string;
/**
* A user friendly name of this extraction.
*/
name: string;
extractData(): TData;
}
export function getExtractions<T extends ExtractedData>(
extractor: DataExtractor<T>,
data: unknown,
context: DataExtractorContext
): DataExtraction<T>[] {
const extractions = new Array<DataExtraction<T>>();
const collector: ExtractionCollector<T> = {
addExtraction(extraction) {
extractions.push(extraction);
},
};
extractor.getExtractions(data, collector, context);
return extractions;
}

View File

@@ -1,193 +0,0 @@
import {
ExtractedData,
DataExtractionResult,
DataExtractorInfo,
} from "../DataExtractionResult";
import {
DataExtractor,
DataExtraction,
ExtractionCollector,
} from "./DataExtractor";
export interface DataExtractorApi {
/**
* Registers a single extractor.
* @preferExisting if `true`, an existing extractor is not overwritten.
*/
registerExtractor<TExtractedData extends ExtractedData>(
extractor: DataExtractor<TExtractedData>,
preferExisting?: boolean
): void;
/**
* Registers multiple extractors.
* @preferExisting if `true`, an existing extractor is not overwritten.
*/
registerExtractors(
extractors: DataExtractor<ExtractedData>[],
preferExisting?: boolean
): void;
/**
* Extracts data from `value`.
*/
getData(
value: unknown,
evalFn: <T>(expression: string) => T,
preferredDataExtractorId: string | undefined
): JSONString<DataResult>;
}
export type DataResult =
| {
kind: "Data";
extractionResult: DataExtractionResult;
}
| { kind: "NoExtractors" }
| { kind: "Error"; message: string };
export interface JSONString<T> extends String {
__brand: { json: T };
}
declare const window: any;
/**
* @internal
*/
export function selfContainedInitDataExtractorApi(): boolean {
const globalObj =
typeof window === "object" ? (window as any) : (global as any);
const key = "@hediet/data-extractor/v1";
const prefix = key + "::";
let api: DataExtractorApi | undefined = globalObj[key];
if (api) {
return false;
}
function toJson<TData>(data: TData): JSONString<TData> {
return JSON.stringify(data) as any;
}
function getExtractors(): DataExtractor<ExtractedData>[] {
return Object.entries(globalObj)
.filter(([key, value]) => key.startsWith(prefix))
.map(([key, value]) => value as DataExtractor<ExtractedData>);
}
globalObj["$asData"] = (x: unknown) => x;
globalObj[key] = api = {
registerExtractor(extractor, preferExisting) {
const key = prefix + extractor.id;
if (preferExisting && key in globalObj) {
// don't overwrite existing data extractor
return;
}
globalObj[key] = extractor;
},
registerExtractors(
extractors: DataExtractor<ExtractedData>[],
preferExisting
) {
for (const e of extractors) {
this.registerExtractor(e, preferExisting);
}
},
getData(value, evalFn, preferredDataExtractorId) {
const extractions = new Array<DataExtraction<ExtractedData>>();
const extractionCollector: ExtractionCollector<ExtractedData> = {
addExtraction(extraction) {
extractions.push(extraction);
},
};
for (const e of getExtractors()) {
e.getExtractions(value, extractionCollector, { evalFn });
}
extractions.sort((a, b) => b.priority - a.priority);
let usedExtraction = extractions[0];
if (!usedExtraction) {
return toJson({ kind: "NoExtractors" } as DataResult);
}
if (preferredDataExtractorId) {
const preferred = extractions.find(
e => e.id === preferredDataExtractorId
);
if (preferred) {
usedExtraction = preferred;
}
}
function mapExtractor(
e: DataExtraction<ExtractedData>
): DataExtractorInfo {
return {
id: e.id as any,
name: e.name,
priority: e.priority,
};
}
const data = usedExtraction.extractData();
return toJson({
kind: "Data",
extractionResult: {
data,
usedExtractor: mapExtractor(usedExtraction),
availableExtractors: extractions.map(mapExtractor),
},
} as DataResult);
},
};
api.registerExtractor({
id: "to-string",
getExtractions: (data, collector) => {
collector.addExtraction({
id: "to-string",
name: "To String",
priority: 100,
extractData() {
return {
kind: {
text: true,
},
text: "" + data,
};
},
});
},
});
return true;
}
/**
* This code is used to detect if the API has not been initialized yet.
* @internal
*/
export const ApiHasNotBeenInitializedCode = "EgH0cybXij1jYUozyakO" as const;
/**
* @internal
*/
export function selfContainedGetInitializedDataExtractorApi(): DataExtractorApi {
const obj = typeof window === "object" ? (window as any) : (global as any);
const key = "@hediet/data-extractor/v1";
let api: DataExtractorApi | undefined = obj[key];
if (!api) {
const code: typeof ApiHasNotBeenInitializedCode =
"EgH0cybXij1jYUozyakO";
throw new Error(
`Data Extractor API has not been initialized. Code: ${code}`
);
}
return api;
}
/**
* Get the data extractor API.
*/
export function getDataExtractorApi(): DataExtractorApi {
selfContainedInitDataExtractorApi();
return selfContainedGetInitializedDataExtractorApi();
}

View File

@@ -1,5 +0,0 @@
export { TypeScriptAstDataExtractor } from "./TypeScriptDataExtractors";
export { AsIsDataExtractor } from "./AsIsDataExtractor";
export { GetDebugVisualizationDataExtractor } from "./GetDebugVisualizationDataExtractor";
export { registerDefaultDataExtractors } from "./registerDefaultDataExtractors";

View File

@@ -0,0 +1,89 @@
import {
ExtractedData,
DataExtractionResult,
} from "../../DataExtractionResult";
export interface DataExtractorApi {
/**
* Registers a single extractor.
*/
registerExtractor<TExtractedData extends ExtractedData>(
extractor: DataExtractor<TExtractedData>
): void;
/**
* Registers multiple extractors.
*/
registerExtractors(extractors: DataExtractor<ExtractedData>[]): void;
/**
* Extracts data from `value`.
*/
getData(
value: unknown,
evalFn: <T>(expression: string) => T,
preferredDataExtractorId: string | undefined
): JSONString<DataResult>;
/**
* Registers all default (built-in) extractors.
* @preferExisting if `true`, existing extractors with the same id are not overwritten.
*/
registerDefaultExtractors(preferExisting?: boolean): void;
}
export type DataResult =
| {
kind: "Data";
extractionResult: DataExtractionResult;
}
| { kind: "NoExtractors" }
| { kind: "Error"; message: string };
export interface JSONString<T> extends String {
__brand: { json: T };
}
export interface DataExtractor<T extends ExtractedData> {
/**
* Must be unique among all data extractors.
*/
id: string;
getExtractions(
data: unknown,
extractionCollector: ExtractionCollector<T>,
context: DataExtractorContext
): void;
}
export interface ExtractionCollector<T extends ExtractedData> {
/**
* Suggests a possible extraction.
*/
addExtraction(extraction: DataExtraction<T>): void;
}
export interface DataExtractorContext {
/**
* Evaluates an expression in the context of the active stack frame.
*/
evalFn: <TEval>(expression: string) => TEval;
}
export interface DataExtraction<TData extends ExtractedData> {
/**
* Higher priorities are preferred.
*/
priority: number;
/**
* A unique id identifying this extraction among all extractions.
* Required to express extraction preferences.
*/
id: string;
/**
* A user friendly name of this extraction.
*/
name: string;
extractData(): TData;
}

View File

@@ -0,0 +1,90 @@
import {
DataExtractorApi,
DataResult,
JSONString,
DataExtractor,
DataExtraction,
ExtractionCollector,
} from "./DataExtractorApi";
import { ExtractedData, DataExtractorInfo } from "../../DataExtractionResult";
import { registerDefaultDataExtractors } from "./default-extractors";
export class DataExtractorApiImpl implements DataExtractorApi {
private readonly extractors = new Map<
string,
DataExtractor<ExtractedData>
>();
private toJson<TData>(data: TData): JSONString<TData> {
return JSON.stringify(data) as any;
}
public registerExtractor<TExtractedData extends ExtractedData>(
extractor: DataExtractor<TExtractedData>
): void {
this.extractors.set(extractor.id, extractor);
}
public registerExtractors(
extractors: DataExtractor<ExtractedData>[]
): void {
for (const e of extractors) {
this.registerExtractor(e);
}
}
public getData(
value: unknown,
evalFn: <T>(expression: string) => T,
preferredDataExtractorId: string | undefined
): JSONString<DataResult> {
const extractions = new Array<DataExtraction<ExtractedData>>();
const extractionCollector: ExtractionCollector<ExtractedData> = {
addExtraction(extraction) {
extractions.push(extraction);
},
};
for (const e of this.extractors.values()) {
e.getExtractions(value, extractionCollector, { evalFn });
}
extractions.sort((a, b) => b.priority - a.priority);
let usedExtraction = extractions[0];
if (!usedExtraction) {
return this.toJson({ kind: "NoExtractors" } as DataResult);
}
if (preferredDataExtractorId) {
const preferred = extractions.find(
e => e.id === preferredDataExtractorId
);
if (preferred) {
usedExtraction = preferred;
}
}
function mapExtractor(
e: DataExtraction<ExtractedData>
): DataExtractorInfo {
return {
id: e.id as any,
name: e.name,
priority: e.priority,
};
}
const data = usedExtraction.extractData();
return this.toJson({
kind: "Data",
extractionResult: {
data,
usedExtractor: mapExtractor(usedExtraction),
availableExtractors: extractions.map(mapExtractor),
},
} as DataResult);
}
public registerDefaultExtractors(preferExisting: boolean = false): void {
registerDefaultDataExtractors(this);
}
}

View File

@@ -1,5 +1,9 @@
import { ExtractedData } from "../../DataExtractionResult";
import { DataExtractor, ExtractionCollector, DataExtractorContext } from "..";
import { ExtractedData } from "../../../DataExtractionResult";
import {
DataExtractor,
ExtractionCollector,
DataExtractorContext,
} from "../..";
export class AsIsDataExtractor implements DataExtractor<ExtractedData> {
readonly id = "as-is";

View File

@@ -1,9 +1,9 @@
import { ExtractedData } from "../../DataExtractionResult";
import { ExtractedData } from "../../../DataExtractionResult";
import {
ExtractionCollector,
DataExtractor,
ExtractionCollector,
DataExtractorContext,
} from "../DataExtractor";
} from "../DataExtractorApi";
export class GetDebugVisualizationDataExtractor
implements DataExtractor<ExtractedData> {

View File

@@ -0,0 +1,30 @@
import { ExtractedData } from "../../../DataExtractionResult";
import {
DataExtractor,
ExtractionCollector,
DataExtractorContext,
} from "../DataExtractorApi";
export class ToStringDataExtractor implements DataExtractor<ExtractedData> {
readonly id = "to-string";
getExtractions(
data: unknown,
collector: ExtractionCollector<ExtractedData>,
context: DataExtractorContext
): void {
collector.addExtraction({
id: "to-string",
name: "To String",
priority: 100,
extractData() {
return {
kind: {
text: true,
},
text: "" + data,
};
},
});
}
}

View File

@@ -1,10 +1,10 @@
import * as ts from "typescript";
import * as ts from "typescript"; // Only compile-time import!
import {
DataExtractor,
ExtractionCollector,
DataExtractorContext,
} from "../DataExtractor";
import { CommonDataTypes } from "../../CommonDataTypes";
} from "../DataExtractorApi";
import { CommonDataTypes } from "../../../CommonDataTypes";
// This class is self contained and can be injected into both nodejs and browser environments.
export class TypeScriptAstDataExtractor

View File

@@ -0,0 +1 @@
export { registerDefaultDataExtractors } from "./registerDefaultDataExtractors";

View File

@@ -1,18 +1,19 @@
import { getDataExtractorApi } from "../DataExtractorApi";
import { DataExtractorApi } from "../DataExtractorApi";
import { TypeScriptAstDataExtractor } from "./TypeScriptDataExtractors";
import { AsIsDataExtractor } from "./AsIsDataExtractor";
import { GetDebugVisualizationDataExtractor } from "./GetDebugVisualizationDataExtractor";
import { ToStringDataExtractor } from "./ToStringExtractor";
/**
* The default data extractors should be registered by vs code anyways.
* This however ensures that the most up to date extractors are registered.
* The default data extractors should be registered by VS Code automatically.
* Registering them manually ensures that they are up to date.
*/
export function registerDefaultDataExtractors() {
const api = getDataExtractorApi();
export function registerDefaultDataExtractors(api: DataExtractorApi) {
for (const item of [
new TypeScriptAstDataExtractor(),
new AsIsDataExtractor(),
new GetDebugVisualizationDataExtractor(),
new ToStringDataExtractor(),
]) {
api.registerExtractor(item);
}

View File

@@ -0,0 +1,2 @@
export * from "./DataExtractorApi";
export * from "./injection";

View File

@@ -0,0 +1,73 @@
import * as fs from "fs";
import { DataExtractorApi } from "./DataExtractorApi";
import { DataExtractorApiImpl } from "./DataExtractorApiImpl";
import * as helpers from "../helpers";
import * as globalHelpers from "../global-helpers";
/**
* Returns standalone JS code representing an expression that initializes the data extraction API.
* This expression returns nothing.
* This function is called in the VS Code extension, the expression is evaluated in the debugee.
*/
export function getExpressionToInitializeDataExtractorApi(): string {
const _fs = require("fs") as typeof fs;
const moduleSrc = _fs.readFileSync(__filename, { encoding: "utf8" });
return `((function () {
let module = {};
${moduleSrc}
module.exports.installHelpers();
return module.exports.getDataExtractorApi();
})())`;
}
/**
* Returns standalone JS code representing an expression returns the data extraction API.
* This expression returns an object of type `DataExtractorApi`.
* If the API must be initialized with `getExpressionToInitializeDataExtractorApi()`,
* it throws an exception containing the text of `ApiHasNotBeenInitializedCode`.
* This function is called in the VS Code extension, the expression is evaluated in the debugee.
*/
export function getExpressionForDataExtractorApi(): string {
return `((${selfContainedGetInitializedDataExtractorApi.toString()})())`;
}
const apiKey = "@hediet/data-extractor-api/v2";
export function getDataExtractorApi(): DataExtractorApi {
const globalObj =
typeof window === "object" ? (window as any) : (global as any);
if (!globalObj[apiKey]) {
globalObj[apiKey] = new DataExtractorApiImpl();
}
return globalObj[apiKey];
}
/**
* This code is used to detect if the API has not been initialized yet.
* @internal
*/
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) {
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);
globalObj["hediet_dbgVis"] = { ...helpers, ...globalHelpers };
}

View File

@@ -0,0 +1 @@
export { getDataExtractorApi as getApi } from "./api/injection";

View File

@@ -2,7 +2,7 @@ import {
CommonDataTypes,
EdgeGraphData,
NodeGraphData,
} from "../CommonDataTypes";
} from "../../CommonDataTypes";
/**
* Given a list of roots, it creates a graph by following their edges recursively.
@@ -66,46 +66,3 @@ export function createGraph<T>(
}
return r;
}
/**
* Given a labeled list of roots, it creates a graph by following their edges recursively.
* Tracks cycles.
*/
export function createGraphFromPointers<T>(
roots: Record<string, T | undefined | null>,
infoSelector: (
item: T
) => {
id?: string | number;
edges: ({ to: T } & Omit<EdgeGraphData, "from" | "to">)[];
} & Omit<NodeGraphData, "id">
): CommonDataTypes.Graph {
const marker = {};
interface Pointer {
marker: {};
name: string;
value: T | null | undefined;
}
const items = Object.entries(roots).map<Pointer>(([name, value]) => ({
marker,
name,
value,
}));
return createGraph<T | Pointer>(items, item => {
if ("marker" in item && item["marker"] === marker) {
return {
id: "label____" + item.name,
color: "orange",
label: item.name,
edges: [{ to: item.value!, color: "orange", label: "" }].filter(
t => !!t.to
),
};
} else {
return infoSelector(item as T);
}
});
}

View File

@@ -0,0 +1,45 @@
import { EdgeGraphData, NodeGraphData, CommonDataTypes } from "../..";
import { createGraph } from ".";
/**
* Given a labeled list of roots, it creates a graph by following their edges recursively.
* Tracks cycles.
*/
export function createGraphFromPointers<T>(
roots: Record<string, T | undefined | null>,
infoSelector: (
item: T
) => {
id?: string | number;
edges: ({ to: T } & Omit<EdgeGraphData, "from" | "to">)[];
} & Omit<NodeGraphData, "id">
): CommonDataTypes.Graph {
const marker = {};
interface Pointer {
marker: {};
name: string;
value: T | null | undefined;
}
const items = Object.entries(roots).map<Pointer>(([name, value]) => ({
marker,
name,
value,
}));
return createGraph<T | Pointer>(items, item => {
if ("marker" in item && item["marker"] === marker) {
return {
id: "label____" + item.name,
color: "orange",
label: item.name,
edges: [{ to: item.value!, color: "orange", label: "" }].filter(
t => !!t.to
),
};
} else {
return infoSelector(item as T);
}
});
}

View File

@@ -0,0 +1,2 @@
export * from "./createGraph";
export * from "./createGraphFromPointers";

View File

@@ -1,4 +1,2 @@
export * from "./Extractors";
export * from "./DataExtractor";
export * from "./DataExtractorApi";
export * from "./createGraph";
export * from "./api";
export * from "./helpers";

View File

@@ -0,0 +1,2 @@
require("ts-node").register();
module.exports = require("./webpack.config.ts");

View File

@@ -0,0 +1,43 @@
import * as webpack from "webpack";
import path = require("path");
import { CleanWebpackPlugin } from "clean-webpack-plugin";
const r = (file: string) => path.resolve(__dirname, file);
module.exports = {
target: "node",
entry: r("./src/index"),
output: {
path: r("./dist"),
filename: "index.js",
libraryTarget: "commonjs2",
devtoolModuleFilenameTemplate: "../[resource-path]",
},
devtool: "source-map",
resolve: {
extensions: [".ts", ".js"],
},
module: {
rules: [
{
test: /\.ts$/,
exclude: /node_modules/,
use: [
{
loader: "ts-loader",
},
],
},
],
},
node: {
__dirname: false,
__filename: false,
},
plugins: [
new CleanWebpackPlugin({
protectWebpackAssets: true,
cleanAfterEveryBuildPatterns: ["!**/*.d.ts", "!**/*.d.ts.map"],
}),
],
} as webpack.Configuration;

View File

@@ -1,10 +1,7 @@
import {
registerDefaultDataExtractors,
getDataExtractorApi,
} from "@hediet/debug-visualizer-data-extraction";
import { getDataExtractorApi } from "@hediet/debug-visualizer-data-extraction";
// Registers all existing extractors.
registerDefaultDataExtractors();
getDataExtractorApi().registerDefaultExtractors();
getDataExtractorApi().registerExtractor({
id: "my-extractor",

View File

@@ -1,9 +1,9 @@
import {
registerDefaultDataExtractors,
getDataExtractorApi,
createGraphFromPointers,
} from "@hediet/debug-visualizer-data-extraction";
registerDefaultDataExtractors();
getDataExtractorApi().registerDefaultExtractors();
setTimeout(() => {
new Main().run();

View File

@@ -1,8 +1,9 @@
import * as ts from "typescript";
import { registerDefaultDataExtractors } from "@hediet/debug-visualizer-data-extraction";
import { getDataExtractorApi } from "@hediet/debug-visualizer-data-extraction";
import { MockLanguageServiceHost } from "./MockLanguageServiceHost";
registerDefaultDataExtractors();
// Registers all existing extractors.
getDataExtractorApi().registerDefaultExtractors();
setTimeout(() => {
new Main().run();

View File

@@ -64,13 +64,14 @@
"crypto-random-string": "^3.1.0"
},
"devDependencies": {
"@types/express": "^4.17.0",
"@types/serve-static": "^1.13.2",
"@types/node": "^12.6.8",
"@types/vscode": "1.35.0",
"tslint": "^5.18.0",
"typescript": "^3.5.3",
"webpack": "^4.38.0",
"webpack-cli": "^3.3.6"
"@types/express": "^4.17.2",
"@types/serve-static": "^1.13.3",
"@types/node": "^13.7.1",
"@types/vscode": "1.42.0",
"tslint": "^6.0.0",
"typescript": "^3.7.5",
"webpack": "^4.41.6",
"webpack-cli": "^3.3.11",
"ts-loader": "^6.2.1"
}
}

View File

@@ -6,14 +6,11 @@ import {
import { observable, autorun, action } from "mobx";
import { Disposable } from "@hediet/std/disposable";
import {
selfContainedGetInitializedDataExtractorApi,
getExpressionForDataExtractorApi,
DataResult,
DataExtractorId,
ApiHasNotBeenInitializedCode,
selfContainedInitDataExtractorApi,
TypeScriptAstDataExtractor,
AsIsDataExtractor,
GetDebugVisualizationDataExtractor,
getExpressionToInitializeDataExtractorApi,
} from "@hediet/debug-visualizer-data-extraction";
import {
DataExtractionState,
@@ -100,14 +97,30 @@ export class JsDebuggerSourceImplementation implements JsDataSource {
try {
w._state = { kind: "loading" };
const fnSrc = selfContainedGetInitializedDataExtractorApi.toString();
const preferredExtractor = w.preferredDataExtractor
? `"${w.preferredDataExtractor}"`
: "undefined";
const body = `(${fnSrc})().getData(${w.expression}, expr => eval(expr), ${preferredExtractor})`;
const expression = `(() => { try { return ${body}; } catch (e) { return JSON.stringify({ kind: "Error", message: e.message, stack: e.stack }); } })()`;
const body = `${getExpressionForDataExtractorApi()}.getData(
${w.expression},
expr => eval(expr),
${preferredExtractor}
)`;
const expression = `
(() => {
try {
return ${body};
} catch (e) {
return JSON.stringify({
kind: "Error",
message: e.message,
stack: e.stack
});
}
})()
`;
const reply = await session.evaluate({
expression,
@@ -147,24 +160,14 @@ export class JsDebuggerSourceImplementation implements JsDataSource {
frameId: number | undefined
): Promise<boolean> {
try {
let expression = `(${selfContainedInitDataExtractorApi.toString()})();`;
// prefer existing is true, so that manually registered (possibly newer) extractors are not overwritten.
const expression = `${getExpressionToInitializeDataExtractorApi()}.registerDefaultExtractors(true);`;
const es = [
TypeScriptAstDataExtractor,
AsIsDataExtractor,
GetDebugVisualizationDataExtractor,
].map(e => `new (${e.toString()})()`);
expression += `(${selfContainedGetInitializedDataExtractorApi.toString()})()`;
// prefer existing is true, so that manually registered extractors are not overwritten.
expression += `.registerExtractors([${es.join(",")}], true)`;
const reply = await session.evaluate({
await session.evaluate({
expression,
frameId,
});
if (reply.result === "true") {
// register extractors
}
return true;
} catch (error) {
return false;

View File

@@ -28,26 +28,26 @@
"vis-data": "^6.3.5"
},
"devDependencies": {
"@types/classnames": "^2.2.8",
"@types/react": "^16.8.21",
"@types/react-dom": "^16.8.4",
"@types/classnames": "^2.2.9",
"@types/react": "^16.9.19",
"@types/react-dom": "^16.9.5",
"@types/line-column": "^1.0.0",
"clean-webpack-plugin": "^3.0.0",
"css-loader": "^3.0.0",
"file-loader": "^4.0.0",
"fork-ts-checker-webpack-plugin": "^1.4.2",
"css-loader": "^3.4.2",
"file-loader": "^5.0.2",
"fork-ts-checker-webpack-plugin": "^4.0.3",
"html-webpack-plugin": "^3.2.0",
"monaco-editor-webpack-plugin": "^1.7.0",
"node-sass": "^4.12.0",
"raw-loader": "^3.0.0",
"sass": "^1.20.1",
"sass-loader": "^7.1.0",
"style-loader": "^0.23.1",
"ts-loader": "^6.0.3",
"ts-node": "^8.3.0",
"typescript": "^3.5.2",
"webpack": "^4.35.0",
"webpack-cli": "^3.3.4",
"webpack-dev-server": "^3.7.2"
"monaco-editor-webpack-plugin": "^1.8.2",
"node-sass": "^4.13.1",
"raw-loader": "^4.0.0",
"sass": "^1.25.0",
"sass-loader": "^8.0.2",
"style-loader": "^1.1.3",
"ts-loader": "^6.2.1",
"ts-node": "^8.6.2",
"typescript": "^3.7.5",
"webpack": "^4.41.6",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.10.3"
}
}

611
yarn.lock

File diff suppressed because it is too large Load Diff