Skip to content

Commit

Permalink
Add experimental ES6 import style
Browse files Browse the repository at this point in the history
This change adds a new `import_style` that emits ES6 modules.
For now, this only re-exports the symbols from `import_style=closure`.

In the future, this will no longer require emitting google modules
(`goog.provide`). If protobuf ever adds support for emitting ES6
modules, we will use them instead of the `goog.provided`'d versions
as well.

Note that the Bazel integration is currently broken because of a
limitation in `rules_closure`.
  • Loading branch information
Yannic committed Jun 4, 2020
1 parent 721afdf commit f07876b
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 40 deletions.
98 changes: 59 additions & 39 deletions bazel/closure_grpc_web_library.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -36,60 +36,73 @@ def _proto_include_path(proto):
def _proto_include_paths(protos):
return [_proto_include_path(proto) for proto in protos]

def _generate_closure_grpc_web_src_progress_message(name):
def _generate_closure_grpc_web_src_progress_message(proto):
# TODO(yannic): Add a better message?
return "Generating GRPC Web %s" % name
return "Generating GRPC Web for %s" % proto

def _assert(condition, message):
if not condition:
fail(message)

def _generate_closure_grpc_web_srcs(
label,
actions,
protoc,
protoc_gen_grpc_web,
import_style,
mode,
sources,
transitive_sources):
all_sources = [src for src in sources] + [src for src in transitive_sources.to_list()]
proto_include_paths = [
"-I%s" % p
for p in _proto_include_paths(
[f for f in all_sources],
)
]
args = actions.args()

args.add("--plugin", "protoc-gen-grpc-web=" + protoc_gen_grpc_web.path)

args.add_all(_proto_include_paths(transitive_sources.to_list()), format_each = "-I%s")

grpc_web_out_common_options = ",".join([
"import_style={}".format(import_style),
"mode={}".format(mode),
])
args.add("--grpc-web_opt", "mode=" + mode)
if "es6" == import_style:
args.add("--grpc-web_opt", "import_style=experimental_closure_es6")
else:
args.add("--grpc-web_opt", "import_style=" + import_style)

root = None

files = []
es6_files = []
for src in sources:
name = "{}.grpc.js".format(
".".join(src.path.split("/")[-1].split(".")[:-1]),
)
js = actions.declare_file(name)
basename = src.basename[:-(len(src.extension) + 1)]

js = actions.declare_file(basename + "_grpc_web_pb.js", sibling = src)
files.append(js)

args = proto_include_paths + [
"--plugin=protoc-gen-grpc-web={}".format(protoc_gen_grpc_web.path),
"--grpc-web_out={options},out={out_file}:{path}".format(
options = grpc_web_out_common_options,
out_file = name,
path = js.path[:js.path.rfind("/")],
),
src.path,
]

actions.run(
tools = [protoc_gen_grpc_web],
inputs = all_sources,
outputs = [js],
executable = protoc,
arguments = args,
progress_message =
_generate_closure_grpc_web_src_progress_message(name),
_assert(
((root == None) or (root == js.root.path)),
"proto sources do not have the same root: '{}' != '{}'".format(root, js.root.path),
)
root = js.root.path

return files
if "es6" == import_style:
es6 = actions.declare_file(basename + ".pb.grpc-web.js", sibling = src)
es6_files.append(es6)

_assert(root == es6.root.path, "ES6 file should have same root: '{}' != '{}'".format(root, es6.root.path))

_assert(root, "At least one source file is required")

args.add("--grpc-web_out", root)
args.add_all(sources)

actions.run(
tools = [protoc_gen_grpc_web],
inputs = transitive_sources,
outputs = files + es6_files,
executable = protoc,
arguments = [args],
progress_message =
_generate_closure_grpc_web_src_progress_message(str(label)),
)

return files, es6_files

_error_multiple_deps = "".join([
"'deps' attribute must contain exactly one label ",
Expand All @@ -103,7 +116,8 @@ def _closure_grpc_web_library_impl(ctx):
fail(_error_multiple_deps, "deps")

proto_info = ctx.attr.deps[0][ProtoInfo]
srcs = _generate_closure_grpc_web_srcs(
srcs, es6_srcs = _generate_closure_grpc_web_srcs(
label = ctx.label,
actions = ctx.actions,
protoc = ctx.executable._protoc,
protoc_gen_grpc_web = ctx.executable._protoc_gen_grpc_web,
Expand All @@ -117,7 +131,7 @@ def _closure_grpc_web_library_impl(ctx):
deps.append(ctx.attr._runtime)
library = create_closure_js_library(
ctx = ctx,
srcs = srcs,
srcs = srcs + es6_srcs,
deps = deps,
suppress = [
"misplacedTypeAnnotation",
Expand Down Expand Up @@ -149,7 +163,13 @@ closure_grpc_web_library = rule(
),
"import_style": attr.string(
default = "closure",
values = ["closure"],
values = [
"closure",

# This is experimental and requires closure-js.
# We reserve the right to do breaking changes at any time.
"es6",
],
),
"mode": attr.string(
default = "grpcwebtext",
Expand Down
63 changes: 63 additions & 0 deletions javascript/net/grpc/web/grpc_generator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1471,6 +1471,53 @@ void PrintMultipleFilesMode(const FileDescriptor* file, string file_name,
printer2.Print("}); // goog.scope\n\n");
}

void PrintClosureES6Imports(
Printer* printer, const FileDescriptor* file, string package_dot) {
for (int i = 0; i < file->service_count(); ++i) {
const ServiceDescriptor* service = file->service(i);

string service_namespace = "proto." + package_dot + service->name();
printer->Print(
"import $service_name$Client_import from 'goog:$namespace$';\n",
"service_name", service->name(),
"namespace", service_namespace + "Client");
printer->Print(
"import $service_name$PromiseClient_import from 'goog:$namespace$';\n",
"service_name", service->name(),
"namespace", service_namespace + "PromiseClient");
}

printer->Print("\n\n\n");
}

void PrintGrpcWebClosureES6File(Printer* printer, const FileDescriptor* file) {
string package_dot = file->package().empty() ? "" : file->package() + ".";

printer->Print(
"// GENERATED CODE -- DO NOT EDIT!\n"
"\n"
"/**\n"
" * @fileoverview gRPC-Web generated client stub for '$file$'\n"
" */\n"
"\n"
"\n",
"file", file->name());

PrintClosureES6Imports(printer, file, package_dot);

for (int i = 0; i < file->service_count(); ++i) {
const ServiceDescriptor* service = file->service(i);

string service_namespace = "proto." + package_dot + service->name();
printer->Print(
"export const $name$Client = $name$Client_import;\n",
"name", service->name());
printer->Print(
"export const $name$PromiseClient = $name$PromiseClient_import;\n",
"name", service->name());
}
}

class GeneratorOptions {
public:
GeneratorOptions();
Expand All @@ -1485,13 +1532,15 @@ class GeneratorOptions {
string mode() const { return mode_; }
ImportStyle import_style() const { return import_style_; }
bool generate_dts() const { return generate_dts_; }
bool generate_closure_es6() const { return generate_closure_es6_; }
bool multiple_files() const { return multiple_files_; }

private:
string file_name_;
string mode_;
ImportStyle import_style_;
bool generate_dts_;
bool generate_closure_es6_;
bool multiple_files_;
};

Expand All @@ -1500,6 +1549,7 @@ GeneratorOptions::GeneratorOptions()
mode_(""),
import_style_(ImportStyle::CLOSURE),
generate_dts_(false),
generate_closure_es6_(false),
multiple_files_(false){}

bool GeneratorOptions::ParseFromOptions(const string& parameter,
Expand All @@ -1519,6 +1569,9 @@ bool GeneratorOptions::ParseFromOptions(
} else if ("import_style" == option.first) {
if ("closure" == option.second) {
import_style_ = ImportStyle::CLOSURE;
} else if ("experimental_closure_es6" == option.second) {
import_style_ = ImportStyle::CLOSURE;
generate_closure_es6_ = true;
} else if ("commonjs" == option.second) {
import_style_ = ImportStyle::COMMONJS;
} else if ("commonjs+dts" == option.second) {
Expand Down Expand Up @@ -1749,6 +1802,16 @@ class GrpcCodeGenerator : public CodeGenerator {
PrintGrpcWebDtsFile(&grpcweb_dts_printer, file);
}

if (generator_options.generate_closure_es6()) {
string es6_file_name = StripProto(file->name()) + ".pb.grpc-web.js";

std::unique_ptr<ZeroCopyOutputStream> es6_output(
context->Open(es6_file_name));
Printer es6_printer(es6_output.get(), '$');

PrintGrpcWebClosureES6File(&es6_printer, file);
}

return true;
}
};
Expand Down
16 changes: 15 additions & 1 deletion net/grpc/gateway/examples/echo/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
load("@io_bazel_rules_closure//closure:defs.bzl", "closure_js_binary")
load("@rules_proto//proto:defs.bzl", "proto_library")
load("//bazel:closure_grpc_web_library.bzl", "closure_grpc_web_library")

Expand All @@ -9,8 +10,21 @@ proto_library(
)

closure_grpc_web_library(
name = "echo",
name = "echo_es6",
import_style = "es6",
deps = [
":echo_proto",
],
)

# A (very simple) inegration test.
closure_js_binary(
name = "echo_es6_bin",
entry_points = [
"goog:proto.grpc.gateway.testing.EchoServiceClient",
"/net/grpc/gateway/examples/echo/echo.pb.grpc-web.js",
],
deps = [
":echo_es6",
],
)

0 comments on commit f07876b

Please sign in to comment.