github.com/stackb/rules_proto@v0.0.0-20240221195024-5428336c51f1/pkg/rule/rules_nodejs/proto_ts_library.go (about) 1 package rules_nodejs 2 3 import ( 4 "flag" 5 "log" 6 "strings" 7 8 "github.com/bazelbuild/bazel-gazelle/config" 9 "github.com/bazelbuild/bazel-gazelle/label" 10 "github.com/bazelbuild/bazel-gazelle/resolve" 11 "github.com/bazelbuild/bazel-gazelle/rule" 12 13 "github.com/stackb/rules_proto/pkg/protoc" 14 ) 15 16 const ( 17 ProtoTsLibraryRuleName = "proto_ts_library" 18 ProtoTsLibraryRuleSuffix = "_ts_proto" 19 ) 20 21 func init() { 22 protoc.Rules().MustRegisterRule("stackb:rules_proto:proto_ts_library", &protoTsLibrary{}) 23 } 24 25 // protoTsLibrary implements LanguageRule for the 'proto_ts_library' rule from 26 // @rules_proto. 27 type protoTsLibrary struct{} 28 29 // Name implements part of the LanguageRule interface. 30 func (s *protoTsLibrary) Name() string { 31 return ProtoTsLibraryRuleName 32 } 33 34 // KindInfo implements part of the LanguageRule interface. 35 func (s *protoTsLibrary) KindInfo() rule.KindInfo { 36 return rule.KindInfo{ 37 MergeableAttrs: map[string]bool{ 38 "srcs": true, 39 "tsc": true, 40 "args": true, 41 "data": true, 42 "tsconfig": true, 43 "out_dir": true, 44 }, 45 NonEmptyAttrs: map[string]bool{ 46 "srcs": true, 47 }, 48 ResolveAttrs: map[string]bool{ 49 "deps": true, 50 }, 51 } 52 53 } 54 55 // LoadInfo implements part of the LanguageRule interface. 56 func (s *protoTsLibrary) LoadInfo() rule.LoadInfo { 57 return rule.LoadInfo{ 58 Name: "@build_stack_rules_proto//rules/ts:proto_ts_library.bzl", 59 Symbols: []string{ProtoTsLibraryRuleName}, 60 } 61 } 62 63 // ProvideRule implements part of the LanguageRule interface. 64 func (s *protoTsLibrary) ProvideRule(cfg *protoc.LanguageRuleConfig, pc *protoc.ProtocConfiguration) protoc.RuleProvider { 65 flags := parseProtoTsLibraryFlags(ProtoTsLibraryRuleName, cfg.GetOptions()) 66 67 outputs := make([]string, 0) 68 for _, out := range pc.Outputs { 69 if strings.HasSuffix(out, ".ts") { 70 outputs = append(outputs, out) 71 } 72 } 73 if len(outputs) == 0 { 74 return nil 75 } 76 return &tsLibrary{ 77 flags: flags, 78 KindName: ProtoTsLibraryRuleName, 79 RuleNameSuffix: ProtoTsLibraryRuleSuffix, 80 Outputs: outputs, 81 RuleConfig: cfg, 82 Config: pc, 83 } 84 } 85 86 // tsLibrary implements RuleProvider for 'ts_library'-like rules. 87 type tsLibrary struct { 88 flags *protoTsLibraryFlags 89 KindName string 90 RuleNameSuffix string 91 Outputs []string 92 Config *protoc.ProtocConfiguration 93 RuleConfig *protoc.LanguageRuleConfig 94 } 95 96 // Kind implements part of the ruleProvider interface. 97 func (s *tsLibrary) Kind() string { 98 return s.KindName 99 } 100 101 // Name implements part of the ruleProvider interface. 102 func (s *tsLibrary) Name() string { 103 return s.Config.Library.BaseName() + s.RuleNameSuffix 104 } 105 106 // Srcs computes the srcs list for the rule. 107 func (s *tsLibrary) Srcs() []string { 108 return s.Outputs 109 } 110 111 // Deps computes the deps list for the rule. 112 func (s *tsLibrary) Deps() []string { 113 return s.RuleConfig.GetDeps() 114 } 115 116 // Visibility provides visibility labels. 117 func (s *tsLibrary) Visibility() []string { 118 return s.RuleConfig.GetVisibility() 119 } 120 121 // Rule implements part of the ruleProvider interface. 122 func (s *tsLibrary) Rule(otherGen ...*rule.Rule) *rule.Rule { 123 newRule := rule.NewRule(s.Kind(), s.Name()) 124 125 newRule.SetAttr("srcs", s.Srcs()) 126 127 deps := s.Deps() 128 if len(deps) > 0 { 129 newRule.SetAttr("deps", deps) 130 } 131 132 args := s.RuleConfig.GetAttr("args") 133 if len(args) > 0 { 134 newRule.SetAttr("args", args) 135 } 136 137 tsconfig := s.RuleConfig.GetAttr("tsconfig") 138 if len(tsconfig) > 0 { 139 if len(tsconfig) > 1 { 140 log.Printf("warning (%s) found multiple entries for 'tsconfig', choosing last one: %v", s.Kind(), tsconfig) 141 } 142 newRule.SetAttr("tsconfig", tsconfig[len(tsconfig)-1]) 143 } 144 145 outdir := s.RuleConfig.GetAttr("out_dir") 146 if len(outdir) > 0 { 147 if len(outdir) > 1 { 148 log.Printf("warning (%s) found multiple entries for 'out_dir', choosing last one: %v", s.Kind(), outdir) 149 } 150 newRule.SetAttr("out_dir", outdir[len(outdir)-1]) 151 } 152 153 if s.flags.includeProtoLibraryData { 154 newRule.SetAttr("data", []string{s.Config.Library.Name()}) 155 } 156 157 visibility := s.Visibility() 158 if len(visibility) > 0 { 159 newRule.SetAttr("visibility", visibility) 160 } 161 162 return newRule 163 } 164 165 // Imports implements part of the RuleProvider interface. 166 func (s *tsLibrary) Imports(c *config.Config, r *rule.Rule, file *rule.File) []resolve.ImportSpec { 167 if lib, ok := r.PrivateAttr(protoc.ProtoLibraryKey).(protoc.ProtoLibrary); ok { 168 return protoc.ProtoLibraryImportSpecsForKind(r.Kind(), lib) 169 } 170 return nil 171 } 172 173 // Resolve implements part of the RuleProvider interface. 174 func (s *tsLibrary) Resolve(c *config.Config, ix *resolve.RuleIndex, r *rule.Rule, imports []string, from label.Label) { 175 protoc.ResolveDepsAttr("deps", false /* resolve wkts */)(c, ix, r, imports, from) 176 } 177 178 // protoTsLibraryFlags represents the parsed flag configuration for the 179 // proto_ts_library rule. 180 type protoTsLibraryFlags struct { 181 // includeProtoLibraryData is true if the rule should populate the data 182 // attribute with the proto_library rule. 183 includeProtoLibraryData bool 184 } 185 186 func parseProtoTsLibraryFlags(kindName string, args []string) *protoTsLibraryFlags { 187 flags := flag.NewFlagSet(kindName, flag.ExitOnError) 188 189 var includeProtoLibraryData bool 190 flags.BoolVar(&includeProtoLibraryData, "include_proto_library_data", false, "--include_proto_library_data=true populates the data attribute with the proto_library rule") 191 192 if err := flags.Parse(args); err != nil { 193 log.Fatalf("failed to parse flags for %q: %v", kindName, err) 194 } 195 196 return &protoTsLibraryFlags{includeProtoLibraryData} 197 }