-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
migrated graphviz and plantuml to rehype-code-hook-img
- Loading branch information
1 parent
dfc6a46
commit 6eed68e
Showing
9 changed files
with
132 additions
and
437 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,152 +1,14 @@ | ||
// @ts-expect-error The inferred type of '...' cannot be named without a reference to | ||
import type { Plugin } from "unified"; | ||
// @ts-expect-error The inferred type of '...' cannot be named without a reference to | ||
import type { Root } from "hast"; | ||
import { processGraphvizSvg } from "./graphviz.js"; | ||
import { rehypeCodeHook, type MapLike } from "@beoe/rehype-code-hook"; | ||
import { type Config as SvgoConfig } from "svgo"; | ||
import parse from "@beoe/fenceparser"; | ||
|
||
function processMeta(meta?: string): Record<string, any> { | ||
return meta ? parse(meta, { lowerCase: false }) : {}; | ||
} | ||
|
||
// it is possible to add other formats, like Graphology etc. | ||
type GraphFormat = "dagre" | "graphology"; | ||
|
||
// import { type Engine } from "@hpcc-js/wasm"; | ||
type Engine = | ||
| "circo" | ||
| "dot" | ||
| "fdp" | ||
| "sfdp" | ||
| "neato" | ||
| "osage" | ||
| "patchwork" | ||
| "twopi" | ||
| "nop" | ||
| "nop2"; | ||
|
||
export type RenderGraphvizOptions = { | ||
code: string; | ||
class?: string; | ||
/* it is possible to change layout with in dot code itself */ | ||
engine?: Engine; | ||
svgo?: SvgoConfig | boolean; | ||
dataGraph?: GraphFormat; | ||
}; | ||
|
||
// import { Graphviz } from "@hpcc-js/wasm"; | ||
// const graphviz = await Graphviz.load(); | ||
// export const renderGraphviz = (o: RenderGraphvizOptions) => | ||
// processGraphvizSvg( | ||
// graphviz.layout(o.code, "svg", o.engine || "dot"), | ||
// o.class | ||
// ); | ||
|
||
import { waitFor } from "@beoe/rehype-code-hook"; | ||
/** | ||
* If all graphviz diagrams are cached it would not even load module in memory. | ||
* If there are diagrams, it would load module and first few renders would be async, | ||
* but all consequent renders would be sync | ||
*/ | ||
export const renderGraphviz = waitFor( | ||
async () => { | ||
// @ts-ignore | ||
const Graphviz = (await import("@hpcc-js/wasm")).Graphviz; | ||
return await Graphviz.load(); | ||
}, | ||
(graphviz) => (o: RenderGraphvizOptions) => { | ||
let code = o.code; | ||
let graph; | ||
if (o.dataGraph) { | ||
// without this I can't get consistent ids for JSON and SVG outputs | ||
code = graphviz.unflatten(code); | ||
code = graphviz.nop(code); | ||
|
||
const obj = JSON.parse( | ||
graphviz.layout(code, "dot_json", o.engine || "dot") | ||
); | ||
|
||
if (o.dataGraph === "graphology") { | ||
graph = { | ||
attributes: { name: "g" }, | ||
options: { | ||
allowSelfLoops: true, | ||
multi: true, | ||
type: obj.directed ? "directed" : "mixed", | ||
}, | ||
nodes: obj.objects.map((node: any) => ({ | ||
key: node._gvid + 1, | ||
})), | ||
edges: obj.edges.map((edge: any) => ({ | ||
key: edge._gvid + 1, | ||
source: edge.tail + 1, | ||
target: edge.head + 1, | ||
})), | ||
}; | ||
} | ||
|
||
if (o.dataGraph === "dagre") { | ||
graph = { | ||
options: { | ||
directed: obj.directed, | ||
multigraph: true, | ||
compound: false, | ||
}, | ||
nodes: obj.objects.map((node: any) => ({ | ||
v: node.unique_id + 1, | ||
})), | ||
edges: obj.edges.map((edge: any) => ({ | ||
v: edge.tail + 1, | ||
w: edge.head + 1, | ||
name: edge._gvid + 1, | ||
})), | ||
}; | ||
} | ||
} | ||
|
||
return processGraphvizSvg( | ||
graphviz.layout(code, "svg", o.engine || "dot"), | ||
o.class, | ||
o.svgo, | ||
graph | ||
); | ||
} | ||
); | ||
|
||
export { processGraphvizSvg }; | ||
|
||
export type RehypeGraphvizConfig = { | ||
cache?: MapLike; | ||
class?: string; | ||
svgo?: SvgoConfig | boolean; | ||
// adds data-graph to each figure with JSON representation of graph | ||
dataGraph?: GraphFormat; | ||
}; | ||
|
||
export const rehypeGraphviz: Plugin<[RehypeGraphvizConfig?], Root> = ( | ||
options = {} | ||
) => { | ||
const salt = { class: options.class, svgo: options.svgo }; | ||
// @ts-expect-error | ||
return rehypeCodeHook({ | ||
...options, | ||
salt, | ||
language: "dot", | ||
code: ({ code, meta }) => { | ||
const metaOptions = processMeta(meta); | ||
const dataGraph = | ||
metaOptions.datagraph !== undefined | ||
? metaOptions.datagraph | ||
: options.dataGraph; | ||
const cssClass = `${options.class || ""} ${ | ||
metaOptions.class || "" | ||
}`.trim(); | ||
const svgo = | ||
metaOptions.svgo !== undefined ? metaOptions.svgo : options.svgo; | ||
|
||
return renderGraphviz({ code, class: cssClass, svgo, dataGraph }); | ||
}, | ||
}); | ||
}; | ||
import { rehypeCodeHookImg } from "@beoe/rehype-code-hook-img"; | ||
import { RehypeGraphvizConfig, renderGraphviz } from "./render.js"; | ||
|
||
export const rehypeGraphviz = rehypeCodeHookImg<RehypeGraphvizConfig>({ | ||
language: "dot", | ||
class: "graphviz", | ||
render: (code, options) => renderGraphviz({ ...options, code }), | ||
}); | ||
|
||
export default rehypeGraphviz; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
/** | ||
* it is possible to add other formats | ||
*/ | ||
export type DataFormat = "dagre" | "graphology"; | ||
|
||
// import { type Engine } from "@hpcc-js/wasm"; | ||
type Engine = | ||
| "circo" | ||
| "dot" | ||
| "fdp" | ||
| "sfdp" | ||
| "neato" | ||
| "osage" | ||
| "patchwork" | ||
| "twopi" | ||
| "nop" | ||
| "nop2"; | ||
|
||
export type RehypeGraphvizConfig = { | ||
dataGraph: DataFormat; | ||
engine: Engine; | ||
}; | ||
|
||
type RenderGraphvizOptions = RehypeGraphvizConfig & { | ||
code: string; | ||
}; | ||
|
||
import { waitFor } from "@beoe/rehype-code-hook"; | ||
/** | ||
* If all graphviz diagrams are cached it would not even load module in memory. | ||
* If there are diagrams, it would load module and first few renders would be async, | ||
* but all consequent renders would be sync | ||
*/ | ||
export const renderGraphviz = waitFor( | ||
async () => { | ||
// @ts-ignore | ||
const Graphviz = (await import("@hpcc-js/wasm")).Graphviz; | ||
return await Graphviz.load(); | ||
}, | ||
(graphviz) => | ||
({ code, engine, dataGraph }: RenderGraphvizOptions) => { | ||
let data; | ||
if (dataGraph) { | ||
// without this I can't get consistent ids for JSON and SVG outputs | ||
code = graphviz.unflatten(code); | ||
code = graphviz.nop(code); | ||
|
||
const obj = JSON.parse( | ||
graphviz.layout(code, "dot_json", engine || "dot") | ||
); | ||
|
||
if (dataGraph === "graphology") { | ||
data = { | ||
attributes: { name: "g" }, | ||
options: { | ||
allowSelfLoops: true, | ||
multi: true, | ||
type: obj.directed ? "directed" : "mixed", | ||
}, | ||
nodes: obj.objects.map((node: any) => ({ | ||
key: node._gvid + 1, | ||
})), | ||
edges: obj.edges.map((edge: any) => ({ | ||
key: edge._gvid + 1, | ||
source: edge.tail + 1, | ||
target: edge.head + 1, | ||
})), | ||
}; | ||
} | ||
|
||
if (dataGraph === "dagre") { | ||
data = { | ||
options: { | ||
directed: obj.directed, | ||
multigraph: true, | ||
compound: false, | ||
}, | ||
nodes: obj.objects.map((node: any) => ({ | ||
v: node.unique_id + 1, | ||
})), | ||
edges: obj.edges.map((edge: any) => ({ | ||
v: edge.tail + 1, | ||
w: edge.head + 1, | ||
name: edge._gvid + 1, | ||
})), | ||
}; | ||
} | ||
} | ||
|
||
return { | ||
svg: cleanup(graphviz.layout(code, "svg", engine || "dot")), | ||
data, | ||
}; | ||
} | ||
); | ||
|
||
/** | ||
* removes `<?xml version="1.0" encoding="UTF-8" standalone="no"?>` | ||
* removes `<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"` | ||
*/ | ||
const cleanup = (svg: string) => svg.split("\n").slice(6).join("\n"); |
Oops, something went wrong.