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 : " " } \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 - z 0 - 9 \n ] / gmi, "" ) . replace ( / \s + / g, "" ) ;
86
+ const id = resource . id . replace ( / [ ^ a - z 0 - 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
+ }
0 commit comments