Merge branch 'master' of git.scode.ovh:timo/graphviz-builder
This commit is contained in:
commit
84bda96276
@ -7,6 +7,7 @@ const fs = require ('fs');
|
||||
const child_process = require ('child_process');
|
||||
|
||||
const pkg = JSON.parse (fs.readFileSync ('package.json', 'utf-8'));
|
||||
|
||||
[
|
||||
,, pkg.version
|
||||
] = process.argv;
|
||||
|
@ -6,15 +6,18 @@ export class Edge {
|
||||
public target: string;
|
||||
public style?: EdgeStyles;
|
||||
public color?: Color;
|
||||
private _directional: boolean;
|
||||
|
||||
public constructor (origin: string, target: string) {
|
||||
public constructor (origin: string, target: string, directional: boolean) {
|
||||
this.origin = origin;
|
||||
this.target = target;
|
||||
this._directional = directional;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
public toString (): string {
|
||||
const attributes = [];
|
||||
|
||||
if (this.style)
|
||||
attributes.push ({ name: 'style', value: this.style.toString () });
|
||||
if (this.color)
|
||||
@ -23,7 +26,9 @@ export class Edge {
|
||||
const attr_string = ` [${attributes.map ((v) => `${v.name}="${v.value}"`)
|
||||
.join (',')}]`;
|
||||
|
||||
return `${this.origin} -> ${this.target}${attributes.length > 0
|
||||
return `${this.origin} -${
|
||||
this._directional ? '>' : '-'
|
||||
} ${this.target}${attributes.length > 0
|
||||
? attr_string
|
||||
: ''}`;
|
||||
}
|
||||
|
@ -25,6 +25,13 @@ export class Element {
|
||||
}
|
||||
|
||||
public constructor (name: string, parent = '') {
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
const regex = /^[a-z_][a-z_0-9]+$/iu;
|
||||
|
||||
if (!regex.test (name))
|
||||
throw new Error ('invalid name specified');
|
||||
>>>>>>> f553396eee29e1e0820624345e5d23dc3471a4a4
|
||||
this.name = name;
|
||||
this.parent_name = parent;
|
||||
}
|
||||
|
35
lib/Graph.ts
35
lib/Graph.ts
@ -3,6 +3,7 @@ import { Edge } from './Edge';
|
||||
import { Node } from './Node';
|
||||
import { GraphStyles, NodeStyles } from './Styles';
|
||||
import { Color } from './Color';
|
||||
import { GraphLayouts } from './GraphLayouts';
|
||||
|
||||
interface NodeOptions {
|
||||
name: string;
|
||||
@ -18,19 +19,35 @@ export class Graph extends Element {
|
||||
public edges: Array<Edge> = [];
|
||||
public style?: GraphStyles;
|
||||
public color?: Color;
|
||||
public directional = true;
|
||||
public overlap?: boolean | string;
|
||||
public splines?: boolean | string;
|
||||
public layout?: GraphLayouts;
|
||||
|
||||
private get attributes (): Array<{name: string; value: string}> {
|
||||
const attributes = [];
|
||||
|
||||
if (typeof this.color !== 'undefined')
|
||||
attributes.push ({ name: 'color', value: this.color.toString () });
|
||||
if (typeof this.style !== 'undefined')
|
||||
attributes.push ({ name: 'style', value: this.style.toString () });
|
||||
if (typeof this.overlap !== 'undefined')
|
||||
attributes.push ({ name: 'overlap', value: this.overlap.toString () });
|
||||
if (typeof this.splines !== 'undefined')
|
||||
attributes.push ({ name: 'splines', value: this.splines.toString () });
|
||||
if (typeof this.layout !== 'undefined')
|
||||
attributes.push ({ name: 'layout', value: this.layout.toString () });
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
public toString (): string {
|
||||
const header = this.parent
|
||||
? `subgraph cluster_${this.full_name}`
|
||||
: `digraph ${this.full_name}`;
|
||||
const attributes = [];
|
||||
if (this.color)
|
||||
attributes.push ({ name: 'color', value: this.color.toString () });
|
||||
if (this.style)
|
||||
attributes.push ({ name: 'style', value: this.style.toString () });
|
||||
: `${this.directional ? 'di' : ''}graph ${this.full_name}`;
|
||||
|
||||
let attrs = `\n${attributes.map ((v) => `${v.name} = ${v.value}`)
|
||||
let attrs = `\n${this.attributes.map ((v) => `${v.name} = ${v.value}`)
|
||||
.join ('\n')}\n`;
|
||||
let children = `\n${this.children.map ((c) => c.toString ())
|
||||
.join ('\n')}\n`;
|
||||
@ -75,6 +92,8 @@ export class Graph extends Element {
|
||||
public add_graph (constructor: ((g: Graph) => void) | string): string {
|
||||
const graph = new Graph ('unnamed', this.full_name);
|
||||
|
||||
graph.directional = this.directional;
|
||||
|
||||
if (typeof constructor === 'string')
|
||||
graph.name = constructor;
|
||||
else
|
||||
@ -85,6 +104,6 @@ export class Graph extends Element {
|
||||
}
|
||||
|
||||
public add_edge (origin: string, target: string): void {
|
||||
this.edges.push (new Edge (origin, target));
|
||||
this.edges.push (new Edge (origin, target, this.directional));
|
||||
}
|
||||
}
|
||||
|
10
lib/GraphLayouts.ts
Normal file
10
lib/GraphLayouts.ts
Normal file
@ -0,0 +1,10 @@
|
||||
export enum GraphLayouts {
|
||||
neato = 'neato',
|
||||
dot = 'dot',
|
||||
circo = 'circo',
|
||||
fdp = 'fdp',
|
||||
sfdp = 'sfdp',
|
||||
osage = 'osage',
|
||||
twopi = 'twopi',
|
||||
patchwork = 'patchwork'
|
||||
}
|
@ -20,6 +20,7 @@ export class Node extends Element {
|
||||
|
||||
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>`;
|
||||
@ -28,6 +29,7 @@ export class Node extends Element {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
public toString (): string {
|
||||
const attributes = [];
|
||||
|
||||
if (this.label || this.is_table) {
|
||||
attributes.push ({
|
||||
name: 'label',
|
||||
@ -51,6 +53,7 @@ export class Node extends Element {
|
||||
'"',
|
||||
'"'
|
||||
];
|
||||
|
||||
return `${v.name}=${d[0]}${v.value}${d[1]}`;
|
||||
})
|
||||
.join (', ');
|
||||
|
@ -4,3 +4,4 @@ export { Element } from './Element';
|
||||
export { Edge } from './Edge';
|
||||
export { Color } from './Color';
|
||||
export { EdgeStyles, NodeStyles, GraphStyles } from './Styles';
|
||||
export { GraphLayouts } from './GraphLayouts';
|
||||
|
@ -18,7 +18,7 @@
|
||||
"compile": "tsc"
|
||||
},
|
||||
"files": [
|
||||
"/dist/",
|
||||
"/dist/lib/",
|
||||
"LICENSE"
|
||||
],
|
||||
"dependencies": {
|
||||
|
@ -4,6 +4,7 @@ import { Color } from '../lib';
|
||||
test ('serialize', (t) => {
|
||||
const wh = Color.white;
|
||||
const tr = Color.transparent;
|
||||
|
||||
t.is (wh.toString (), '#ffffff');
|
||||
t.is (tr.toString (), '#00000000');
|
||||
});
|
||||
|
14
test/Edge.ts
14
test/Edge.ts
@ -2,9 +2,21 @@ import test from 'ava';
|
||||
import { Edge, Color, EdgeStyles } from '../lib';
|
||||
|
||||
test ('serialize', (t) => {
|
||||
const e = new Edge ('foo', 'bar');
|
||||
const e = new Edge ('foo', 'bar', false);
|
||||
|
||||
e.color = Color.white;
|
||||
e.style = EdgeStyles.dashed;
|
||||
const serialized = e.toString ();
|
||||
|
||||
t.is (serialized, 'foo -- bar [style="dashed",color="#ffffff"]');
|
||||
});
|
||||
|
||||
test ('serialize directional', (t) => {
|
||||
const e = new Edge ('foo', 'bar', true);
|
||||
|
||||
e.color = Color.white;
|
||||
e.style = EdgeStyles.dashed;
|
||||
const serialized = e.toString ();
|
||||
|
||||
t.is (serialized, 'foo -> bar [style="dashed",color="#ffffff"]');
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
import test from 'ava';
|
||||
import { Graph, GraphStyles, Color, NodeStyles } from '../lib';
|
||||
import { Graph, GraphStyles, Color, NodeStyles, GraphLayouts } from '../lib';
|
||||
|
||||
const result = `digraph foo {
|
||||
subgraph cluster_foo_baz {
|
||||
@ -26,6 +26,27 @@ const result = `digraph foo {
|
||||
foo_foo -> foo_baz
|
||||
}`;
|
||||
|
||||
const non_directional = `graph foo {
|
||||
subgraph cluster_foo_bar {
|
||||
foo_bar_baz [label="baz"]
|
||||
foo_bar_asd [label="asd"]
|
||||
|
||||
foo_bar_baz -- foo_bar_asd
|
||||
}
|
||||
|
||||
foo_foo [label="foo"]
|
||||
|
||||
foo_bar_baz -- foo_foo
|
||||
}`;
|
||||
|
||||
const attributes = `digraph attr {
|
||||
color = #000000
|
||||
style = bold
|
||||
overlap = false
|
||||
splines = true
|
||||
layout = neato
|
||||
}`;
|
||||
|
||||
test ('serialize', (t) => {
|
||||
const g = new Graph ('foo');
|
||||
|
||||
@ -57,9 +78,44 @@ test ('serialize', (t) => {
|
||||
|
||||
const baz = g.add_node ('baz');
|
||||
const foo = g.add_node ('foo');
|
||||
|
||||
g.add_edge (foo, baz);
|
||||
|
||||
const serialized = g.toString ();
|
||||
|
||||
t.is (serialized, result);
|
||||
});
|
||||
|
||||
test ('non directional', (t) => {
|
||||
const g = new Graph ('foo');
|
||||
|
||||
g.directional = false;
|
||||
|
||||
let n = '';
|
||||
|
||||
g.add_graph ((sub) => {
|
||||
sub.name = 'bar';
|
||||
n = sub.add_node ('baz');
|
||||
const n2 = sub.add_node ('asd');
|
||||
|
||||
sub.add_edge (n, n2);
|
||||
});
|
||||
|
||||
const f = g.add_node ('foo');
|
||||
|
||||
g.add_edge (n, f);
|
||||
|
||||
t.is (g.toString (), non_directional);
|
||||
});
|
||||
|
||||
test ('attributes', (t) => {
|
||||
const g = new Graph ('attr');
|
||||
|
||||
g.layout = GraphLayouts.neato;
|
||||
g.overlap = false;
|
||||
g.splines = true;
|
||||
g.color = Color.black;
|
||||
g.style = GraphStyles.bold;
|
||||
|
||||
t.is (g.toString (), attributes);
|
||||
});
|
||||
|
@ -11,16 +11,19 @@ const serialized_table = `bar_foo [label=<<table>
|
||||
|
||||
test ('serialize simple', (t) => {
|
||||
const g = new Node ('foo', 'bar', 'baz');
|
||||
|
||||
g.color = Color.green;
|
||||
g.style = NodeStyles.dashed;
|
||||
|
||||
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.color = Color.green;
|
||||
g.style = NodeStyles.invisible;
|
||||
|
||||
@ -44,6 +47,7 @@ test ('serialize table', (t) => {
|
||||
g.is_table = true;
|
||||
|
||||
const serialized = g.toString ();
|
||||
|
||||
t.is (g.full_name, 'bar_foo');
|
||||
t.is (serialized, serialized_table);
|
||||
});
|
||||
@ -55,7 +59,12 @@ test ('adhere to naming convention', (t) => {
|
||||
|
||||
test ('throw on invalid name', (t) => {
|
||||
t.throws (() => {
|
||||
<<<<<<< HEAD
|
||||
const n = new Node ('564#+-.,/@', 'parent');
|
||||
=======
|
||||
const n = new Node ('invalid.name', 'parent');
|
||||
|
||||
>>>>>>> f553396eee29e1e0820624345e5d23dc3471a4a4
|
||||
return n.toString ();
|
||||
}, { message: 'invalid name specified' });
|
||||
});
|
||||
|
60
yarn.lock
60
yarn.lock
@ -228,31 +228,31 @@
|
||||
fastq "^1.6.0"
|
||||
|
||||
"@scode/encoding-helper@^1.0.21":
|
||||
version "1.0.25"
|
||||
resolved "https://npm.scode.ovh/@scode%2fencoding-helper/-/encoding-helper-1.0.25.tgz#a6e1fe47525d99fe24b0f9df554d688c8d9e5ab0"
|
||||
integrity sha512-cMLfuCKhawpqasdkG8DuYMKmh1p2WzB7MuCiiFTglSU6zseISBCmEPmX1UduELY5+qt42KxmXiwgkC4/xK3MHQ==
|
||||
version "1.0.26"
|
||||
resolved "https://npm.scode.ovh/@scode%2fencoding-helper/-/encoding-helper-1.0.26.tgz#d0e4627e16885ecb10bdc3b1b87b5bb9aae39455"
|
||||
integrity sha512-f7hJzrHjI5D+yC8KnwGV1q/vARE0ZOUHe0f3fybCmzU38wobs0Jm+XgYi98ViXqFmvt18cz1NI1c4q9sdrcsQQ==
|
||||
|
||||
"@scode/eslint-config-es6@^1.0.1":
|
||||
version "1.0.22"
|
||||
resolved "https://npm.scode.ovh/@scode%2feslint-config-es6/-/eslint-config-es6-1.0.22.tgz#be1a200c821137e74b829ec0029acc81734f1fca"
|
||||
integrity sha512-/BnuycrNMFm4KKk2rz7JF0DBAN/JvWKpE0uK2fLz8m4o88TX5AKrlUg/w4c6ctPTzhD9t9tS9tuZFn/Vs0J3mg==
|
||||
version "1.0.24"
|
||||
resolved "https://npm.scode.ovh/@scode%2feslint-config-es6/-/eslint-config-es6-1.0.24.tgz#c3c88ac98beb9dc73da7812f614e8f289cec763b"
|
||||
integrity sha512-FyMzNbI6ZgGTr7bG1av+VSuYYWZIRbUp+ZvjVwtZduKkijXotOTg3jw0vMWi09IEq7esusQQZ/lZInAjvXJWKA==
|
||||
dependencies:
|
||||
"@scode/eslint-config" "^2.0.1"
|
||||
eslint-plugin-import "^2.20.1"
|
||||
|
||||
"@scode/eslint-config-ts@^1.0.22":
|
||||
version "1.0.23"
|
||||
resolved "https://npm.scode.ovh/@scode%2feslint-config-ts/-/eslint-config-ts-1.0.23.tgz#fc29e278fffd3737657a1acdf69ade81cd0736c5"
|
||||
integrity sha512-V+wzFQvl5sypixhTeNxO0lTaCDp6TSNMvbNfpFsNUl4kEZVNtlPY9+9Bf7Xnn7GY0qoA5d/fdqn4Qkxo96UhTA==
|
||||
version "1.0.27"
|
||||
resolved "https://npm.scode.ovh/@scode%2feslint-config-ts/-/eslint-config-ts-1.0.27.tgz#d3d068df6f287273041029f4549378ecaa17972b"
|
||||
integrity sha512-KapsP1enFNw4kBjI4L3WKBnwKeKp6Ka9ml3OoT7zpGZZLd9nzvyD1SpGks9PQ1ZJse9nBy2HVuRVPAHzfIBvmw==
|
||||
dependencies:
|
||||
"@scode/eslint-config-es6" "^1.0.1"
|
||||
"@typescript-eslint/eslint-plugin" "^2.26.0"
|
||||
"@typescript-eslint/parser" "^2.26.0"
|
||||
|
||||
"@scode/eslint-config@^2.0.1":
|
||||
version "2.0.11"
|
||||
resolved "https://npm.scode.ovh/@scode%2feslint-config/-/eslint-config-2.0.11.tgz#3cc3cd71f3bc3ac39868bf608e0517bee09da58f"
|
||||
integrity sha512-K8DpFdmepU1FNp0QJn5gbXS45g7k04rFUJp2OKQDqSa+3iywBvi44pMzJNOdZjkj+t3dGcOdeWcYOngz2MjdlA==
|
||||
version "2.0.13"
|
||||
resolved "https://npm.scode.ovh/@scode%2feslint-config/-/eslint-config-2.0.13.tgz#e7e4d3c9185449de7a7d810f3ee783459b779e8a"
|
||||
integrity sha512-ans0dulrnReK+9+XD5nw04kKEdveEVbRL9AKH3PTr8jUQJBY/pzeDznkf6oWnLPBKqbDn/MEKlS5MOExAgooWw==
|
||||
dependencies:
|
||||
eslint-plugin-jsdoc "^24.0.0"
|
||||
eslint-plugin-node "^11.0.0"
|
||||
@ -315,39 +315,39 @@
|
||||
integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==
|
||||
|
||||
"@typescript-eslint/eslint-plugin@^2.26.0":
|
||||
version "2.29.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.29.0.tgz#c9efab7624e3dd6d144a0e4577a541d1bd42c2ac"
|
||||
integrity sha512-X/YAY7azKirENm4QRpT7OVmzok02cSkqeIcLmdz6gXUQG4Hk0Fi9oBAynSAyNXeGdMRuZvjBa0c1Lu0dn/u6VA==
|
||||
version "2.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.30.0.tgz#312a37e80542a764d96e8ad88a105316cdcd7b05"
|
||||
integrity sha512-PGejii0qIZ9Q40RB2jIHyUpRWs1GJuHP1pkoCiaeicfwO9z7Fx03NQzupuyzAmv+q9/gFNHu7lo1ByMXe8PNyg==
|
||||
dependencies:
|
||||
"@typescript-eslint/experimental-utils" "2.29.0"
|
||||
"@typescript-eslint/experimental-utils" "2.30.0"
|
||||
functional-red-black-tree "^1.0.1"
|
||||
regexpp "^3.0.0"
|
||||
tsutils "^3.17.1"
|
||||
|
||||
"@typescript-eslint/experimental-utils@2.29.0":
|
||||
version "2.29.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.29.0.tgz#3cb8060de9265ba131625a96bbfec31ba6d4a0fe"
|
||||
integrity sha512-H/6VJr6eWYstyqjWXBP2Nn1hQJyvJoFdDtsHxGiD+lEP7piGnGpb/ZQd+z1ZSB1F7dN+WsxUDh8+S4LwI+f3jw==
|
||||
"@typescript-eslint/experimental-utils@2.30.0":
|
||||
version "2.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.30.0.tgz#9845e868c01f3aed66472c561d4b6bac44809dd0"
|
||||
integrity sha512-L3/tS9t+hAHksy8xuorhOzhdefN0ERPDWmR9CclsIGOUqGKy6tqc/P+SoXeJRye5gazkuPO0cK9MQRnolykzkA==
|
||||
dependencies:
|
||||
"@types/json-schema" "^7.0.3"
|
||||
"@typescript-eslint/typescript-estree" "2.29.0"
|
||||
"@typescript-eslint/typescript-estree" "2.30.0"
|
||||
eslint-scope "^5.0.0"
|
||||
eslint-utils "^2.0.0"
|
||||
|
||||
"@typescript-eslint/parser@^2.26.0":
|
||||
version "2.29.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.29.0.tgz#6e3c4e21ed6393dc05b9d8b47f0b7e731ef21c9c"
|
||||
integrity sha512-H78M+jcu5Tf6m/5N8iiFblUUv+HJDguMSdFfzwa6vSg9lKR8Mk9BsgeSjO8l2EshKnJKcbv0e8IDDOvSNjl0EA==
|
||||
version "2.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.30.0.tgz#7681c305a6f4341ae2579f5e3a75846c29eee9ce"
|
||||
integrity sha512-9kDOxzp0K85UnpmPJqUzdWaCNorYYgk1yZmf4IKzpeTlSAclnFsrLjfwD9mQExctLoLoGAUXq1co+fbr+3HeFw==
|
||||
dependencies:
|
||||
"@types/eslint-visitor-keys" "^1.0.0"
|
||||
"@typescript-eslint/experimental-utils" "2.29.0"
|
||||
"@typescript-eslint/typescript-estree" "2.29.0"
|
||||
"@typescript-eslint/experimental-utils" "2.30.0"
|
||||
"@typescript-eslint/typescript-estree" "2.30.0"
|
||||
eslint-visitor-keys "^1.1.0"
|
||||
|
||||
"@typescript-eslint/typescript-estree@2.29.0":
|
||||
version "2.29.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.29.0.tgz#1be6612bb02fc37ac9f466521c1459a4744e8d3a"
|
||||
integrity sha512-3YGbtnWy4az16Egy5Fj5CckkVlpIh0MADtAQza+jiMADRSKkjdpzZp/5WuvwK/Qib3Z0HtzrDFeWanS99dNhnA==
|
||||
"@typescript-eslint/typescript-estree@2.30.0":
|
||||
version "2.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.30.0.tgz#1b8e848b55144270255ffbfe4c63291f8f766615"
|
||||
integrity sha512-nI5WOechrA0qAhnr+DzqwmqHsx7Ulr/+0H7bWCcClDhhWkSyZR5BmTvnBEyONwJCTWHfc5PAQExX24VD26IAVw==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
eslint-visitor-keys "^1.1.0"
|
||||
|
Loading…
x
Reference in New Issue
Block a user