@thi.ng/rstream-dot
Version:
Graphviz DOT conversion of @thi.ng/rstream dataflow graph topologies
88 lines (87 loc) • 2.8 kB
JavaScript
import { StreamMerge } from "@thi.ng/rstream/merge";
import { Stream } from "@thi.ng/rstream/stream";
import { StreamSync } from "@thi.ng/rstream/sync";
import { truncate } from "@thi.ng/strings/truncate";
import { map } from "@thi.ng/transducers/map";
export * from "./api.js";
const __getNodeType = (sub) => sub instanceof Stream ? "Stream" : sub instanceof StreamSync ? "StreamSync" : sub instanceof StreamMerge ? "StreamMerge" : void 0;
const __getChildren = (sub) => {
let children = [];
if (sub.subs) children.push(...sub.subs);
if (sub.__owner) children.push(sub.__owner);
if (sub.wrapped) children.push(...__getChildren(sub.wrapped));
return children;
};
const __dotNode = (s, opts) => {
let res = `s${s.id}[label="`;
res += s.type ? `${s.label}\\n(${s.type})` : `${s.label}`;
if (s.body !== void 0) {
res += `\\n${s.body.replace(/"/g, `'`).replace(/\n/g, "\\n")}`;
}
res += `", color="`;
res += s.type && opts.color[s.type.toLowerCase()] || (s.label === "<noid>" ? opts.color.noid : opts.color.default);
return res + `"];`;
};
const __dotEdge = (a, b, _) => `s${a.id} -> s${b.id}${b.xform ? `[label="xform"]` : ""};`;
const __subValue = (sub) => {
const res = JSON.stringify(sub.deref ? sub.deref() : void 0);
return res ? truncate(64, "...")(res) : res;
};
const traverse = (subs, opts, state) => {
opts || (opts = {});
state || (state = { id: 0, subs: /* @__PURE__ */ new Map(), rels: [] });
for (let sub of subs) {
if (state.subs.get(sub)) return state;
const id = state.id;
const desc = {
id,
label: sub.id || "<noid>",
type: __getNodeType(sub),
xform: !!sub.xform,
body: opts.values ? __subValue(sub) : void 0
};
state.subs.set(sub, desc);
state.id++;
const children = __getChildren(sub);
if (children.length) {
traverse(children, opts, state);
for (let c of children) {
const childNode = state.subs.get(c);
childNode && state.rels.push([desc, childNode]);
}
}
}
return state;
};
const toDot = (state, opts) => {
opts = {
dir: "LR",
font: "sans-serif",
fontsize: 10,
text: "white",
...opts
};
opts.color = {
default: "black",
noid: "gray",
stream: "blue",
streammerge: "red",
streamsync: "red",
...opts.color
};
return [
"digraph g {",
`rankdir=${opts.dir};`,
`node[fontname="${opts.font}",fontsize=${opts.fontsize},style=filled,fontcolor=${opts.text}];`,
`edge[fontname="${opts.font}",fontsize=${opts.fontsize}];`,
...map((n) => __dotNode(n, opts), state.subs.values()),
...map((r) => __dotEdge(r[0], r[1], opts), state.rels),
"}"
].join("\n");
};
const serialize = (subs, opts) => toDot(traverse(subs, opts), opts);
export {
serialize,
toDot,
traverse
};