Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for generating grpc-node service types #193

Merged
merged 10 commits into from
Aug 29, 2019
36 changes: 33 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

This repository contains a [protoc](https://github.com/google/protobuf) plugin that generates TypeScript declarations
(`.d.ts` files) that match the JavaScript output of `protoc --js_out=import_style=commonjs,binary`. This plugin can
also output service definitions as both `.js` and `.d.ts` files in the structure required by [grpc-web](https://github.com/improbable-eng/grpc-web).
also output service definitions as both `.js` and `.d.ts` files in the structure required by [grpc-web](https://github.com/improbable-eng/grpc-web), and as `.d.ts` files in the structure required by [grpc-node](https://github.com/grpc/grpc-node).

This plugin is tested and written using TypeScript 2.7.

Expand Down Expand Up @@ -168,7 +168,7 @@ msg.setName("John Doe");

[grpc-web](https://github.com/improbable-eng/grpc-web) is a comparability layer on both the server and client-side which allows gRPC to function natively in modern web-browsers.

To generate client-side service stubs from your protobuf files you must configure ts-protoc-gen to emit service definitions by passing the `service=true` param to the `--ts_out` flag, eg:
To generate client-side service stubs from your protobuf files you must configure ts-protoc-gen to emit service definitions by passing the `service=grpc-web` param to the `--ts_out` flag, eg:

```
# Path to this plugin, Note this must be an abolsute path on Windows (see #15)
Expand All @@ -180,7 +180,7 @@ OUT_DIR="./generated"
protoc \
--plugin="protoc-gen-ts=${PROTOC_GEN_TS_PATH}" \
--js_out="import_style=commonjs,binary:${OUT_DIR}" \
--ts_out="service=true:${OUT_DIR}" \
--ts_out="service=grpc-web:${OUT_DIR}" \
users.proto base.proto
```

Expand All @@ -203,6 +203,36 @@ client.getUser(req, (err, user) => {
});
```

### Generating gRPC Service Stubs for use with grpc-node

This plugin can generate `.d.ts` files for gRPC service definitions as required by [grpc-node](https://github.com/grpc/grpc-node).

To generate these declaration files from your protobuf files you must configure ts-protoc-gen to emit service definitions by passing the `service=grpc-node` param to the `--ts_out` flag, eg:

```
# Path to this plugin, Note this must be an abolsute path on Windows (see #15)
PROTOC_GEN_TS_PATH="./node_modules/.bin/protoc-gen-ts"

# Path to the grpc_node_plugin
PROTOC_GEN_GRPC_PATH="./node_modules/.bin/grpc_tools_node_protoc_plugin"

# Directory to write generated code to (.js and .d.ts files)
OUT_DIR="./generated"

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

The `generated` folder will now contain both `_grpc_pb.js` and `_grpc_pb.d.ts` files which you can reference in your TypeScript project to make RPCs.

**Note** This plugin does not generate the `_grpc_pb.js` files itself; those are generated by the protoc-gen-grpc plugin. This plugin only generates the `_grpc_pb.d.ts` files.


## Examples

- [Example output](https://github.com/improbable-eng/ts-protoc-gen/tree/master/examples) -- Code generated by `ts-protoc-gen`.
Expand Down
2 changes: 1 addition & 1 deletion defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def _build_protoc_command(target, ctx):
protoc_command += " --plugin=protoc-gen-ts=%s" % (ctx.files._ts_protoc_gen[1].path)

protoc_output_dir = ctx.var["BINDIR"]
protoc_command += " --ts_out=service=true:%s" % (protoc_output_dir)
protoc_command += " --ts_out=service=grpc-web:%s" % (protoc_output_dir)
protoc_command += " --js_out=import_style=commonjs,binary:%s" % (protoc_output_dir)

descriptor_sets_paths = [desc.path for desc in target.proto.transitive_descriptor_sets.to_list()]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// GENERATED CODE -- NO SERVICES IN PROTO
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// GENERATED CODE -- NO SERVICES IN PROTO
41 changes: 41 additions & 0 deletions examples/generated-grpc-node/proto/examplecom/annotations_pb.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// package: examplecom
// file: proto/examplecom/annotations.proto

import * as jspb from "google-protobuf";

export class AnnotatedMessage extends jspb.Message {
getMyunit64(): string;
setMyunit64(value: string): void;

getMyint64(): string;
setMyint64(value: string): void;

getMyfixed64(): string;
setMyfixed64(value: string): void;

getMysint64(): string;
setMysint64(value: string): void;

getMysfixed64(): string;
setMysfixed64(value: string): void;

serializeBinary(): Uint8Array;
toObject(includeInstance?: boolean): AnnotatedMessage.AsObject;
static toObject(includeInstance: boolean, msg: AnnotatedMessage): AnnotatedMessage.AsObject;
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
static serializeBinaryToWriter(message: AnnotatedMessage, writer: jspb.BinaryWriter): void;
static deserializeBinary(bytes: Uint8Array): AnnotatedMessage;
static deserializeBinaryFromReader(message: AnnotatedMessage, reader: jspb.BinaryReader): AnnotatedMessage;
}

export namespace AnnotatedMessage {
export type AsObject = {
myunit64: string,
myint64: string,
myfixed64: string,
mysint64: string,
mysfixed64: string,
}
}

265 changes: 265 additions & 0 deletions examples/generated-grpc-node/proto/examplecom/annotations_pb.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
/**
* @fileoverview
* @enhanceable
* @suppress {messageConventions} JS Compiler reports an error if a variable or
* field starts with 'MSG_' and isn't a translatable message.
* @public
*/
// GENERATED CODE -- DO NOT EDIT!

var jspb = require('google-protobuf');
var goog = jspb;
var global = Function('return this')();

goog.exportSymbol('proto.examplecom.AnnotatedMessage', null, global);

/**
* Generated by JsPbCodeGenerator.
* @param {Array=} opt_data Optional initial data array, typically from a
* server response, or constructed directly in Javascript. The array is used
* in place and becomes part of the constructed object. It is not cloned.
* If no data is provided, the constructed object will be empty, but still
* valid.
* @extends {jspb.Message}
* @constructor
*/
proto.examplecom.AnnotatedMessage = function(opt_data) {
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
};
goog.inherits(proto.examplecom.AnnotatedMessage, jspb.Message);
if (goog.DEBUG && !COMPILED) {
proto.examplecom.AnnotatedMessage.displayName = 'proto.examplecom.AnnotatedMessage';
}


if (jspb.Message.GENERATE_TO_OBJECT) {
/**
* Creates an object representation of this proto suitable for use in Soy templates.
* Field names that are reserved in JavaScript and will be renamed to pb_name.
* To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
* For the list of reserved names please see:
* com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS.
* @param {boolean=} opt_includeInstance Whether to include the JSPB instance
* for transitional soy proto support: http://goto/soy-param-migration
* @return {!Object}
*/
proto.examplecom.AnnotatedMessage.prototype.toObject = function(opt_includeInstance) {
return proto.examplecom.AnnotatedMessage.toObject(opt_includeInstance, this);
};


/**
* Static version of the {@see toObject} method.
* @param {boolean|undefined} includeInstance Whether to include the JSPB
* instance for transitional soy proto support:
* http://goto/soy-param-migration
* @param {!proto.examplecom.AnnotatedMessage} msg The msg instance to transform.
* @return {!Object}
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.examplecom.AnnotatedMessage.toObject = function(includeInstance, msg) {
var f, obj = {
myunit64: jspb.Message.getFieldWithDefault(msg, 1, "0"),
myint64: jspb.Message.getFieldWithDefault(msg, 2, "0"),
myfixed64: jspb.Message.getFieldWithDefault(msg, 3, "0"),
mysint64: jspb.Message.getFieldWithDefault(msg, 4, "0"),
mysfixed64: jspb.Message.getFieldWithDefault(msg, 5, "0")
};

if (includeInstance) {
obj.$jspbMessageInstance = msg;
}
return obj;
};
}


/**
* Deserializes binary data (in protobuf wire format).
* @param {jspb.ByteSource} bytes The bytes to deserialize.
* @return {!proto.examplecom.AnnotatedMessage}
*/
proto.examplecom.AnnotatedMessage.deserializeBinary = function(bytes) {
var reader = new jspb.BinaryReader(bytes);
var msg = new proto.examplecom.AnnotatedMessage;
return proto.examplecom.AnnotatedMessage.deserializeBinaryFromReader(msg, reader);
};


/**
* Deserializes binary data (in protobuf wire format) from the
* given reader into the given message object.
* @param {!proto.examplecom.AnnotatedMessage} msg The message object to deserialize into.
* @param {!jspb.BinaryReader} reader The BinaryReader to use.
* @return {!proto.examplecom.AnnotatedMessage}
*/
proto.examplecom.AnnotatedMessage.deserializeBinaryFromReader = function(msg, reader) {
while (reader.nextField()) {
if (reader.isEndGroup()) {
break;
}
var field = reader.getFieldNumber();
switch (field) {
case 1:
var value = /** @type {string} */ (reader.readUint64String());
msg.setMyunit64(value);
break;
case 2:
var value = /** @type {string} */ (reader.readInt64String());
msg.setMyint64(value);
break;
case 3:
var value = /** @type {string} */ (reader.readFixed64String());
msg.setMyfixed64(value);
break;
case 4:
var value = /** @type {string} */ (reader.readSint64String());
msg.setMysint64(value);
break;
case 5:
var value = /** @type {string} */ (reader.readSfixed64String());
msg.setMysfixed64(value);
break;
default:
reader.skipField();
break;
}
}
return msg;
};


/**
* Serializes the message to binary data (in protobuf wire format).
* @return {!Uint8Array}
*/
proto.examplecom.AnnotatedMessage.prototype.serializeBinary = function() {
var writer = new jspb.BinaryWriter();
proto.examplecom.AnnotatedMessage.serializeBinaryToWriter(this, writer);
return writer.getResultBuffer();
};


/**
* Serializes the given message to binary data (in protobuf wire
* format), writing to the given BinaryWriter.
* @param {!proto.examplecom.AnnotatedMessage} message
* @param {!jspb.BinaryWriter} writer
* @suppress {unusedLocalVariables} f is only used for nested messages
*/
proto.examplecom.AnnotatedMessage.serializeBinaryToWriter = function(message, writer) {
var f = undefined;
f = message.getMyunit64();
if (parseInt(f, 10) !== 0) {
writer.writeUint64String(
1,
f
);
}
f = message.getMyint64();
if (parseInt(f, 10) !== 0) {
writer.writeInt64String(
2,
f
);
}
f = message.getMyfixed64();
if (parseInt(f, 10) !== 0) {
writer.writeFixed64String(
3,
f
);
}
f = message.getMysint64();
if (parseInt(f, 10) !== 0) {
writer.writeSint64String(
4,
f
);
}
f = message.getMysfixed64();
if (parseInt(f, 10) !== 0) {
writer.writeSfixed64String(
5,
f
);
}
};


/**
* optional uint64 myUnit64 = 1;
* @return {string}
*/
proto.examplecom.AnnotatedMessage.prototype.getMyunit64 = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "0"));
};


/** @param {string} value */
proto.examplecom.AnnotatedMessage.prototype.setMyunit64 = function(value) {
jspb.Message.setProto3StringIntField(this, 1, value);
};


/**
* optional int64 myInt64 = 2;
* @return {string}
*/
proto.examplecom.AnnotatedMessage.prototype.getMyint64 = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "0"));
};


/** @param {string} value */
proto.examplecom.AnnotatedMessage.prototype.setMyint64 = function(value) {
jspb.Message.setProto3StringIntField(this, 2, value);
};


/**
* optional fixed64 myFixed64 = 3;
* @return {string}
*/
proto.examplecom.AnnotatedMessage.prototype.getMyfixed64 = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, "0"));
};


/** @param {string} value */
proto.examplecom.AnnotatedMessage.prototype.setMyfixed64 = function(value) {
jspb.Message.setProto3StringIntField(this, 3, value);
};


/**
* optional sint64 mySint64 = 4;
* @return {string}
*/
proto.examplecom.AnnotatedMessage.prototype.getMysint64 = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, "0"));
};


/** @param {string} value */
proto.examplecom.AnnotatedMessage.prototype.setMysint64 = function(value) {
jspb.Message.setProto3StringIntField(this, 4, value);
};


/**
* optional sfixed64 mySfixed64 = 5;
* @return {string}
*/
proto.examplecom.AnnotatedMessage.prototype.getMysfixed64 = function() {
return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 5, "0"));
};


/** @param {string} value */
proto.examplecom.AnnotatedMessage.prototype.setMysfixed64 = function(value) {
jspb.Message.setProto3StringIntField(this, 5, value);
};


goog.object.extend(exports, proto.examplecom);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// GENERATED CODE -- NO SERVICES IN PROTO
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// GENERATED CODE -- NO SERVICES IN PROTO
Loading