@@ -2,6 +2,7 @@ package list
22
33import (
44 "context"
5+ "fmt"
56 "os"
67 "path/filepath"
78 "sort"
@@ -104,6 +105,8 @@ func Run(ctx context.Context, l log.Logger, opts *Options) error {
104105 return outputTree (l , opts , listedComponents , opts .Mode )
105106 case FormatLong :
106107 return outputLong (l , opts , listedComponents )
108+ case FormatDot :
109+ return outputDot (l , opts , listedComponents )
107110 default :
108111 // This should never happen, because of validation in the command.
109112 // If it happens, we want to throw so we can fix the validation.
@@ -131,10 +134,10 @@ func shouldDiscoverDependencies(opts *Options) bool {
131134type ListedComponents []* ListedComponent
132135
133136type ListedComponent struct {
134- Type component.Kind
135- Path string
136-
137+ Type component.Kind
138+ Path string
137139 Dependencies []* ListedComponent
140+ Excluded bool
138141}
139142
140143// Contains checks to see if the given path is in the listed components.
@@ -168,11 +171,17 @@ func discoveredToListed(components component.Components, opts *Options) (ListedC
168171 continue
169172 }
170173
174+ excluded := false
175+
171176 if opts .QueueConstructAs != "" {
172177 if unit , ok := c .(* component.Unit ); ok {
173178 if cfg := unit .Config (); cfg != nil && cfg .Exclude != nil {
174179 if cfg .Exclude .IsActionListed (opts .QueueConstructAs ) {
175- continue
180+ if opts .Format != FormatDot {
181+ continue
182+ }
183+
184+ excluded = true
176185 }
177186 }
178187 }
@@ -186,8 +195,9 @@ func discoveredToListed(components component.Components, opts *Options) (ListedC
186195 }
187196
188197 listedCfg := & ListedComponent {
189- Type : c .Kind (),
190- Path : relPath ,
198+ Type : c .Kind (),
199+ Path : relPath ,
200+ Excluded : excluded ,
191201 }
192202
193203 if len (c .Dependencies ()) == 0 {
@@ -206,9 +216,22 @@ func discoveredToListed(components component.Components, opts *Options) (ListedC
206216 continue
207217 }
208218
219+ depExcluded := false
220+
221+ if opts .QueueConstructAs != "" {
222+ if depUnit , ok := dep .(* component.Unit ); ok {
223+ if depCfg := depUnit .Config (); depCfg != nil && depCfg .Exclude != nil {
224+ if depCfg .Exclude .IsActionListed (opts .QueueConstructAs ) {
225+ depExcluded = true
226+ }
227+ }
228+ }
229+ }
230+
209231 listedCfg .Dependencies [i ] = & ListedComponent {
210- Type : dep .Kind (),
211- Path : relDepPath ,
232+ Type : dep .Kind (),
233+ Path : relDepPath ,
234+ Excluded : depExcluded ,
212235 }
213236 }
214237
@@ -442,6 +465,11 @@ func outputTree(l log.Logger, opts *Options, components ListedComponents, sort s
442465 return renderTree (opts , components , s , sort )
443466}
444467
468+ // outputDot outputs the discovered components in GraphViz DOT format.
469+ func outputDot (_ log.Logger , opts * Options , components ListedComponents ) error {
470+ return renderDot (opts , components )
471+ }
472+
445473type TreeStyler struct {
446474 entryStyle lipgloss.Style
447475 rootStyle lipgloss.Style
@@ -674,3 +702,73 @@ func getLongestPathLen(components ListedComponents) int {
674702
675703 return longest
676704}
705+
706+ // renderDot renders the components in GraphViz DOT format.
707+ func renderDot (opts * Options , components ListedComponents ) error {
708+ if _ , err := opts .Writer .Write ([]byte ("digraph {\n " )); err != nil {
709+ return errors .New (err )
710+ }
711+
712+ writtenNodes := make (map [string ]bool , len (components ))
713+
714+ for _ , component := range components {
715+ if ! writtenNodes [component .Path ] {
716+ style := ""
717+ if component .Excluded {
718+ style = "[color=red]"
719+ }
720+
721+ if _ , writeErr := opts .Writer .Write (
722+ fmt .Appendf (
723+ nil ,
724+ "\t \" %s\" %s;\n " ,
725+ component .Path ,
726+ style ,
727+ ),
728+ ); writeErr != nil {
729+ return errors .New (writeErr )
730+ }
731+
732+ writtenNodes [component .Path ] = true
733+ }
734+
735+ for _ , dep := range component .Dependencies {
736+ if ! writtenNodes [dep .Path ] {
737+ style := ""
738+ if dep .Excluded {
739+ style = "[color=red]"
740+ }
741+
742+ if _ , writeErr := opts .Writer .Write (
743+ fmt .Appendf (
744+ nil ,
745+ "\t \" %s\" %s;\n " ,
746+ dep .Path ,
747+ style ,
748+ ),
749+ ); writeErr != nil {
750+ return errors .New (writeErr )
751+ }
752+
753+ writtenNodes [dep .Path ] = true
754+ }
755+
756+ if _ , writeErr := opts .Writer .Write (
757+ fmt .Appendf (
758+ nil ,
759+ "\t \" %s\" -> \" %s\" ;\n " ,
760+ component .Path ,
761+ dep .Path ,
762+ ),
763+ ); writeErr != nil {
764+ return errors .New (writeErr )
765+ }
766+ }
767+ }
768+
769+ if _ , err := opts .Writer .Write ([]byte ("}\n " )); err != nil {
770+ return errors .New (err )
771+ }
772+
773+ return nil
774+ }
0 commit comments