Skip to content

Commit

Permalink
refactor: move protobufjs to userspace protobufjs_library macro
Browse files Browse the repository at this point in the history
The Labs package has this old and crufty protobufjs_ts_library rule which is not really documented.
The grpc-based one was meant to replace it.

Instead of providing a rule in @bazel/labs, just add an example showing how this can be done in userspace with a macro.
(there's also a one-liner rule to convert ProtoInfo to DefaultInfo so that the macro can interop with `proto_library`)

NOTE: this is not a breaking change because the @bazel/labs package comes with no stability guarantee.
If you're using protobufjs_ts_library you can duplicate the macro from the example to keep using it.

refactor: use ProtoInfo in the protobufjs example

This ensures that users don't have to repeat the dependency tree
  • Loading branch information
Alex Eagle authored and alexeagle committed Jan 31, 2021
1 parent 99bfe5f commit 078243a
Show file tree
Hide file tree
Showing 25 changed files with 1,320 additions and 893 deletions.
4 changes: 2 additions & 2 deletions .bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import %workspace%/common.bazelrc
# This lets us glob() up all the files inside the examples to make them inputs to tests
# To update these lines, just run `yarn bazel:update-deleted-packages`
# (Note, we cannot use common --deleted_packages because the bazel version command doesn't support it)
build --deleted_packages=examples/angular,examples/angular/e2e,examples/angular/src/app/home,examples/angular/src/app/hello-world,examples/angular/src/app,examples/angular/src/app/todos,examples/angular/src/app/todos/reducers,examples/angular/src/shared/material,examples/angular/src/lib/shorten,examples/angular/src,examples/angular/src/assets,examples/angular/tools,examples/angular_bazel_architect,examples/angular_bazel_architect/projects/frontend-lib,examples/angular_view_engine,examples/angular_view_engine/e2e,examples/angular_view_engine/src/app/home,examples/angular_view_engine/src/app/hello-world,examples/angular_view_engine/src/app,examples/angular_view_engine/src/app/todos,examples/angular_view_engine/src/app/todos/reducers,examples/angular_view_engine/src/shared/material,examples/angular_view_engine/src/lib/typography,examples/angular_view_engine/src/lib/shorten,examples/angular_view_engine/src,examples/angular_view_engine/src/assets,examples/app,examples/app/styles,examples/app/test,examples/closure,examples/create-react-app,examples/cypress,examples/from_source,examples/jest,examples/jest/ts/test,examples/jest/ts,examples/jest/ts/src,examples/kotlin,examples/nestjs,examples/nestjs/src,examples/parcel,examples/protocol_buffers,examples/react_webpack,examples/user_managed_deps,examples/vendored_node,examples/vendored_node_and_yarn,examples/vue,examples/vue/src,examples/web_testing,examples/webapp,examples/worker,e2e/bazel_managed_deps,e2e/concatjs_devserver,e2e/concatjs_devserver/genrule,e2e/concatjs_devserver/subpackage,e2e/coverage,e2e/fine_grained_symlinks,e2e/jasmine,e2e/node_loader_no_preserve_symlinks,e2e/node_loader_preserve_symlinks,e2e/nodejs_image,e2e/nodejs_image/foolib,e2e/packages,e2e/symlinked_node_modules_npm,e2e/symlinked_node_modules_yarn,e2e/typescript,e2e/webapp
query --deleted_packages=examples/angular,examples/angular/e2e,examples/angular/src/app/home,examples/angular/src/app/hello-world,examples/angular/src/app,examples/angular/src/app/todos,examples/angular/src/app/todos/reducers,examples/angular/src/shared/material,examples/angular/src/lib/shorten,examples/angular/src,examples/angular/src/assets,examples/angular/tools,examples/angular_bazel_architect,examples/angular_bazel_architect/projects/frontend-lib,examples/angular_view_engine,examples/angular_view_engine/e2e,examples/angular_view_engine/src/app/home,examples/angular_view_engine/src/app/hello-world,examples/angular_view_engine/src/app,examples/angular_view_engine/src/app/todos,examples/angular_view_engine/src/app/todos/reducers,examples/angular_view_engine/src/shared/material,examples/angular_view_engine/src/lib/typography,examples/angular_view_engine/src/lib/shorten,examples/angular_view_engine/src,examples/angular_view_engine/src/assets,examples/app,examples/app/styles,examples/app/test,examples/closure,examples/create-react-app,examples/cypress,examples/from_source,examples/jest,examples/jest/ts/test,examples/jest/ts,examples/jest/ts/src,examples/kotlin,examples/nestjs,examples/nestjs/src,examples/parcel,examples/protocol_buffers,examples/react_webpack,examples/user_managed_deps,examples/vendored_node,examples/vendored_node_and_yarn,examples/vue,examples/vue/src,examples/web_testing,examples/webapp,examples/worker,e2e/bazel_managed_deps,e2e/concatjs_devserver,e2e/concatjs_devserver/genrule,e2e/concatjs_devserver/subpackage,e2e/coverage,e2e/fine_grained_symlinks,e2e/jasmine,e2e/node_loader_no_preserve_symlinks,e2e/node_loader_preserve_symlinks,e2e/nodejs_image,e2e/nodejs_image/foolib,e2e/packages,e2e/symlinked_node_modules_npm,e2e/symlinked_node_modules_yarn,e2e/typescript,e2e/webapp
build --deleted_packages=examples/angular,examples/angular/e2e,examples/angular/src/app/home,examples/angular/src/app/hello-world,examples/angular/src/app,examples/angular/src/app/todos,examples/angular/src/app/todos/reducers,examples/angular/src/shared/material,examples/angular/src/lib/shorten,examples/angular/src,examples/angular/src/assets,examples/angular/tools,examples/angular_bazel_architect,examples/angular_bazel_architect/projects/frontend-lib,examples/angular_view_engine,examples/angular_view_engine/e2e,examples/angular_view_engine/src/app/home,examples/angular_view_engine/src/app/hello-world,examples/angular_view_engine/src/app,examples/angular_view_engine/src/app/todos,examples/angular_view_engine/src/app/todos/reducers,examples/angular_view_engine/src/shared/material,examples/angular_view_engine/src/lib/typography,examples/angular_view_engine/src/lib/shorten,examples/angular_view_engine/src,examples/angular_view_engine/src/assets,examples/app,examples/app/styles,examples/app/test,examples/closure,examples/create-react-app,examples/cypress,examples/from_source,examples/jest,examples/jest/ts/test,examples/jest/ts,examples/jest/ts/src,examples/kotlin,examples/nestjs,examples/nestjs/src,examples/parcel,examples/protobufjs,examples/protocol_buffers,examples/react_webpack,examples/user_managed_deps,examples/vendored_node,examples/vendored_node_and_yarn,examples/vue,examples/vue/src,examples/web_testing,examples/webapp,examples/worker,e2e/bazel_managed_deps,e2e/concatjs_devserver,e2e/concatjs_devserver/genrule,e2e/concatjs_devserver/subpackage,e2e/coverage,e2e/fine_grained_symlinks,e2e/jasmine,e2e/node_loader_no_preserve_symlinks,e2e/node_loader_preserve_symlinks,e2e/nodejs_image,e2e/nodejs_image/foolib,e2e/packages,e2e/symlinked_node_modules_npm,e2e/symlinked_node_modules_yarn,e2e/typescript,e2e/webapp
query --deleted_packages=examples/angular,examples/angular/e2e,examples/angular/src/app/home,examples/angular/src/app/hello-world,examples/angular/src/app,examples/angular/src/app/todos,examples/angular/src/app/todos/reducers,examples/angular/src/shared/material,examples/angular/src/lib/shorten,examples/angular/src,examples/angular/src/assets,examples/angular/tools,examples/angular_bazel_architect,examples/angular_bazel_architect/projects/frontend-lib,examples/angular_view_engine,examples/angular_view_engine/e2e,examples/angular_view_engine/src/app/home,examples/angular_view_engine/src/app/hello-world,examples/angular_view_engine/src/app,examples/angular_view_engine/src/app/todos,examples/angular_view_engine/src/app/todos/reducers,examples/angular_view_engine/src/shared/material,examples/angular_view_engine/src/lib/typography,examples/angular_view_engine/src/lib/shorten,examples/angular_view_engine/src,examples/angular_view_engine/src/assets,examples/app,examples/app/styles,examples/app/test,examples/closure,examples/create-react-app,examples/cypress,examples/from_source,examples/jest,examples/jest/ts/test,examples/jest/ts,examples/jest/ts/src,examples/kotlin,examples/nestjs,examples/nestjs/src,examples/parcel,examples/protobufjs,examples/protocol_buffers,examples/react_webpack,examples/user_managed_deps,examples/vendored_node,examples/vendored_node_and_yarn,examples/vue,examples/vue/src,examples/web_testing,examples/webapp,examples/worker,e2e/bazel_managed_deps,e2e/concatjs_devserver,e2e/concatjs_devserver/genrule,e2e/concatjs_devserver/subpackage,e2e/coverage,e2e/fine_grained_symlinks,e2e/jasmine,e2e/node_loader_no_preserve_symlinks,e2e/node_loader_preserve_symlinks,e2e/nodejs_image,e2e/nodejs_image/foolib,e2e/packages,e2e/symlinked_node_modules_npm,e2e/symlinked_node_modules_yarn,e2e/typescript,e2e/webapp

# Mock versioning command to test the --stamp behavior
build --workspace_status_command="echo BUILD_SCM_VERSION 1.2.3"
Expand Down
16 changes: 14 additions & 2 deletions docs/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,21 @@ Example at [examples/kotlin](https://github.com/bazelbuild/rules_nodejs/tree/sta

### Protocol Buffers and gRPC

Note: this is considered a "labs" feature in rules_nodejs, so support and stability are not great. gRPC is still a WIP.
Note: this is under active development. Come chat in the #javascript channel on Slack to get the latest.
Support and stability are not great but expected to improve.

See [examples/protocol_buffers](https://github.com/bazelbuild/rules_nodejs/tree/stable/examples/protocol_buffers)
There are many alternative implementations for protobuf and RPC.
We generally intend to support all of them, with a small layer in rules_nodejs that allows you to build around any of those tools.

<https://github.com/rules-proto-grpc/rules_proto_grpc> is an excellent, broad ruleset based on the tooling from <http://grpc.io>. We may point to this as the canonical example in the future.

The `@bazel/labs` package has an experimental `ts_proto_library` rule.
It integrates with the "concatjs" bundler and is suitable for projects using `ts_library`.
However it's not clear whether this will be promoted to a stable API.
See the example in [examples/protocol_buffers](https://github.com/bazelbuild/rules_nodejs/tree/stable/examples/protocol_buffers)

[protobuf.js](https://github.com/protobufjs/protobuf.js) from https://github.com/dcodeIO is a simple alternative.
See the example in [examples/protobufjs](https://github.com/bazelbuild/rules_nodejs/tree/stable/examples/protobufjs)

## Bazel-specific

Expand Down
8 changes: 8 additions & 0 deletions examples/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,14 @@ example_integration_test(
tags = ["no-bazelci-windows"],
)

example_integration_test(
name = "examples_protobufjs",
npm_packages = {
"//packages/jasmine:npm_package": "@bazel/jasmine",
"//packages/typescript:npm_package": "@bazel/typescript",
},
)

example_integration_test(
name = "examples_vendored_node",
npm_packages = {
Expand Down
3 changes: 3 additions & 0 deletions examples/protobufjs/.bazelignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
dist
bazel-out
1 change: 1 addition & 0 deletions examples/protobufjs/.bazelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import %workspace%/../../common.bazelrc
48 changes: 48 additions & 0 deletions examples/protobufjs/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
load("@npm//@bazel/jasmine:index.bzl", "jasmine_node_test")
load("@npm//@bazel/typescript:index.bzl", "ts_project")
load("@rules_proto//proto:defs.bzl", "proto_library")
load(":defs.bzl", "protobufjs_library")

proto_library(
name = "car_proto",
srcs = [
"car.proto",
"tire.proto",
],
)

protobufjs_library(
# produces outputs named after this,
# car.d.ts and car.js
name = "car",
proto = "car_proto",
)

ts_project(
name = "test_lib",
testonly = True,
srcs = ["car.spec.ts"],
tsconfig = "tsconfig.json",
deps = [
":car",
"@npm//@types/jasmine",
"@npm//protobufjs",
],
)

ts_project(
name = "app",
srcs = [
"app.ts",
],
tsconfig = "//:tsconfig.json",
deps = [
":car",
"@npm//protobufjs",
],
)

jasmine_node_test(
name = "test",
deps = ["test_lib"],
)
11 changes: 11 additions & 0 deletions examples/protobufjs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# protobufjs example

This shows how the [protobuf.js](https://github.com/protobufjs/protobuf.js)
build tools and runtime library can be used to consume `.proto` files.

Note that the example requires some "userland" code to invoke the tools,
since there is no "custom rule" to invoke them under Bazel.
See `defs.bzl` for the sample code you will need.

Currently the example doesn't exercise the Service definitions in the proto,
but we expect this is easily added. It would be a great community contribution.
49 changes: 49 additions & 0 deletions examples/protobufjs/WORKSPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Copyright 2019 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

workspace(
name = "examples_protobufjs",
managed_directories = {"@npm": ["node_modules"]},
)

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
name = "build_bazel_rules_nodejs",
sha256 = "6142e9586162b179fdd570a55e50d1332e7d9c030efd853453438d607569721d",
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/3.0.0/rules_nodejs-3.0.0.tar.gz"],
)

http_archive(
name = "rules_proto",
sha256 = "2a20fd8af3cad3fbab9fd3aec4a137621e0c31f858af213a7ae0f997723fc4a9",
strip_prefix = "rules_proto-a0761ed101b939e19d83b2da5f59034bffc19c12",
urls = [
"https://github.com/bazelbuild/rules_proto/archive/a0761ed101b939e19d83b2da5f59034bffc19c12.tar.gz",
],
)

load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains")

rules_proto_dependencies()

rules_proto_toolchains()

load("@build_bazel_rules_nodejs//:index.bzl", "yarn_install")

yarn_install(
name = "npm",
package_json = "//:package.json",
yarn_lock = "//:yarn.lock",
)
15 changes: 15 additions & 0 deletions examples/protobufjs/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {Proto as pb} from './car_pb';

// TODO: use a service to fetch this data from a nodejs server
// documentation: https://github.com/protobufjs/protobuf.js/tree/6.8.8#using-services
const car = new pb.Car();
car.make = 'Porsche';

const el: HTMLDivElement = document.createElement('div');
el.innerText = `Car from server: ${car.make}`;
el.className = 'ts1';
document.body.appendChild(el);

const el2: HTMLDivElement = document.createElement('div');
el2.className = 'ts2';
document.body.appendChild(el2);
24 changes: 24 additions & 0 deletions examples/protobufjs/car.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
syntax = "proto3";

import "tire.proto";

package Proto;

message Car {
string make = 1;
string model = 2;
int32 year = 3;
Tire front_tires = 4;
Tire rear_tires = 5;
int64 mileage = 6;
}

message GetCarRequest {
}
message GetCarResponse {
}

service CarService {
rpc GetCar(GetCarRequest) returns (GetCarResponse) {
}
}
17 changes: 17 additions & 0 deletions examples/protobufjs/car.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {Proto as pb} from './car_pb';

describe('protocol buffers', () => {
it('allows creation of an object described by proto', () => {
const tires = pb.Tire.create();
tires.aspectRatio = 65;
tires.width = 225;
tires.construction = 'R';
tires.diameter = 17;

const pontiac = pb.Car.create();
pontiac.make = 'pontiac';
pontiac.frontTires = tires;

expect(pontiac.make).toEqual('pontiac');
});
});
106 changes: 106 additions & 0 deletions examples/protobufjs/defs.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
"Illustrate how to wrap pbjs and pbts from protobufjs"

load("@build_bazel_rules_nodejs//:index.bzl", "js_library")

# TODO switch to protobufjs-cli when its published
# https://github.com/protobufjs/protobuf.js/commit/da34f43ccd51ad97017e139f137521782f5ef119
load("@npm//protobufjs:index.bzl", "pbjs", "pbts")
load("@rules_proto//proto:defs.bzl", "ProtoInfo")

# protobuf.js relies on these packages, but does not list them as dependencies
# in its package.json.
# Instead they are listed under "cliDependencies"
# (see https://unpkg.com/[email protected]/package.json)
# When run, the CLI attempts to run `npm install` at runtime to get them.
# This fails under Bazel as it tries to access the npm cache outside of the sandbox.
# Per Bazel semantics, all dependencies should be pre-declared.
# Note, you'll also need to install all of these in your package.json!
# (This should be fixed when we switch to protobufjs-cli)
_PROTOBUFJS_CLI_DEPS = ["@npm//%s" % s for s in [
"chalk",
"escodegen",
"espree",
"estraverse",
"glob",
"jsdoc",
"minimist",
"semver",
"tmp",
"uglify-js",
]]

def _proto_sources_impl(ctx):
return DefaultInfo(files = ctx.attr.proto[ProtoInfo].transitive_sources)

_proto_sources = rule(
doc = """Provider Adapter from ProtoInfo to DefaultInfo.
Extracts the transitive_sources from the ProtoInfo provided by the proto attr.
This allows a macro to access the complete set of .proto files needed during compilation.
""",
implementation = _proto_sources_impl,
attrs = {"proto": attr.label(providers = [ProtoInfo])},
)

def protobufjs_library(name, proto, **kwargs):
"""Minimal wrapper macro around pbjs/pbts tooling
Args:
name: name of generated js_library target, also used to name the .js/.d.ts outputs
proto: label of a single proto_library target to generate for
**kwargs: passed through to the js_library
"""

js_out = name + "_pb.js"
ts_out = js_out.replace(".js", ".d.ts")

# Generate some target names, based on the provided name
# (so that they are unique if the macro is called several times in one package)
proto_target = "_%s_protos" % name
js_target = "_%s_pbjs" % name
ts_target = "_%s_pbts" % name

# grab the transitive .proto files needed to compile the given one
_proto_sources(
name = proto_target,
proto = proto,
)

# Transform .proto files to a single _pb.js file named after the macro
pbjs(
name = js_target,
data = [proto_target] + _PROTOBUFJS_CLI_DEPS,
# Arguments documented at
# https://github.com/protobufjs/protobuf.js/tree/6.8.8#pbjs-for-javascript
args = [
"--target=static-module",
"--wrap=default",
"--strict-long", # Force usage of Long type with int64 fields
"--out=$@",
"$(execpaths %s)" % proto_target,
],
outs = [js_out],
)

# Transform the _pb.js file to a .d.ts file with TypeScript types
pbts(
name = ts_target,
data = [js_target] + _PROTOBUFJS_CLI_DEPS,
# Arguments documented at
# https://github.com/protobufjs/protobuf.js/tree/6.8.8#pbts-for-typescript
args = [
"--out=$@",
"$(execpath %s)" % js_target,
],
outs = [ts_out],
)

# Expose the results as js_library which provides DeclarationInfo for interop with other rules
js_library(
name = name,
srcs = [
js_target,
ts_target,
],
**kwargs
)
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
{
"name": "protobufjs",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"private": true,
"devDependencies": {
"@bazel/jasmine": "^3.0.0",
"@bazel/typescript": "^3.0.0",
"@types/jasmine": "2.8.2",
"@types/long": "^4.0.0",
"@types/node": "11.11.1",
"chalk": "^2.4.1",
"escodegen": "^1.9.1",
"espree": "^3.5.4",
"estraverse": "^4.2.0",
"glob": "^7.1.2",
"jasmine": "2.8.0",
"jsdoc": "^3.5.5",
"long": "4.0.0",
"minimist": "^1.2.0",
"protobufjs": "On update, other dependencies here to be updated to the same versions as in protobufjs package.json",
"protobufjs": "=6.8.8",
"semver": "^5.5.0",
"tmp": "0.0.33",
"typescript": "^3.3.1",
"uglify-js": "^3.3.25"
},
"scripts": {
"test": "bazel test ..."
}
}
13 changes: 13 additions & 0 deletions examples/protobufjs/tire.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
syntax = "proto3";

package Proto;

message Tire {
string type = 1;
int32 width = 2;
int32 aspect_ratio = 3;
string construction = 4;
int32 diameter = 5;
int32 load_index = 6;
string speed_rating = 7;
}
19 changes: 19 additions & 0 deletions examples/protobufjs/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"compilerOptions": {
"strict": true,
"lib": ["ES2015", "dom"],
// Include the output directory in rootDirs so that generated .d.ts files
// can be used for type-checking in the editor, for example the car.proto
// produces a car.d.ts.
"rootDirs": [
".",
"bazel-out/darwin-fastbuild/bin",
"bazel-out/k8-fastbuild/bin",
"bazel-out/x64_windows-fastbuild/bin",
"bazel-out/darwin-dbg/bin",
"bazel-out/k8-dbg/bin",
"bazel-out/x64_windows-dbg/bin",
]
},
"include": ["*.ts"]
}
Loading

0 comments on commit 078243a

Please sign in to comment.