From e63ce1f8ed54dfcc990d2e2dcd3300fb4fa0268f Mon Sep 17 00:00:00 2001 From: Timo Hocker Date: Fri, 24 Apr 2020 12:02:32 +0200 Subject: [PATCH] complete basic structures --- lib/Edge.ts | 5 +++++ lib/Element.ts | 17 +++++++++++++++ lib/Graph.ts | 55 ++++++++++++++++++++++++++++++++++++----------- lib/GraphNode.ts | 20 ----------------- lib/Node.ts | 33 ++++++++++++++++++++++------ lib/index.ts | 2 +- package.json | 9 ++++---- test/.eslintrc.js | 20 +++++++++++++++++ test/Edge.ts | 8 +++++++ test/Graph.ts | 35 ++++++++++++++++++++++++++++++ test/Node.ts | 44 +++++++++++++++++++++++++++++++++++++ tsconfig.json | 2 +- yarn.lock | 23 +++++++++++++------- 13 files changed, 221 insertions(+), 52 deletions(-) create mode 100644 lib/Element.ts delete mode 100644 lib/GraphNode.ts create mode 100644 test/.eslintrc.js create mode 100644 test/Edge.ts create mode 100644 test/Graph.ts create mode 100644 test/Node.ts diff --git a/lib/Edge.ts b/lib/Edge.ts index 759d910..a03a458 100644 --- a/lib/Edge.ts +++ b/lib/Edge.ts @@ -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}`; diff --git a/lib/Element.ts b/lib/Element.ts new file mode 100644 index 0000000..bc87ca4 --- /dev/null +++ b/lib/Element.ts @@ -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; + } +} diff --git a/lib/Graph.ts b/lib/Graph.ts index c3f0f45..4c75d1d 100644 --- a/lib/Graph.ts +++ b/lib/Graph.ts @@ -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 = []; - public nodes: Array = []; + public nodes: Array = []; public is_root = false; - public edges: Array; + public edges: Array = []; // eslint-disable-next-line @typescript-eslint/naming-convention - public toString (): string { - return `subgraph cluster_${this.full_name} { - ${this.children.map ((c) => c.toString ()) - .join ('\n')} - + public toString (level = 0): string { + return `subgraph cluster_${this.full_name} { + ${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}`) + ); } } diff --git a/lib/GraphNode.ts b/lib/GraphNode.ts deleted file mode 100644 index 3302574..0000000 --- a/lib/GraphNode.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Node } from './Node'; - -export class GraphNode extends Node { - public label: string; - public is_table: boolean; - public table_contents: Array>; - - private get serialized_table (): string { - const mapped_columns = this.table_contents - .map ((val) => `${val.join ('')}`); - return `${mapped_columns.join ('')}
`; - } - - // eslint-disable-next-line @typescript-eslint/naming-convention - public toString (): string { - return `${this.full_name}[label=<${this.is_table - ? this._serialized_table - : this.label}>]`; - } -} diff --git a/lib/Node.ts b/lib/Node.ts index 7f56a5a..bae6be9 100644 --- a/lib/Node.ts +++ b/lib/Node.ts @@ -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>; + + 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) => `${val.join ('')}`); + return `\n ${ + mapped_columns.join ('\n ') + }\n
`; } // 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}`; } } diff --git a/lib/index.ts b/lib/index.ts index 0f65973..75525de 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -1,4 +1,4 @@ export { Graph } from './Graph'; -export { GraphNode } from './GraphNode'; export { Node } from './Node'; +export { Element } from './Element'; export { Edge } from './Edge'; diff --git a/package.json b/package.json index 3fefb93..3e2c40d 100644 --- a/package.json +++ b/package.json @@ -2,18 +2,19 @@ "name": "graphviz-builder", "version": "1.0.0", "main": "index.js", - "author": "Timo Hocker ", + "author": "Timo Hocker ", "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" } } diff --git a/test/.eslintrc.js b/test/.eslintrc.js new file mode 100644 index 0000000..617eb4c --- /dev/null +++ b/test/.eslintrc.js @@ -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' + } +} diff --git a/test/Edge.ts b/test/Edge.ts new file mode 100644 index 0000000..1600cee --- /dev/null +++ b/test/Edge.ts @@ -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'); +}); diff --git a/test/Graph.ts b/test/Graph.ts new file mode 100644 index 0000000..025e151 --- /dev/null +++ b/test/Graph.ts @@ -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); +}); diff --git a/test/Node.ts b/test/Node.ts new file mode 100644 index 0000000..fee94b6 --- /dev/null +++ b/test/Node.ts @@ -0,0 +1,44 @@ +import test from 'ava'; +import { Node } from '../lib/Node'; + +const serialized_simple = 'bar_foo [label=]'; +const serialized_table = `bar_foo [label=< + + + +
foobarbaz
barbazfoo
bazfoobar
>]`; + +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); +}); diff --git a/tsconfig.json b/tsconfig.json index fb57a9c..0b7f8dc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -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. */ diff --git a/yarn.lock b/yarn.lock index bd79b48..64dceb1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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"