mirror of https://github.com/grpc/grpc-web.git
Add experimental ES6 import style
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`.
This commit is contained in:
parent
f516ccbf3c
commit
a055960f80
|
@ -36,11 +36,16 @@ 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,
|
||||
|
@ -48,48 +53,56 @@ def _generate_closure_grpc_web_srcs(
|
|||
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()
|
||||
|
||||
grpc_web_out_common_options = ",".join([
|
||||
"import_style={}".format(import_style),
|
||||
"mode={}".format(mode),
|
||||
])
|
||||
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")
|
||||
|
||||
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 ",
|
||||
|
@ -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,
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
|
|
@ -1468,6 +1468,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();
|
||||
|
@ -1482,6 +1529,7 @@ 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:
|
||||
|
@ -1489,6 +1537,7 @@ class GeneratorOptions {
|
|||
string mode_;
|
||||
ImportStyle import_style_;
|
||||
bool generate_dts_;
|
||||
bool generate_closure_es6_;
|
||||
bool multiple_files_;
|
||||
};
|
||||
|
||||
|
@ -1497,6 +1546,7 @@ GeneratorOptions::GeneratorOptions()
|
|||
mode_(""),
|
||||
import_style_(ImportStyle::CLOSURE),
|
||||
generate_dts_(false),
|
||||
generate_closure_es6_(false),
|
||||
multiple_files_(false){}
|
||||
|
||||
bool GeneratorOptions::ParseFromOptions(const string& parameter,
|
||||
|
@ -1516,6 +1566,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) {
|
||||
|
@ -1744,6 +1797,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;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
@ -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",
|
||||
],
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue