protocでprotoc-gen-js: program not found or is not executableエラーが出るとき

  • javascript
  • grpc

原因

protocで proto ファイルから型生成をしようとすると出るエラー。

$ protoc \
    --plugin="protoc-gen-ts=${PROTOC_GEN_TS_PATH}" \
    --js_out="import_style=commonjs,binary:${OUT_DIR}" \
    --ts_out="${OUT_DIR}" \
    users.proto base.proto

protoc-gen-js: program not found or is not executable
Please specify a program using absolute path or make sure the program is available in your PATH system variable
--js_out: protoc-gen-js: Plugin failed with status code 1.

最初 protoc-gen-js なんてパッケージ使ってないし何やねんと思ったのだけど --js_out オプションを指定すると内部で暗黙的に呼ばれる実行ファイルのことっぽく、これが2022年の時点でprotocのコアに含まれなくなったため見つからないというエラーが出るらしい。

(JSの実装落としますわっていうPRがこれ Remove the JavaScript implementation by acozzette · Pull Request #9874 · protocolbuffers/protobuf )

対処1. バイナリの入手

JSファイルを吐き出したい場合、なんとかして自力で protoc-gen-js を手に入れる必要がある。

1. ビルド済みのものをダウンロードする

もっとも手軽。

いちおう移行先のrepoがあるので、Releasesから最新のバイナリを落とすことができる。

protocolbuffers/protobuf-javascript

2. 自力でビルドする

落としてきたバイナリだと動かないことがあるらしく、その場合は自力でバイナリをビルドする必要がある。

issueのコメントに完全な手順を載せてくれている方がおり、実際に手元でその通りやったらうまくいったので、ざっと紹介する。

https://github.com/protocolbuffers/protobuf-javascript/issues/127#issuecomment-1204202870

まず上述したJS対応実装repoをcloneする。

$ git clone https://github.com/protocolbuffers/protobuf-javascript
$ cd protobuf-javascript

ビルドにはbazelが必要なので、手元にない場合はインストールする。

bazel自体はもうnpmから配信されてないのでbazeliskを導入するのが一番手軽だと思う。macならhomebrew経由でインストールできる。 (bazeliskを入れると普通にbazelが使えるようになる)

bazelbuild/bazelisk: A user-friendly launcher for Bazel.

そしてビルドする。

$ bazel build //generator:protoc-gen-js

これでバイナリが吐き出される。

$ ls bazel-bin/generator/protoc-gen-js
bazel-bin/generator/protoc-gen-js

対処2. パスを指定する

protoc-gen-js が入手できたら protoc に所在を知らせる必要がある。 protoc-gen-ts と同じく --plugins オプションに渡せればいいのだけど、ちょっと調べた感じ複数指定ができなさそうだったのでPATHを通す形にした。

$ PATH="./path/to/protoc-gen-js:$PATH" protoc \
    --plugin="protoc-gen-ts=${PROTOC_GEN_TS_PATH}" \
    --js_out="import_style=commonjs,binary:${OUT_DIR}" \
    --ts_out="${OUT_DIR}" \
    users.proto base.proto

ちょっと気持ち悪いけど、当面のworkaroundはこうなるらしい。ややこしい。