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