github.com/stackb/rules_proto@v0.0.0-20240221195024-5428336c51f1/pkg/plugin/stephenh/ts-proto/protoc-gen-ts-proto.go (about) 1 package ts_proto 2 3 import ( 4 "flag" 5 "log" 6 "path" 7 "path/filepath" 8 "strings" 9 10 "github.com/bazelbuild/bazel-gazelle/label" 11 "github.com/stackb/rules_proto/pkg/protoc" 12 ) 13 14 func init() { 15 protoc.Plugins().MustRegisterPlugin(&ProtocGenTsProto{}) 16 } 17 18 // ProtocGenTsProto implements Plugin for the built-in protoc js/library plugin. 19 type ProtocGenTsProto struct{} 20 21 // Name implements part of the Plugin interface. 22 func (p *ProtocGenTsProto) Name() string { 23 return "stephenh:ts-proto:protoc-gen-ts-proto" 24 } 25 26 // Configure implements part of the Plugin interface. 27 func (p *ProtocGenTsProto) Configure(ctx *protoc.PluginContext) *protoc.PluginConfiguration { 28 flags := parseProtocGenTsProtoOptions(p.Name(), ctx.PluginConfig.GetFlags()) 29 imports := make(map[string]bool) 30 for _, file := range ctx.ProtoLibrary.Files() { 31 for _, imp := range file.Imports() { 32 imports[imp.Filename] = true 33 } 34 } 35 var emitImportedFiles bool 36 var options []string 37 for _, option := range ctx.PluginConfig.GetOptions() { 38 // options may be configured to include many "M" options, but only 39 // include the relevant ones to avoid BUILD file clutter. 40 if strings.HasPrefix(option, "M") { 41 keyVal := option[len("M"):] 42 parts := strings.SplitN(keyVal, "=", 2) 43 filename := parts[0] 44 if !imports[filename] { 45 continue 46 } 47 } 48 if option == "emitImportedFiles=true" { 49 emitImportedFiles = true 50 } 51 options = append(options, option) 52 } 53 54 tsFiles := make([]string, 0) 55 for _, file := range ctx.ProtoLibrary.Files() { 56 if emitImportedFiles { 57 for _, imp := range file.Imports() { 58 if !strings.HasPrefix(imp.Filename, "google/protobuf") { 59 continue 60 } 61 tsFiles = append(tsFiles, strings.TrimSuffix(imp.Filename, ".proto")+".ts") 62 } 63 } 64 65 tsFile := file.Name + ".ts" 66 if flags.excludeOutput[filepath.Base(tsFile)] { 67 continue 68 } 69 if ctx.Rel != "" { 70 tsFile = path.Join(ctx.Rel, tsFile) 71 } 72 tsFiles = append(tsFiles, tsFile) 73 } 74 75 pc := &protoc.PluginConfiguration{ 76 Label: label.New("build_stack_rules_proto", "plugin/stephenh/ts-proto", "protoc-gen-ts-proto"), 77 Outputs: protoc.DeduplicateAndSort(tsFiles), 78 Options: protoc.DeduplicateAndSort(options), 79 } 80 if len(pc.Outputs) == 0 { 81 pc.Outputs = nil 82 } 83 return pc 84 } 85 86 // protocGenTsProtoOptions represents the parsed flag configuration for the 87 // ProtocGenTsProto implementation. 88 type protocGenTsProtoOptions struct { 89 excludeOutput map[string]bool 90 } 91 92 func parseProtocGenTsProtoOptions(kindName string, args []string) *protocGenTsProtoOptions { 93 flags := flag.NewFlagSet(kindName, flag.ExitOnError) 94 95 var excludeOutput string 96 flags.StringVar(&excludeOutput, "exclude_output", "", "--exclude_output=foo.ts suppresses the file 'foo.ts' from the output list") 97 98 if err := flags.Parse(args); err != nil { 99 log.Fatalf("failed to parse flags for %q: %v", kindName, err) 100 } 101 config := &protocGenTsProtoOptions{ 102 excludeOutput: make(map[string]bool), 103 } 104 for _, value := range strings.Split(excludeOutput, ",") { 105 config.excludeOutput[value] = true 106 } 107 108 return config 109 }