complete basic structures

This commit is contained in:
Timo Hocker 2020-04-24 12:02:32 +02:00
parent b586e57fd7
commit e63ce1f8ed
13 changed files with 221 additions and 52 deletions

View File

@ -2,6 +2,11 @@ export class Edge {
public origin: string;
public target: string;
public constructor (origin: string, target: string) {
this.origin = origin;
this.target = target;
}
// eslint-disable-next-line @typescript-eslint/naming-convention
public toString (): string {
return `${this.origin} -> ${this.target}`;

17
lib/Element.ts Normal file
View File

@ -0,0 +1,17 @@
export class Element {
public name: string;
public parent: string;
public get full_name (): string {
return `${this.parent}_${this.name}`;
}
public constructor (name: string, parent = '') {
this.name = name;
this.parent = parent;
}
// eslint-disable-next-line @typescript-eslint/naming-convention
public toString (): string {
return this.full_name;
}
}

View File

@ -1,23 +1,54 @@
import { Node } from './Node';
import { Element } from './Element';
import { Edge } from './Edge';
import { Node } from './Node';
export class Graph extends Node {
export class Graph extends Element {
public children: Array<Graph> = [];
public nodes: Array<GraphNode> = [];
public nodes: Array<Node> = [];
public is_root = false;
public edges: Array<Edge>;
public edges: Array<Edge> = [];
// eslint-disable-next-line @typescript-eslint/naming-convention
public toString (): string {
public toString (level = 0): string {
return `subgraph cluster_${this.full_name} {
${this.children.map ((c) => c.toString ())
.join ('\n')}
${this.children.map ((c) => c.toString (level + 1))
.join ('\n ')}
${this.nodes.map ((c) => c.toString ())
.join ('\n')}
.join ('\n ')}
${this.edges.map ((c) => c.toString ())
.join ('\n')}
}`;
.join ('\n ')}
}`.replace (/\n/gu, `\n${' '.repeat (level)}`)
.replace (/^\s+$/gmu, '');
}
public add_node (constructor: () => Node): void {
const node = constructor ();
node.parent = this.full_name;
this.nodes.push (node);
}
public add_graph (constructor: () => Graph): void {
const graph = constructor ();
graph.parent = this.full_name;
graph.update_parent ();
this.children.push (graph);
}
public update_parent (): void {
for (const node of this.nodes)
node.parent = this.full_name;
for (const graph of this.children) {
graph.parent = this.full_name;
graph.update_parent ();
}
}
public add_edge (origin: string, target: string): void {
this.edges.push (
new Edge (`${this.full_name}_${origin}`, `${this.full_name}_${target}`)
);
}
}

View File

@ -1,20 +0,0 @@
import { Node } from './Node';
export class GraphNode extends Node {
public label: string;
public is_table: boolean;
public table_contents: Array<Array<string>>;
private get serialized_table (): string {
const mapped_columns = this.table_contents
.map ((val) => `<td>${val.join ('</td><td>')}</td>`);
return `<table><tr>${mapped_columns.join ('</tr><tr>')}</tr></table>`;
}
// eslint-disable-next-line @typescript-eslint/naming-convention
public toString (): string {
return `${this.full_name}[label=<${this.is_table
? this._serialized_table
: this.label}>]`;
}
}

View File

@ -1,12 +1,33 @@
export class Node {
public name;
public parent;
public get full_name (): string {
return `${this.parent}_${this.name}`;
import { Element } from './Element';
export class Node extends Element {
public label?: string;
public is_table = false;
public table_contents?: Array<Array<string>>;
public constructor (name: string, parent?: string, label?: string) {
super (name, parent);
this.label = label;
}
private get serialized_table (): string {
if (typeof this.table_contents === 'undefined')
throw new Error ('table contents are undefined');
const mapped_columns = this.table_contents
.map ((val) => `<td>${val.join ('</td><td>')}</td>`);
return `<table>\n <tr>${
mapped_columns.join ('</tr>\n <tr>')
}</tr>\n</table>`;
}
// eslint-disable-next-line @typescript-eslint/naming-convention
public toString (): string {
return this.full_name;
if (this.is_table || typeof this.label !== 'undefined') {
return `${this.full_name} [label=<${this.is_table
? this.serialized_table
: this.label}>]`;
}
return `${this.full_name}`;
}
}

View File

@ -1,4 +1,4 @@
export { Graph } from './Graph';
export { GraphNode } from './GraphNode';
export { Node } from './Node';
export { Element } from './Element';
export { Edge } from './Edge';

View File

@ -2,18 +2,19 @@
"name": "graphviz-builder",
"version": "1.0.0",
"main": "index.js",
"author": "Timo Hocker <thocker@oas.de>",
"author": "Timo Hocker <timo@scode.ovh>",
"license": "MIT",
"devDependencies": {
"@scode/eslint-config-ts": "^1.0.19",
"ava": "^3.7.0",
"@ava/typescript": "^1.1.1",
"@scode/eslint-config-ts": "^1.0.22",
"ava": "^3.7.1",
"eslint": "^6.8.0",
"nyc": "^15.0.1",
"typescript": "^3.8.3"
},
"scripts": {
"lint": "eslint . --ext .js,.jsx,.ts,.tsx,.vue,.mjs",
"test": "nyc ava",
"test": "tsc && nyc ava",
"ci": "yarn && node jenkins.js"
}
}

20
test/.eslintrc.js Normal file
View File

@ -0,0 +1,20 @@
module.exports = {
env: {
commonjs: true,
es6: true,
node: true
},
extends: [
'@scode/eslint-config-ts'
],
globals: {
Atomics: 'readonly',
SharedArrayBuffer: 'readonly'
},
parserOptions: {
ecmaVersion: 2018
},
rules: {
'node/no-unpublished-import': 'off'
}
}

8
test/Edge.ts Normal file
View File

@ -0,0 +1,8 @@
import test from 'ava';
import { Edge } from '../lib';
test ('serialize', (t) => {
const e = new Edge ('foo', 'bar');
const serialized = e.toString ();
t.is (serialized, 'foo -> bar');
});

35
test/Graph.ts Normal file
View File

@ -0,0 +1,35 @@
import fs from 'fs';
import test from 'ava';
import { Graph, Node } from '../lib';
const result = `subgraph cluster_bar_foo {
subgraph cluster_bar_foo_baz {
bar_foo_baz_asd
}
bar_foo_baz
bar_foo_foo
bar_foo_foo -> bar_foo_baz
}`;
test ('serialize', (t) => {
const g = new Graph ('foo', 'bar');
t.is (g.full_name, 'bar_foo');
g.add_graph (() => {
const graph = new Graph ('baz');
graph.add_node (() => new Node ('asd'));
return graph;
});
g.add_node (() => new Node ('baz'));
g.add_node (() => new Node ('foo'));
g.add_edge ('foo', 'baz');
const serialized = g.toString ();
t.is (serialized, result);
});

44
test/Node.ts Normal file
View File

@ -0,0 +1,44 @@
import test from 'ava';
import { Node } from '../lib/Node';
const serialized_simple = 'bar_foo [label=<baz>]';
const serialized_table = `bar_foo [label=<<table>
<tr><td>foo</td><td>bar</td><td>baz</td></tr>
<tr><td>bar</td><td>baz</td><td>foo</td></tr>
<tr><td>baz</td><td>foo</td><td>bar</td></tr>
</table>>]`;
test ('serialize simple', (t) => {
const g = new Node ('foo', 'bar', 'baz');
const serialized = g.toString ();
t.is (g.full_name, 'bar_foo');
t.is (serialized, serialized_simple);
});
test ('serialize table', (t) => {
const g = new Node ('foo', 'bar', 'baz');
g.table_contents = [
[
'foo',
'bar',
'baz'
],
[
'bar',
'baz',
'foo'
],
[
'baz',
'foo',
'bar'
]
];
g.is_table = true;
const serialized = g.toString ();
t.is (g.full_name, 'bar_foo');
t.is (serialized, serialized_table);
});

View File

@ -13,7 +13,7 @@
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
"outDir": "./dist", /* Redirect output structure to the directory. */
"rootDir": "./lib", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "rootDir": "./lib", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */

View File

@ -2,6 +2,13 @@
# yarn lockfile v1
"@ava/typescript@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@ava/typescript/-/typescript-1.1.1.tgz#3dcaba3aced8026fdb584d927d809752854dc6e6"
integrity sha512-KbLUAe2cWXK63WLK6LnOJonjwEDU/8MNXCOA1ooX/YFZgKRmeAD1kZu+2K0ks5fnOCEcckNQAooyBNGdZUmMQA==
dependencies:
escape-string-regexp "^2.0.0"
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e"
@ -228,10 +235,10 @@
"@scode/eslint-config" "^2.0.1"
eslint-plugin-import "^2.20.1"
"@scode/eslint-config-ts@^1.0.19":
version "1.0.20"
resolved "https://npm.scode.ovh/@scode%2feslint-config-ts/-/eslint-config-ts-1.0.20.tgz#2bd2265591e2605dff77c16d3d82f95948681b59"
integrity sha512-9NgP8ksrlRZ29pys7bDLu23FhgfuZfukwh/XPmkflO208Vd1vSpecRS9nm+da0gsXCjIefJQEfQXYyRxNi+iJQ==
"@scode/eslint-config-ts@^1.0.22":
version "1.0.22"
resolved "https://npm.scode.ovh/@scode%2feslint-config-ts/-/eslint-config-ts-1.0.22.tgz#bedb57f187f2fca7ca661292b0ce15a26530bd95"
integrity sha512-QwRen8tNC/b4XtdIADr5J4WNM58f5lryINcLBObTzZVQfjr8Qlz7AkwgxFN57u//iP3fCXzn4nPI89J2ift7Hw==
dependencies:
"@scode/eslint-config-es6" "^1.0.1"
"@typescript-eslint/eslint-plugin" "^2.26.0"
@ -496,10 +503,10 @@ astral-regex@^2.0.0:
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
ava@^3.7.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/ava/-/ava-3.7.0.tgz#86869f831628de31e817fe00d46b350849295092"
integrity sha512-SV1oqRpZ00qevETsNzcqTqaTnspJZZ1wBGOjyQzcLOMChnUF+17/RS4YiieClaV0eCFULLU/roICpJoQlNLHZw==
ava@^3.7.1:
version "3.7.1"
resolved "https://registry.yarnpkg.com/ava/-/ava-3.7.1.tgz#7baba69820242e0efcbfb4ab2060b500ec745421"
integrity sha512-UX7RSenUgFPhxe866doqOJy6tQZAXAVAU4yufYeBAcnEjnS/plIcG6lE2yGIqgjk5cIMpSi+sP4f6EsornlsuA==
dependencies:
"@concordance/react" "^2.0.0"
ansi-styles "^4.2.1"