Skip to content

A faster, Bazel-friendly version of protobufjs-cli, written in Go.

License

Notifications You must be signed in to change notification settings

buildbuddy-io/protoc-gen-protobufjs

Repository files navigation

protoc-gen-protobufjs

A protoc plugin to generate JavaScript code from .proto files along with TypeScript declarations.

It is intended as a faster replacement for protobufjs-cli, ideal for large, evolving projects with many protos. The generated code uses protobufjs/minimal and long as the only runtime dependencies.

Improvements over protobufjs-cli (pbjs / pbts)

  • Generates code 10X faster. One benchmark run on https://github.com/buildbuddy-io/buildbuddy brings the code generation time from ~19s to ~1.8s.

  • Supports generating code for only a single .proto file and not its imported files1. This makes it a good fit for build systems like Bazel which are optimized for many small build steps rather than a few big build steps.

  • Imports are declared explicitly, rather than referencing global "roots". This makes it easier to navigate the generated code.

  • Generated code preserves comments from the original proto source.

protobufjs-cli compatibility

The generated code is compatible with pbjs + pbts, where pbjs is run with the following flags:

--target=static-module
--wrap=es6
--force-message
--strict-long
--no-delimited
--no-verify

(PRs are welcome to add support for other options that would increase compatibility with the protobufjs-cli.)

In addition, the generated code is incompatible in the following ways:

  • Does not support the deprecated proto2 groups feature
  • Does not yet support proto2 default value annotations
  • Does not generate _optional getters for proto3 optional scalar fields; instead types the field as |null|undefined
  • Does not support reflection
  • Does not support register global protobuf.roots entries like protobuf.roots["default"].my.proto.Message

Usage

# Install the plugin
go install github.com/buildbuddy-io/protoc-gen-protobufjs@latest

# Make sure the installed binary (protoc-gen-protobufjs) is in $PATH
export PATH="$(go env GOPATH)/bin:$PATH"

# Compile your protos
protoc --protobufjs_out=./out ./src/hello.proto

Plugin flags

The plugin supports the following flags:

-import_path: PROTO_PATH=TS_PATH
  A mapping of proto imports to TS imports, as PROTO_PATH=TS_PATH pairs
  (this flag can be specified more than once). If the proto does not end
  with '.proto' then these act as directory prefixes.

-out: path
  Output file path. If this is set and multiple protos are provided as
  input, the generated code for all protos will be written to this file.
  Any '.ts' or '.js' extension will be ignored.

-strict_imports:
  If set, all directly imported protos must have an -import_path
  specified. This is useful when integrating with a build system where all
  direct dependencies must be explicitly specified.

-import_module_specifier_ending: string
  Suffix to apply to generated import module specifiers. May need to be
  set to ".js" in rare cases.

Flags are specified using the --protobufjs_opt flag to protoc. To specify multiple plugin flags, separate each flag with ":" (note that passing --protobufjs_opt more than once will not accumulate flags).

Example:

protoc \
  --protobufjs_out=./out/ \
  --protobufjs_opt=-strict_imports:-import_path=src/foo.proto:src/foo.ts

Footnotes

  1. By default, protobufjs-cli bundles all transitive dependencies into the output .js file, which causes an explosion of unnecessary dependency edges in the build graph. protobufjs-cli does have a --sparse option, but this only excludes indirect imports from the output JS. For example, let's say we have a.proto that imports b.proto, and b.proto imports c.proto. Then pbjs --sparse a.proto generates code for both A and B in the output. With protoc-gen-protobufjs, the generated code only contains A, and instead generates an import for B.