Next Iteration
This commit is contained in:
46
data-extraction/src/CommonDataTypes.ts
Normal file
46
data-extraction/src/CommonDataTypes.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { ExtractedData } from "./DataExtractionResult";
|
||||
|
||||
export module CommonDataTypes {
|
||||
export interface Text extends ExtractedData {
|
||||
kind: { text: true };
|
||||
text: string;
|
||||
// what should be used here? File type (.ts, .txt) or mime type?
|
||||
fileType?: string;
|
||||
}
|
||||
|
||||
export interface Svg extends Text {
|
||||
kind: { text: true; svg: true };
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface Html extends Text {
|
||||
kind: { text: true; html: true };
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface DotGraph extends Text {
|
||||
kind: { text: true; dotGraph: true };
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface TreeNodeData extends ExtractedData {
|
||||
kind: { tree: true };
|
||||
root: TreeNode<{}>;
|
||||
}
|
||||
|
||||
export interface AstData extends TreeNodeData, Text {
|
||||
kind: { text: true; tree: true; ast: true };
|
||||
root: TreeNode<{
|
||||
position: number;
|
||||
length: number;
|
||||
}>;
|
||||
}
|
||||
}
|
||||
|
||||
export interface TreeNode<TExtraData> {
|
||||
id: string | undefined;
|
||||
name: string;
|
||||
children: TreeNode<TExtraData>[];
|
||||
data: TExtraData;
|
||||
isSelected: boolean;
|
||||
}
|
||||
20
data-extraction/src/DataExtractionResult.ts
Normal file
20
data-extraction/src/DataExtractionResult.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
export type DataExtractionResult = {
|
||||
data: ExtractedData;
|
||||
usedExtractor: DataExtractorInfo;
|
||||
availableExtractors: DataExtractorInfo[];
|
||||
};
|
||||
|
||||
// is json
|
||||
export type ExtractedData = {
|
||||
kind: Record<string, true>;
|
||||
};
|
||||
|
||||
export type DataExtractorInfo = {
|
||||
id: DataExtractorId;
|
||||
name: string;
|
||||
priority: number;
|
||||
};
|
||||
|
||||
export type DataExtractorId = {
|
||||
__brand: "DataExtractorId";
|
||||
};
|
||||
3
data-extraction/src/index.ts
Normal file
3
data-extraction/src/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from "./js";
|
||||
export * from "./CommonDataTypes";
|
||||
export * from "./DataExtractionResult";
|
||||
21
data-extraction/src/js/DataExtractor.ts
Normal file
21
data-extraction/src/js/DataExtractor.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { ExtractedData } from "../DataExtractionResult";
|
||||
|
||||
export interface DataExtractor<T extends ExtractedData> {
|
||||
id: string;
|
||||
getExtractions(
|
||||
data: any,
|
||||
extractionCollector: ExtractionCollector<T>
|
||||
): void;
|
||||
}
|
||||
|
||||
export interface ExtractionCollector<T extends ExtractedData> {
|
||||
addExtraction(extraction: DataExtraction<T>): void;
|
||||
}
|
||||
|
||||
export interface DataExtraction<TData extends ExtractedData> {
|
||||
// the higher the better
|
||||
priority: number;
|
||||
id: string;
|
||||
name: string;
|
||||
extractData(): TData;
|
||||
}
|
||||
144
data-extraction/src/js/DataExtractorApi.ts
Normal file
144
data-extraction/src/js/DataExtractorApi.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
import {
|
||||
ExtractedData,
|
||||
DataExtractionResult,
|
||||
DataExtractorInfo,
|
||||
} from "../DataExtractionResult";
|
||||
import {
|
||||
DataExtractor,
|
||||
DataExtraction,
|
||||
ExtractionCollector,
|
||||
} from "./DataExtractor";
|
||||
|
||||
export type DataResult =
|
||||
| {
|
||||
kind: "Data";
|
||||
extractionResult: DataExtractionResult;
|
||||
}
|
||||
| { kind: "NoExtractors" };
|
||||
|
||||
export interface JSONString<T> extends String {
|
||||
__brand: { json: T };
|
||||
}
|
||||
|
||||
export interface DataExtractorApi {
|
||||
registerExtractor<TExtractedData extends ExtractedData>(
|
||||
extractor: DataExtractor<TExtractedData>
|
||||
): void;
|
||||
getData(
|
||||
value: unknown,
|
||||
preferredDataExtractorId: string | undefined
|
||||
): JSONString<DataResult>;
|
||||
}
|
||||
|
||||
declare const window: any;
|
||||
|
||||
export function selfContainedInitDataExtractorApi(): boolean {
|
||||
const obj = typeof window === "object" ? (window as any) : (global as any);
|
||||
const key = "@hediet/data-extractor";
|
||||
const prefix = key + "::";
|
||||
|
||||
let api: DataExtractorApi | undefined = obj[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(obj)
|
||||
.filter(([key, value]) => key.startsWith(prefix))
|
||||
.map(([key, value]) => value as DataExtractor<ExtractedData>);
|
||||
}
|
||||
obj[key] = api = {
|
||||
registerExtractor(extractor) {
|
||||
obj[prefix + extractor.id] = extractor;
|
||||
},
|
||||
getData(value, preferredDataExtractorId) {
|
||||
const extractions = new Array<DataExtraction<ExtractedData>>();
|
||||
const collector: ExtractionCollector<ExtractedData> = {
|
||||
addExtraction(extraction) {
|
||||
extractions.push(extraction);
|
||||
},
|
||||
};
|
||||
for (const e of getExtractors()) {
|
||||
e.getExtractions(value, collector);
|
||||
}
|
||||
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: "toString",
|
||||
getExtractions(data, collector) {
|
||||
collector.addExtraction({
|
||||
id: "toString",
|
||||
name: "To String",
|
||||
priority: 100,
|
||||
extractData() {
|
||||
return {
|
||||
kind: {
|
||||
text: true,
|
||||
},
|
||||
text: data.toString(),
|
||||
};
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
export const ApiHasNotBeenInitializedCode = "EgH0cybXij1jYUozyakO" as const;
|
||||
|
||||
export function selfContainedGetInitializedDataExtractorApi(): DataExtractorApi {
|
||||
const obj = typeof window === "object" ? (window as any) : (global as any);
|
||||
const key = "@hediet/data-extractor";
|
||||
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;
|
||||
}
|
||||
|
||||
export function getDataExtractorApi(): DataExtractorApi {
|
||||
selfContainedInitDataExtractorApi();
|
||||
return selfContainedGetInitializedDataExtractorApi();
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import * as ts from "typescript";
|
||||
import { DataExtractor, ExtractionCollector } from "../DataExtractor";
|
||||
import { CommonDataTypes } from "../../CommonDataTypes";
|
||||
|
||||
export class TypeScriptAstDataExtractor
|
||||
implements DataExtractor<CommonDataTypes.AstData> {
|
||||
readonly id = "TypeScriptAst";
|
||||
|
||||
getExtractions(
|
||||
data: any,
|
||||
collector: ExtractionCollector<CommonDataTypes.AstData>
|
||||
): void {
|
||||
if (typeof data !== "object" || data === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tsApi = require("typescript") as typeof ts | undefined;
|
||||
if (!tsApi) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((tsApi as any).isNode(data)) {
|
||||
const n = data as ts.Node;
|
||||
collector.addExtraction({
|
||||
id: "ts-ast",
|
||||
name: "TypeScript AST",
|
||||
priority: 1000,
|
||||
extractData() {
|
||||
return {
|
||||
kind: { text: true, tree: true, ast: true },
|
||||
root: toTreeNode(data, "root"),
|
||||
text: n.getSourceFile().text,
|
||||
fileType: "ts",
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function findKey(value: any, object: any): string | null {
|
||||
for (var key in object) {
|
||||
if (key.startsWith("_")) continue;
|
||||
|
||||
var member = object[key];
|
||||
if (member === value) return key;
|
||||
|
||||
if (Array.isArray(member) && member.indexOf(value) !== -1) return key;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function toTreeNode(
|
||||
node: ts.Node,
|
||||
memberName: string
|
||||
): CommonDataTypes.AstData["root"] {
|
||||
const name = ts.SyntaxKind[node.kind];
|
||||
const children = node
|
||||
.getChildren()
|
||||
.map((childNode, idx) => {
|
||||
let parentPropertyName = findKey(childNode, node) || "";
|
||||
|
||||
if (childNode.kind == ts.SyntaxKind.SyntaxList) {
|
||||
childNode.getChildren().some(c => {
|
||||
parentPropertyName = findKey(c, node) || "";
|
||||
return !!parentPropertyName;
|
||||
});
|
||||
|
||||
if (childNode.getChildren().length === 0) return null!;
|
||||
}
|
||||
|
||||
if (node.kind == ts.SyntaxKind.SyntaxList) {
|
||||
parentPropertyName = "" + idx;
|
||||
}
|
||||
|
||||
return toTreeNode(childNode, parentPropertyName);
|
||||
})
|
||||
.filter(c => c !== null);
|
||||
|
||||
return {
|
||||
name: name,
|
||||
id: memberName,
|
||||
children: children,
|
||||
data: {
|
||||
length: 0,
|
||||
position: 0,
|
||||
},
|
||||
isSelected: false,
|
||||
// startPos: node.pos,
|
||||
// endPos: node.end
|
||||
};
|
||||
}
|
||||
3
data-extraction/src/js/index.ts
Normal file
3
data-extraction/src/js/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from "./Extractors/TypeScriptDataExtractors";
|
||||
export * from "./DataExtractor";
|
||||
export * from "./DataExtractorApi";
|
||||
Reference in New Issue
Block a user