Skip to content

Commit 9fffedb

Browse files
authored
feat: introduce bufbuild/connect-es (#338)
* feat: add bufbuild es-connect * show working example again * feat: add gazelle extension plugin for bufbuild * fix: gazelle extension creates both plugins * chore: set options on generated bufbuild rules * chore: restore original example which conflicts with bufbuild/connect usage * chore: minify delta
1 parent 8296592 commit 9fffedb

15 files changed

+476
-276
lines changed

.bazelignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
docs/
22
node_modules
3+
plugin/bufbuild/node_modules
34
plugin/stephenh/ts-proto/node_modules
45
rules/ts/node_modules

language/protobuf/BUILD.bazel

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ go_library(
77
visibility = ["//visibility:public"],
88
deps = [
99
"//pkg/language/protobuf",
10+
"//pkg/plugin/bufbuild",
1011
"//pkg/plugin/builtin",
1112
"//pkg/plugin/gogo/protobuf",
1213
"//pkg/plugin/golang/protobuf",

language/protobuf/protobuf.go

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
_ "github.com/stackb/rules_proto/pkg/plugin/grpcecosystem/grpcgateway"
1717
_ "github.com/stackb/rules_proto/pkg/plugin/scalapb/scalapb"
1818
_ "github.com/stackb/rules_proto/pkg/plugin/stackb/grpc_js"
19+
_ "github.com/stackb/rules_proto/pkg/plugin/bufbuild"
1920
_ "github.com/stackb/rules_proto/pkg/plugin/stephenh/ts-proto"
2021
_ "github.com/stackb/rules_proto/pkg/rule/rules_cc"
2122
_ "github.com/stackb/rules_proto/pkg/rule/rules_closure"

package.json

-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
{
2-
"dependencies": {
3-
"ts-proto": "1.156.0"
4-
},
52
"devDependencies": {
63
"@nestjs/common": "10.1.3",
74
"@nestjs/core": "10.1.3",

pkg/BUILD.bazel

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ filegroup(
44
srcs = [
55
"//pkg/language/protobuf:all_files",
66
"//pkg/plugin/akka/akka_grpc:all_files",
7+
"//pkg/plugin/bufbuild:all_files",
78
"//pkg/plugin/builtin:all_files",
89
"//pkg/plugin/gogo/protobuf:all_files",
910
"//pkg/plugin/golang/protobuf:all_files",

pkg/plugin/bufbuild/BUILD.bazel

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
2+
3+
go_library(
4+
name = "bufbuild",
5+
srcs = [
6+
"connect_es_plugin.go",
7+
"es_plugin.go",
8+
],
9+
importpath = "github.com/stackb/rules_proto/pkg/plugin/bufbuild",
10+
visibility = ["//visibility:public"],
11+
deps = [
12+
"//pkg/protoc",
13+
"@bazel_gazelle//label:go_default_library",
14+
],
15+
)
16+
17+
go_test(
18+
name = "bufbuild_test",
19+
srcs = ["es_plugin_test.go"],
20+
deps = [
21+
":bufbuild",
22+
"//pkg/plugintest",
23+
],
24+
)
25+
26+
filegroup(
27+
name = "all_files",
28+
testonly = True,
29+
srcs = [
30+
"BUILD.bazel",
31+
] + glob(["*.go"]),
32+
visibility = ["//pkg:__pkg__"],
33+
)
+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package bufbuild
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(&ConnectEsProto{})
16+
}
17+
18+
// ConnectEsProto implements Plugin for the bufbuild/connect-es plugin.
19+
type ConnectEsProto struct{}
20+
21+
// Name implements part of the Plugin interface.
22+
func (p *ConnectEsProto) Name() string {
23+
return "bufbuild:connect-es"
24+
}
25+
26+
// Configure implements part of the Plugin interface.
27+
func (p *ConnectEsProto) Configure(ctx *protoc.PluginContext) *protoc.PluginConfiguration {
28+
flags := parseConnectEsProtoOptions(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+
// TODO: get target option from directive
36+
var options = []string{"keep_empty_files=true", "target=ts"}
37+
tsFiles := make([]string, 0)
38+
for _, file := range ctx.ProtoLibrary.Files() {
39+
// TODO: outputs should be conditional on which target= value is used
40+
tsFile := file.Name + "_connect.ts"
41+
if flags.excludeOutput[filepath.Base(tsFile)] {
42+
continue
43+
}
44+
if ctx.Rel != "" {
45+
tsFile = path.Join(ctx.Rel, tsFile)
46+
}
47+
tsFiles = append(tsFiles, tsFile)
48+
}
49+
50+
pc := &protoc.PluginConfiguration{
51+
Label: label.New("build_stack_rules_proto", "plugin/bufbuild", "connect-es"),
52+
Outputs: protoc.DeduplicateAndSort(tsFiles),
53+
Options: protoc.DeduplicateAndSort(options),
54+
}
55+
if len(pc.Outputs) == 0 {
56+
pc.Outputs = nil
57+
}
58+
return pc
59+
}
60+
61+
// ConnectEsProtoOptions represents the parsed flag configuration for the
62+
// ConnectEsProto implementation.
63+
type ConnectEsProtoOptions struct {
64+
excludeOutput map[string]bool
65+
}
66+
67+
func parseConnectEsProtoOptions(kindName string, args []string) *ConnectEsProtoOptions {
68+
flags := flag.NewFlagSet(kindName, flag.ExitOnError)
69+
70+
var excludeOutput string
71+
flags.StringVar(&excludeOutput, "exclude_output", "", "--exclude_output=foo.ts suppresses the file 'foo.ts' from the output list")
72+
73+
if err := flags.Parse(args); err != nil {
74+
log.Fatalf("failed to parse flags for %q: %v", kindName, err)
75+
}
76+
config := &ConnectEsProtoOptions{
77+
excludeOutput: make(map[string]bool),
78+
}
79+
for _, value := range strings.Split(excludeOutput, ",") {
80+
config.excludeOutput[value] = true
81+
}
82+
83+
return config
84+
}

pkg/plugin/bufbuild/es_plugin.go

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package bufbuild
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(&EsProto{})
16+
}
17+
18+
// EsProto implements Plugin for the bufbuild/protoc-gen-es plugin.
19+
type EsProto struct{}
20+
21+
// Name implements part of the Plugin interface.
22+
func (p *EsProto) Name() string {
23+
return "bufbuild:es"
24+
}
25+
26+
// Configure implements part of the Plugin interface.
27+
func (p *EsProto) Configure(ctx *protoc.PluginContext) *protoc.PluginConfiguration {
28+
flags := parseEsProtoOptions(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+
// TODO: get target option from directive
36+
var options = []string{"keep_empty_files=true", "target=ts"}
37+
tsFiles := make([]string, 0)
38+
for _, file := range ctx.ProtoLibrary.Files() {
39+
// TODO: outputs should be conditional on which target= value is used
40+
tsFile := file.Name + "_pb.ts"
41+
if flags.excludeOutput[filepath.Base(tsFile)] {
42+
continue
43+
}
44+
if ctx.Rel != "" {
45+
tsFile = path.Join(ctx.Rel, tsFile)
46+
}
47+
tsFiles = append(tsFiles, tsFile)
48+
}
49+
50+
pc := &protoc.PluginConfiguration{
51+
Label: label.New("build_stack_rules_proto", "plugin/bufbuild", "es"),
52+
Outputs: protoc.DeduplicateAndSort(tsFiles),
53+
Options: protoc.DeduplicateAndSort(options),
54+
}
55+
if len(pc.Outputs) == 0 {
56+
pc.Outputs = nil
57+
}
58+
return pc
59+
}
60+
61+
// EsProtoOptions represents the parsed flag configuration for the
62+
// EsProto implementation.
63+
type EsProtoOptions struct {
64+
excludeOutput map[string]bool
65+
}
66+
67+
func parseEsProtoOptions(kindName string, args []string) *EsProtoOptions {
68+
flags := flag.NewFlagSet(kindName, flag.ExitOnError)
69+
70+
var excludeOutput string
71+
flags.StringVar(&excludeOutput, "exclude_output", "", "--exclude_output=foo.ts suppresses the file 'foo.ts' from the output list")
72+
73+
if err := flags.Parse(args); err != nil {
74+
log.Fatalf("failed to parse flags for %q: %v", kindName, err)
75+
}
76+
config := &EsProtoOptions{
77+
excludeOutput: make(map[string]bool),
78+
}
79+
for _, value := range strings.Split(excludeOutput, ",") {
80+
config.excludeOutput[value] = true
81+
}
82+
83+
return config
84+
}

pkg/plugin/bufbuild/es_plugin_test.go

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package bufbuild_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stackb/rules_proto/pkg/plugin/bufbuild"
7+
"github.com/stackb/rules_proto/pkg/plugintest"
8+
)
9+
10+
func TestProtocGenTsProtoPlugin(t *testing.T) {
11+
plugintest.Cases(t, &bufbuild.EsProto{}, map[string]plugintest.Case{
12+
"simple": {
13+
Input: "message M{}",
14+
Directives: plugintest.WithDirectives(
15+
"proto_plugin", "es implementation bufbuild:connect-es",
16+
),
17+
PluginName: "es",
18+
Configuration: plugintest.WithConfiguration(
19+
plugintest.WithLabel(t, "@build_stack_rules_proto//plugin/bufbuild:es"),
20+
plugintest.WithOptions("keep_empty_files=true", "target=ts"),
21+
plugintest.WithOutputs("test_pb.ts"),
22+
),
23+
SkipIntegration: true,
24+
},
25+
})
26+
}

plugin/BUILD.bazel

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ filegroup(
44
srcs = [
55
"BUILD.bazel",
66
"//plugin/akka/akka-grpc:all_files",
7+
"//plugin/bufbuild:all_files",
78
"//plugin/builtin:all_files",
89
"//plugin/gogo/protobuf:all_files",
910
"//plugin/golang/protobuf:all_files",

plugin/bufbuild/BUILD.bazel

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
load("@build_stack_rules_proto//rules:proto_plugin.bzl", "proto_plugin")
2+
load("@npm_ts_proto//plugin/bufbuild:@bufbuild/protoc-gen-es/package_json.bzl", gen_bin = "bin")
3+
load("@npm_ts_proto//plugin/bufbuild:@bufbuild/protoc-gen-connect-es/package_json.bzl", connect_bin = "bin")
4+
5+
gen_bin.protoc_gen_es_binary(
6+
name = "protoc-gen-es",
7+
)
8+
9+
connect_bin.protoc_gen_connect_es_binary(
10+
name = "protoc-gen-connect",
11+
)
12+
13+
proto_plugin(
14+
name = "connect-es",
15+
tool = ":protoc-gen-connect",
16+
visibility = ["//visibility:public"],
17+
)
18+
19+
proto_plugin(
20+
name = "es",
21+
tool = ":protoc-gen-es",
22+
visibility = ["//visibility:public"],
23+
)
24+
25+
filegroup(
26+
name = "all_files",
27+
testonly = True,
28+
srcs = ["BUILD.bazel"],
29+
visibility = ["//plugin:__pkg__"],
30+
)

plugin/bufbuild/package.json

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"dependencies": {
3+
"@bufbuild/protobuf": "1.3.0",
4+
"@bufbuild/protoc-gen-es": "1.3.0",
5+
"@bufbuild/protoc-gen-connect-es": "0.12.0"
6+
}
7+
}

plugin/stephenh/ts-proto/package.json

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"dependencies": {
3+
"ts-proto": "1.156.0"
4+
}
5+
}

0 commit comments

Comments
 (0)