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  }