This commit is contained in:
Henning Dieterichs
2020-02-10 12:13:07 +01:00
parent 51957d52eb
commit 8aae7f7b0d
45 changed files with 625 additions and 413 deletions

View File

@@ -19,9 +19,9 @@ export type CommonDataType =
| CommonDataTypes.Svg
| CommonDataTypes.Html
| CommonDataTypes.DotGraph
| CommonDataTypes.TreeNodeData
| CommonDataTypes.AstData
| CommonDataTypes.GraphData;
| CommonDataTypes.Tree
| CommonDataTypes.Ast
| CommonDataTypes.Graph;
export module CommonDataTypes {
export interface Text {
@@ -31,34 +31,31 @@ export module CommonDataTypes {
fileName?: string;
}
export interface GraphData {
export interface Graph {
kind: { graph: true };
nodes: { id: string; label: string }[];
edges: { from: string; to: string; label: string }[];
nodes: NodeGraphData[];
edges: EdgeGraphData[];
}
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<TData = unknown> {
export interface Tree<TData = unknown> {
kind: { tree: true };
root: TreeNode<TData>;
}
export interface AstData
extends TreeNodeData<{
export interface Ast
extends Tree<{
position: number;
length: number;
}>,
@@ -68,11 +65,26 @@ export module CommonDataTypes {
}
export interface TreeNode<TExtraData> {
id: string | undefined;
name: string;
value: string | undefined;
emphasizedValue: string | undefined;
children: TreeNode<TExtraData>[];
data: TExtraData;
isMarked: boolean;
id?: string;
value?: string;
emphasizedValue?: string;
isMarked?: boolean;
}
export interface NodeGraphData {
id: string;
label: string;
color?: string;
}
export interface EdgeGraphData {
from: string;
to: string;
label: string;
id?: string;
color?: string;
weight?: number;
}

View File

@@ -37,7 +37,7 @@ 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 key = "@hediet/data-extractor/v1";
const prefix = key + "::";
let api: DataExtractorApi | undefined = obj[key];
@@ -135,7 +135,7 @@ 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";
const key = "@hediet/data-extractor/v1";
let api: DataExtractorApi | undefined = obj[key];
if (!api) {
const code: typeof ApiHasNotBeenInitializedCode =

View File

@@ -4,12 +4,12 @@ import { CommonDataTypes } from "../../CommonDataTypes";
// This class is self contained and can be injected into both nodejs and browser environments.
export class TypeScriptAstDataExtractor
implements DataExtractor<CommonDataTypes.AstData> {
implements DataExtractor<CommonDataTypes.Ast> {
readonly id = "TypeScriptAst";
getExtractions(
data: unknown,
collector: ExtractionCollector<CommonDataTypes.AstData>,
collector: ExtractionCollector<CommonDataTypes.Ast>,
evalFn: <TEval>(expression: string) => TEval
): void {
if (!data) {
@@ -63,7 +63,7 @@ export class TypeScriptAstDataExtractor
memberName: string,
marked: Set<ts.Node>,
emphasizedValueFn: (node: ts.Node) => string | undefined
): CommonDataTypes.AstData["root"] {
): CommonDataTypes.Ast["root"] {
const name = tsApi.SyntaxKind[node.kind];
const children = getChildren(node)
.map((childNode, idx) => {

View File

@@ -0,0 +1,105 @@
import {
CommonDataTypes,
EdgeGraphData,
NodeGraphData,
} from "../CommonDataTypes";
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,
}));
const includeLabelInName = false;
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);
}
});
}
export function createGraph<T>(
roots: T[],
infoSelector: (
item: T
) => {
id?: string | number;
edges: ({ to: T } & Omit<EdgeGraphData, "from" | "to">)[];
} & Omit<NodeGraphData, "id">
): CommonDataTypes.Graph {
const r: CommonDataTypes.Graph = {
kind: {
graph: true,
},
nodes: [],
edges: [],
};
let idCounter = 1;
const ids = new Map<T, string>();
function getId(item: T): string {
const _id = infoSelector(item).id;
if (_id !== undefined) {
return "" + _id;
}
let id = ids.get(item);
if (!id) {
id = (idCounter++).toString();
ids.set(item, id);
}
return id;
}
const queue = new Array<T>(...roots);
const processed = new Set<T>();
while (queue.length > 0) {
const item = queue.shift()!;
if (processed.has(item)) {
continue;
}
processed.add(item);
const nodeInfo = infoSelector(item);
const fromId = getId(item);
r.nodes.push({ ...nodeInfo, id: fromId, ["edges" as any]: undefined });
for (const e of nodeInfo.edges) {
const toId = getId(e.to);
r.edges.push({
...e,
from: fromId,
to: toId,
});
if (!processed.has(e.to)) {
queue.push(e.to);
}
}
}
return r;
}

View File

@@ -1,80 +0,0 @@
import { CommonDataTypes } from "../CommonDataTypes";
export function getClosureObj<T>(
roots: Record<string, T | undefined | null>,
edgeSelector: (item: T) => { item: T; edgeLabel: string }[],
labelSelector: (item: T) => string,
idSelector?: (item: T) => string | number
): CommonDataTypes.GraphData {
const items = Object.entries(roots).map(([k, v]) => v);
return getClosure<T>(
items.filter(i => !!i) as T[],
edgeSelector,
item => {
const names = new Array<string>();
for (const [key, val] of Object.entries(roots)) {
if (val === item) {
names.push(key);
}
}
const base = labelSelector(item);
return names.length === 0 ? base : `${names.join(", ")}: ${base}`;
},
idSelector
);
}
export function getClosure<T>(
roots: T[],
edgeSelector: (item: T) => { item: T; edgeLabel: string }[],
labelSelector: (item: T) => string,
idSelector?: (item: T) => string | number
): CommonDataTypes.GraphData {
const r: CommonDataTypes.GraphData = {
kind: {
graph: true,
},
nodes: [],
edges: [],
};
let idCounter = 1;
const ids = new Map<T, string>();
function getId(item: T): string {
if (idSelector) {
return "" + idSelector(item);
}
let id = ids.get(item);
if (!id) {
id = (idCounter++).toString();
ids.set(item, id);
}
return id;
}
const queue = new Array<T>(...roots);
const processed = new Set<T>();
while (queue.length > 0) {
const item = queue.shift()!;
if (processed.has(item)) {
continue;
}
processed.add(item);
const edges = edgeSelector(item);
const fromId = getId(item);
r.nodes.push({ id: fromId, label: labelSelector(item) });
for (const e of edges) {
const toId = getId(e.item);
r.edges.push({
from: fromId,
to: toId,
label: e.edgeLabel,
});
if (!processed.has(e.item)) {
queue.push(e.item);
}
}
}
return r;
}

View File

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