Next Iteration

This commit is contained in:
Henning Dieterichs
2019-07-31 11:41:10 +02:00
parent 6175e8f55e
commit 922eb0b845
71 changed files with 19606 additions and 14862 deletions

View 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;
}

View 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";
};

View File

@@ -0,0 +1,3 @@
export * from "./js";
export * from "./CommonDataTypes";
export * from "./DataExtractionResult";

View 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;
}

View 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();
}

View File

@@ -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
};
}

View File

@@ -0,0 +1,3 @@
export * from "./Extractors/TypeScriptDataExtractors";
export * from "./DataExtractor";
export * from "./DataExtractorApi";