@@ -20,6 +20,7 @@ import (
2020type MermaidWriter struct {
2121 MinEdgeName string
2222 SpecifiedPackageName string
23+ UseV0Semantics bool
2324}
2425
2526type MermaidOption func (* MermaidWriter )
@@ -52,6 +53,12 @@ func WithSpecifiedPackageName(specifiedPackageName string) MermaidOption {
5253 }
5354}
5455
56+ func WithV0Semantics () MermaidOption {
57+ return func (o * MermaidWriter ) {
58+ o .UseV0Semantics = true
59+ }
60+ }
61+
5562// writes out the channel edges of the declarative config graph in a mermaid format capable of being pasted into
5663// mermaid renderers like github, mermaid.live, etc.
5764// output is sorted lexicographically by package name, and then by channel name
@@ -125,6 +132,8 @@ func (writer *MermaidWriter) WriteChannels(cfg DeclarativeConfig, out io.Writer)
125132
126133 var deprecatedPackage string
127134 deprecatedChannels := []string {}
135+ linkID := 0
136+ skippedLinkIDs := []string {}
128137
129138 for _ , c := range cfg .Channels {
130139 filteredChannel := writer .filterChannel (& c , versionMap , minVersion , minEdgePackage )
@@ -137,8 +146,8 @@ func (writer *MermaidWriter) WriteChannels(cfg DeclarativeConfig, out io.Writer)
137146 }
138147
139148 channelID := fmt .Sprintf ("%s-%s" , filteredChannel .Package , filteredChannel .Name )
140- pkgBuilder . WriteString ( fmt .Sprintf ( " %%%% channel %q\n " , filteredChannel .Name ) )
141- pkgBuilder . WriteString ( fmt .Sprintf ( " subgraph %s[%q]\n " , channelID , filteredChannel .Name ) )
149+ fmt .Fprintf ( pkgBuilder , " %%%% channel %q\n " , filteredChannel .Name )
150+ fmt .Fprintf ( pkgBuilder , " subgraph %s[%q]\n " , channelID , filteredChannel .Name )
142151
143152 if depByPackage .Has (filteredChannel .Package ) {
144153 deprecatedPackage = filteredChannel .Package
@@ -148,47 +157,79 @@ func (writer *MermaidWriter) WriteChannels(cfg DeclarativeConfig, out io.Writer)
148157 deprecatedChannels = append (deprecatedChannels , channelID )
149158 }
150159
151- for _ , ce := range filteredChannel .Entries {
152- if versionMap [ce .Name ].GE (minVersion ) {
153- bundleDeprecation := ""
154- if depByBundle .Has (ce .Name ) {
155- bundleDeprecation = ":::deprecated"
156- }
160+ // sort edges by decreasing version
161+ sortedEntries := make ([]* ChannelEntry , 0 , len (filteredChannel .Entries ))
162+ for i := range filteredChannel .Entries {
163+ sortedEntries = append (sortedEntries , & filteredChannel .Entries [i ])
164+ }
165+ sort .Slice (sortedEntries , func (i , j int ) bool {
166+ // Sort by decreasing version: greater version comes first
167+ return versionMap [sortedEntries [i ].Name ].GT (versionMap [sortedEntries [j ].Name ])
168+ })
169+
170+ skippedEntities := sets.Set [string ]{}
171+
172+ for _ , ce := range sortedEntries {
173+ bundleDecoration := ""
174+ switch {
175+ case depByBundle .Has (ce .Name ) && skippedEntities .Has (ce .Name ):
176+ bundleDecoration = ":::depandskip"
177+ case depByBundle .Has (ce .Name ):
178+ bundleDecoration = ":::deprecated"
179+ case skippedEntities .Has (ce .Name ):
180+ bundleDecoration = ":::skipped"
181+ }
157182
158- entryID := fmt .Sprintf ("%s-%s" , channelID , ce .Name )
159- pkgBuilder . WriteString ( fmt .Sprintf ( " %s[%q]%s\n " , entryID , ce .Name , bundleDeprecation ) )
183+ entryID := fmt .Sprintf ("%s-%s" , channelID , ce .Name )
184+ fmt .Fprintf ( pkgBuilder , " %s[%q]%s\n " , entryID , ce .Name , bundleDecoration )
160185
161- if len (ce .Replaces ) > 0 {
162- replacesID := fmt . Sprintf ( "%s-%s" , channelID , ce .Replaces )
163- pkgBuilder . WriteString ( fmt .Sprintf (" %s[%q]-- %s --> %s[%q] \n " , replacesID , ce . Replaces , "replace" , entryID , ce . Name ) )
164- }
165- if len ( ce . Skips ) > 0 {
166- for _ , s := range ce . Skips {
167- skipsID := fmt . Sprintf ( "%s-%s" , channelID , s )
168- pkgBuilder . WriteString ( fmt . Sprintf ( " %s[%q]-- %s --> %s[%q] \n " , skipsID , s , "skip" , entryID , ce . Name ) )
186+ if len (ce .Skips ) > 0 {
187+ for _ , s := range ce .Skips {
188+ skipsID := fmt .Sprintf ("%s-%s " , channelID , s )
189+ fmt . Fprintf ( pkgBuilder , " %s[%q]-- %s --> %s[%q] \n " , skipsID , s , "skip" , entryID , ce . Name )
190+ if skippedEntities . Has ( s ) {
191+ skippedLinkIDs = append ( skippedLinkIDs , fmt . Sprintf ( "%d" , linkID ))
192+ } else {
193+ skippedEntities . Insert ( s )
169194 }
195+ linkID ++
170196 }
171- if len (ce .SkipRange ) > 0 {
172- skipRange , err := semver .ParseRange (ce .SkipRange )
173- if err == nil {
174- for _ , edgeName := range filteredChannel .Entries {
175- if skipRange (versionMap [edgeName .Name ]) {
176- skipRangeID := fmt .Sprintf ("%s-%s" , channelID , edgeName .Name )
177- pkgBuilder .WriteString (fmt .Sprintf (" %s[%q]-- \" %s(%s)\" --> %s[%q]\n " , skipRangeID , edgeName .Name , "skipRange" , ce .SkipRange , entryID , ce .Name ))
197+ }
198+ if len (ce .SkipRange ) > 0 {
199+ skipRange , err := semver .ParseRange (ce .SkipRange )
200+ if err == nil {
201+ for _ , edgeName := range filteredChannel .Entries {
202+ if skipRange (versionMap [edgeName .Name ]) {
203+ skipRangeID := fmt .Sprintf ("%s-%s" , channelID , edgeName .Name )
204+ fmt .Fprintf (pkgBuilder , " %s[%q]-- \" %s(%s)\" --> %s[%q]\n " , skipRangeID , edgeName .Name , "skipRange" , ce .SkipRange , entryID , ce .Name )
205+ if skippedEntities .Has (ce .Name ) {
206+ skippedLinkIDs = append (skippedLinkIDs , fmt .Sprintf ("%d" , linkID ))
178207 }
208+ linkID ++
179209 }
180- } else {
181- fmt .Fprintf (os .Stderr , "warning: ignoring invalid SkipRange for package/edge %q/%q: %v\n " , c .Package , ce .Name , err )
182210 }
211+ } else {
212+ fmt .Fprintf (os .Stderr , "warning: ignoring invalid SkipRange for package/edge %q/%q: %v\n " , c .Package , ce .Name , err )
183213 }
184214 }
215+ // have to process replaces last, because applicablity can be impacted by skips
216+ if len (ce .Replaces ) > 0 {
217+ replacesID := fmt .Sprintf ("%s-%s" , channelID , ce .Replaces )
218+ fmt .Fprintf (pkgBuilder , " %s[%q]-- %s --> %s[%q]\n " , replacesID , ce .Replaces , "replace" , entryID , ce .Name )
219+ if skippedEntities .Has (ce .Name ) {
220+ skippedLinkIDs = append (skippedLinkIDs , fmt .Sprintf ("%d" , linkID ))
221+ }
222+ linkID ++
223+ }
185224 }
186- pkgBuilder . WriteString ( " end\n " )
225+ fmt . Fprintf ( pkgBuilder , " end\n " )
187226 }
188227 }
189228
190229 _ , _ = out .Write ([]byte ("graph LR\n " ))
191230 _ , _ = out .Write ([]byte (" classDef deprecated fill:#E8960F\n " ))
231+ _ , _ = out .Write ([]byte (" classDef skipped stroke:#FF0000,stroke-width:4px\n " ))
232+ _ , _ = out .Write ([]byte (" classDef depandskip fill:#E8960,stroke:#FF0000,stroke-width:4px\n " ))
192233 pkgNames := []string {}
193234 for pname := range pkgs {
194235 pkgNames = append (pkgNames , pname )
@@ -213,6 +254,10 @@ func (writer *MermaidWriter) WriteChannels(cfg DeclarativeConfig, out io.Writer)
213254 }
214255 }
215256
257+ if len (skippedLinkIDs ) > 0 {
258+ _ , _ = out .Write ([]byte ("linkStyle " + strings .Join (skippedLinkIDs , "," ) + " stroke:#FF0000,stroke-width:3px,stroke-dasharray:5;" ))
259+ }
260+
216261 return nil
217262}
218263
0 commit comments