Skip to content

Commit 523fefb

Browse files
ljacobssonljacobsson
andauthored
Mermaid support (#139)
* mermaid support wip * feat: adding support for mermaid output * feat: adding support for mermaid output Co-authored-by: ljacobsson <[email protected]>
1 parent 68d047d commit 523fefb

File tree

7 files changed

+1527
-12
lines changed

7 files changed

+1527
-12
lines changed

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,25 @@ Renders a simple Ascii-art diagram of your template directly in the console. Use
130130
Video demo of using the `--watch` option:
131131
[![Demo of watch command](https://img.youtube.com/vi/2V3zimGWTcU/0.jpg)](https://www.youtube.com/watch?v=2V3zimGWTcU)
132132

133+
### Mermaid
134+
```
135+
Usage: cfn-dia mermaid|m [options]
136+
137+
Generates a mermaid graph from a template
138+
139+
Options:
140+
-t, --template-file [templateFile] Path to template or cdk.json file (default: "template.yaml or cdk.json")
141+
-all --render-all If set, all nested stacks will be rendered. By default only root template is rendered (default: false)
142+
-o, --output-path [outputPath] Name of output file
143+
-co, --cdk-output [cdkOutputPath] CDK synth output path (default: "cdk.out")
144+
-s, --skip-synth Skips CDK synth (default: false)
145+
-h, --help display help for command
146+
```
147+
148+
Renders a [mermaid](https://mermaid-js.github.io/mermaid/#/) diagram of your template directly in the console or to a file. Useful to gain a quick overview of smaller stacks and to generate as part of your CI/CD flow for up-to-date documentation.
149+
150+
![Demo](https://raw.githubusercontent.com/mhlabs/cfn-diagram/master/images/demo-mermaid.gif)
151+
133152

134153
### CI-mode
135154
This functionality lives in its own CLI, [cfn-diagram-ci](https://github.com/mhlabs/cfn-diagram-ci). This is beacuse it requires headless Chromium to be installed which makes the package size very large

commands/mermaid/index.js

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
const Vis = require("../../graph/Vis");
2+
const program = require("commander");
3+
const template = require("../../shared/templateParser");
4+
const fs = require("fs");
5+
program
6+
.command("mermaid")
7+
.alias("m")
8+
.option(
9+
"-t, --template-file [templateFile]",
10+
"Path to template or cdk.json file",
11+
"template.yaml or cdk.json"
12+
)
13+
.option("-all --render-all", "If set, all nested stacks will be rendered. By default only root template is rendered", false)
14+
.option(
15+
"-o, --output-path [outputPath]",
16+
"Name of output file"
17+
)
18+
.option("-co, --cdk-output [cdkOutputPath]", "CDK synth output path", `cdk.out`)
19+
.option("-s, --skip-synth", "Skips CDK synth", false)
20+
.description("Generates a mermaid graph from a template")
21+
.action(async (cmd) => {
22+
ciMode = cmd.ciMode;
23+
const templateObj = template.get(cmd);
24+
const graph = await Vis.makeGraph(
25+
templateObj.template,
26+
"root",
27+
false,
28+
cmd.renderAll
29+
);
30+
31+
const groups = {};
32+
for (const edge of graph.edges) {
33+
const owner = edge.from.split(".")[0];
34+
35+
if (edge.to.startsWith(`${owner}.`) && edge.from.startsWith(`${owner}.`)) {
36+
if (!groups[owner]) {
37+
groups[owner] = [];
38+
}
39+
groups[owner].push(edge);
40+
} else {
41+
if (!groups["crossgroup"]) {
42+
groups["crossgroup"] = [];
43+
}
44+
groups["crossgroup"].push(edge);
45+
}
46+
}
47+
const uniqueRelations = [];
48+
let mermaidString = `\`\`\`mermaid\n\tflowchart TB;\n`;
49+
for (const groupKey in groups) {
50+
const group = groups[groupKey];
51+
if (groupKey !== "crossgroup") {
52+
mermaidString += `\t\tsubgraph ${groupKey !== "root" ? groupKey : "&nbsp;"}\n`;
53+
}
54+
55+
mermaidString += `${group.map(p => {
56+
const fromResource = graph.nodes.find(n => n.id === p.from);
57+
const toResource = graph.nodes.find(n => n.id === p.to);
58+
59+
const from = createShape(fromResource);
60+
const to = createShape(toResource);
61+
const relation = `\t\t${from}-->${to}`;
62+
if (!uniqueRelations.includes(relation)) {
63+
uniqueRelations.push(relation);
64+
return relation;
65+
}
66+
}).filter(p => p).join("\n")}
67+
68+
`
69+
if (groupKey !== "crossgroup") {
70+
mermaidString += `\tend\n`;
71+
}
72+
73+
}
74+
75+
mermaidString += `\n\`\`\``;
76+
if (cmd.outputPath) {
77+
fs.writeFileSync(cmd.outputPath, mermaidString);
78+
console.log(`Wrote Mermaid diagram to ${cmd.outputPath}`);
79+
} else {
80+
console.log(mermaidString)
81+
}
82+
});
83+
84+
function createShape(resource, cmd) {
85+
const label = resource.label.replace(/[^a-z0-9\n]/gmi, "").replace(/\s+/g, "");
86+
const id = resource.id.replace(/[^a-z0-9\n]/gmi, "").replace(/\s+/g, "");;
87+
const type = resource.type.replace("AWS::", "");
88+
switch (resource.type) {
89+
case "AWS::Serverless::Function":
90+
case "AWS::Lambda::Function":
91+
return `${id}[[${label}<br/>${type}]]`;
92+
case "AWS::Serverless::SimpleTable":
93+
case "AWS::DynamoDB::Table":
94+
case "AWS::RDS::DBInstance":
95+
case "AWS::RDS::DBCluster":
96+
return `${id}[(${label}<br/>${type})]`;
97+
}
98+
return `${id}[${label}<br/>${type}]`;
99+
100+
}

graph/Vis.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,4 +227,5 @@ async function renderTemplate(
227227
module.exports = {
228228
renderTemplate,
229229
reset,
230+
makeGraph
230231
};

images/demo-mermaid.gif

321 KB
Loading

index.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ require("./commands/draw.io");
44
require("./commands/html");
55
require("./commands/asciiart");
66
require("./commands/browse");
7-
const package = require("./package.json");
8-
program.version(package.version, "-v, --vers", "output the current version");
7+
require("./commands/mermaid");
8+
const packag = require("./package.json");
9+
program.version(packag.version, "-v, --vers", "output the current version");
910
program.parse(process.argv);

0 commit comments

Comments
 (0)