Skip to content

Commit cd88168

Browse files
committed
Fixes issues with global.
1 parent 683f1e1 commit cd88168

File tree

15 files changed

+278
-237
lines changed

15 files changed

+278
-237
lines changed

data-extraction/src/getGlobal.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export function getGlobal(): any {
2+
if (typeof globalThis === "object") {
3+
return globalThis;
4+
} else if (typeof global === "object") {
5+
return global;
6+
} else if (typeof window === "object") {
7+
return window;
8+
}
9+
10+
throw new Error("No global available");
11+
}

data-extraction/src/js/api/DataExtractorApi.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
VisualizationData,
33
DataExtractionResult,
44
} from "../../DataExtractionResult";
5+
import { LoadDataExtractorsFn } from "./LoadDataExtractorsFn";
56

67
export interface DataExtractorApi {
78
/**
@@ -31,6 +32,9 @@ export interface DataExtractorApi {
3132
* @preferExisting if `true`, existing extractors with the same id are not overwritten.
3233
*/
3334
registerDefaultExtractors(preferExisting?: boolean): void;
35+
36+
registerDataExtractorsSource(id: string, fn: LoadDataExtractorsFn): void;
37+
unregisterDataExtractorsSource(id: string): void;
3438
}
3539

3640
export type DataResult =
@@ -70,7 +74,7 @@ export interface DataExtractorContext {
7074
*/
7175
evalFn: <TEval>(expression: string) => TEval;
7276

73-
variablesInScope: Record<string, unknown>;
77+
variablesInScope: Record<string, () => unknown>;
7478
}
7579

7680
export interface DataExtraction {

data-extraction/src/js/api/DataExtractorApiImpl.ts

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,21 @@ import {
55
DataExtractor,
66
DataExtraction,
77
ExtractionCollector,
8+
DataExtractorContext,
89
} from "./DataExtractorApi";
910
import { DataExtractorInfo } from "../../DataExtractionResult";
1011
import { registerDefaultExtractors } from "./default-extractors";
12+
import { LoadDataExtractorsFn } from "./LoadDataExtractorsFn";
13+
import * as helpers from "../helpers";
1114

1215
/**
1316
* @internal
1417
*/
1518
export class DataExtractorApiImpl implements DataExtractorApi {
16-
public static lastEvalFn: (<T>(expression: string) => T) | undefined;
17-
public static lastVariablesInScope:
18-
| Record<string, unknown>
19-
| undefined = undefined;
19+
public static lastContext: DataExtractorContext | undefined = undefined;
2020

2121
private readonly extractors = new Map<string, DataExtractor>();
22+
private readonly extractorSources = new Map<string, LoadDataExtractorsFn>();
2223

2324
private toJson<TData>(data: TData): JSONString<TData> {
2425
return JSON.stringify(data) as any;
@@ -38,7 +39,7 @@ export class DataExtractorApiImpl implements DataExtractorApi {
3839
valueFn: () => unknown,
3940
evalFn: <T>(expression: string) => T,
4041
preferredDataExtractorId: string | undefined,
41-
variablesInScope: Record<string, unknown>
42+
variablesInScope: Record<string, () => unknown>
4243
): JSONString<DataResult> {
4344
const extractions = new Array<DataExtraction>();
4445
const extractionCollector: ExtractionCollector = {
@@ -47,19 +48,27 @@ export class DataExtractorApiImpl implements DataExtractorApi {
4748
},
4849
};
4950

50-
DataExtractorApiImpl.lastVariablesInScope = variablesInScope;
51-
DataExtractorApiImpl.lastEvalFn = evalFn;
51+
const context: DataExtractorContext = {
52+
evalFn,
53+
variablesInScope,
54+
};
55+
56+
DataExtractorApiImpl.lastContext = context;
5257
const value = valueFn();
5358

54-
for (const e of this.extractors.values()) {
55-
e.getExtractions(value, extractionCollector, {
56-
evalFn,
57-
variablesInScope,
58-
});
59+
const extractors = new Array<DataExtractor>();
60+
61+
for (const fn of this.extractorSources.values()) {
62+
fn((extractor) => {
63+
extractors.push(extractor);
64+
}, helpers);
65+
}
66+
67+
for (const e of [...this.extractors.values(), ...extractors]) {
68+
e.getExtractions(value, extractionCollector, context);
5969
}
6070

61-
DataExtractorApiImpl.lastVariablesInScope = undefined;
62-
DataExtractorApiImpl.lastEvalFn = undefined;
71+
DataExtractorApiImpl.lastContext = undefined;
6372

6473
extractions.sort((a, b) => b.priority - a.priority);
6574
let usedExtraction = extractions[0];
@@ -69,7 +78,7 @@ export class DataExtractorApiImpl implements DataExtractorApi {
6978

7079
if (preferredDataExtractorId) {
7180
const preferred = extractions.find(
72-
e => e.id === preferredDataExtractorId
81+
(e) => e.id === preferredDataExtractorId
7382
);
7483
if (preferred) {
7584
usedExtraction = preferred;
@@ -99,4 +108,15 @@ export class DataExtractorApiImpl implements DataExtractorApi {
99108
// TODO consider preferExisting
100109
registerDefaultExtractors(this);
101110
}
111+
112+
public registerDataExtractorsSource(
113+
id: string,
114+
fn: LoadDataExtractorsFn
115+
): void {
116+
this.extractorSources.set(id, fn);
117+
}
118+
119+
public unregisterDataExtractorsSource(id: string): void {
120+
this.extractorSources.delete(id);
121+
}
102122
}

data-extraction/src/js/api/default-extractors/InjectedExtractor.ts

Lines changed: 0 additions & 34 deletions
This file was deleted.

data-extraction/src/js/api/default-extractors/registerDefaultDataExtractors.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { getDataExtractorApi } from "../injection";
99
import { GridExtractor } from "./GridExtractor";
1010
import { TableDataExtractor } from "./TableExtractor";
1111
import { StringDiffExtractor } from "./StringDiffExtractor";
12-
import { InjectedExtractor } from "./InjectedExtractor";
1312

1413
/**
1514
* The default data extractors should be registered by VS Code automatically.
@@ -28,7 +27,6 @@ export function registerDefaultExtractors(
2827
new GridExtractor(),
2928
new TableDataExtractor(),
3029
new StringDiffExtractor(),
31-
new InjectedExtractor(),
3230
]) {
3331
api.registerExtractor(item);
3432
}

data-extraction/src/js/api/injection.ts

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { DataExtractorApi } from "./DataExtractorApi";
33
import { DataExtractorApiImpl } from "./DataExtractorApiImpl";
44
import * as helpers from "../helpers";
55
import * as globalHelpers from "../global-helpers";
6+
import { getGlobal } from "../../getGlobal";
67

78
/**
89
* Returns standalone JS code representing an expression that initializes the data extraction API.
@@ -28,50 +29,43 @@ export function getExpressionForDataExtractorApi(): string {
2829
return `((${selfContainedGetInitializedDataExtractorApi.toString()})())`;
2930
}
3031

31-
export function getExpressionToDetectDataExtractorApiPresence(): string {
32-
return `((${selfContainedIsDataExtractorApiInitialized.toString()})())`;
33-
}
34-
35-
const apiKey = "@hediet/data-extractor-api/v2";
32+
const apiKey = "@hediet/data-extractor-api/v3";
3633

3734
export function getDataExtractorApi(): DataExtractorApi {
3835
installHelpers();
39-
const globalObj =
40-
typeof window === "object" ? (window as any) : (global as any);
36+
const globalObj = getGlobal();
4137
if (!globalObj[apiKey]) {
4238
globalObj[apiKey] = new DataExtractorApiImpl();
4339
}
4440
return globalObj[apiKey];
4541
}
4642

4743
/**
44+
* This code is used to detect if the API has not been initialized yet.
4845
* @internal
4946
*/
50-
function selfContainedIsDataExtractorApiInitialized(): boolean {
51-
const globalObj =
52-
typeof window === "object" ? (window as any) : (global as any);
53-
const key: typeof apiKey = "@hediet/data-extractor-api/v2";
54-
let api: DataExtractorApi | undefined = globalObj[key];
55-
return api !== undefined;
56-
}
47+
export const ApiHasNotBeenInitializedCode = "EgH0cybXij1jYUozyakO" as const;
5748

58-
/**
59-
* @internal
60-
*/
61-
function selfContainedGetInitializedDataExtractorApi(): DataExtractorApi {
62-
const globalObj =
63-
typeof window === "object" ? (window as any) : (global as any);
64-
const key: typeof apiKey = "@hediet/data-extractor-api/v2";
65-
let api: DataExtractorApi | undefined = globalObj[key];
66-
if (!api) {
67-
throw new Error(`Data Extractor API has not been initialized.`);
68-
}
69-
return api;
70-
}
49+
/**
50+
* @internal
51+
*/
52+
function selfContainedGetInitializedDataExtractorApi(): DataExtractorApi {
53+
const globalObj =
54+
typeof window === "object" ? (window as any) : (global as any);
55+
const key: typeof apiKey = "@hediet/data-extractor-api/v3";
56+
let api: DataExtractorApi | undefined = globalObj[key];
57+
if (!api) {
58+
const code: typeof ApiHasNotBeenInitializedCode =
59+
"EgH0cybXij1jYUozyakO";
60+
throw new Error(
61+
`Data Extractor API has not been initialized. Code: ${code}`
62+
);
63+
}
64+
return api;
65+
}
7166

7267
export function installHelpers(): void {
73-
const globalObj =
74-
typeof window === "object" ? (window as any) : (global as any);
68+
const globalObj = getGlobal();
7569
// `hediet` as prefix to avoid name collision (I own `hediet.de`).
7670
globalObj["hedietDbgVis"] = { ...helpers, ...globalHelpers };
7771
}

data-extraction/src/js/helpers/cache.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ export function cache<T>(
1212
let resultFn: () => any;
1313
let key: string;
1414
if (typeof expression === "string") {
15-
const evalFn = DataExtractorApiImpl.lastEvalFn!;
16-
resultFn = () => evalFn(expression);
15+
const context = DataExtractorApiImpl.lastContext!;
16+
resultFn = () => context.evalFn(expression);
1717
key = JSON.stringify({ expr: expression, id });
1818
} else {
1919
resultFn = () => expression();

data-extraction/src/js/helpers/find.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,22 @@ import { DataExtractorApiImpl } from "../api";
22

33
export function find(predicate: (obj: unknown) => boolean): unknown {
44
const processed = new Set();
5-
if (!DataExtractorApiImpl.lastVariablesInScope) {
6-
throw new Error("No variables in scope!");
5+
if (!DataExtractorApiImpl.lastContext) {
6+
throw new Error("No data extractor context!");
77
}
88

9-
const values = Object.values(DataExtractorApiImpl.lastVariablesInScope);
10-
const queue = [...values];
9+
const values = Object.values(
10+
DataExtractorApiImpl.lastContext.variablesInScope
11+
);
12+
const queue = [
13+
...values.map((v) => {
14+
try {
15+
return v();
16+
} catch (e) {
17+
return undefined;
18+
}
19+
}),
20+
];
1121

1222
let i = 10000;
1323
while (i > 0) {

data-extraction/src/js/helpers/tryEval.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,17 @@ export function tryEval(
3030
obj: Record<string, string> | string[] | string
3131
): Record<string, unknown> | unknown {
3232
const result: Record<string, unknown> = {};
33-
const evalFn = DataExtractorApiImpl.lastEvalFn!;
33+
const context = DataExtractorApiImpl.lastContext!;
3434
if (Array.isArray(obj)) {
3535
for (const val of obj) {
3636
try {
37-
result[val] = evalFn(val);
37+
result[val] = context.evalFn(val);
3838
} catch (e) {}
3939
}
4040
} else {
4141
for (const [key, val] of Object.entries(obj)) {
4242
try {
43-
result[key] = evalFn(val);
43+
result[key] = context.evalFn(val);
4444
} catch (e) {}
4545
}
4646
}

docs/custom-scripts.gif

2.35 MB
Loading

0 commit comments

Comments
 (0)