Skip to content

Commit 9a23ded

Browse files
committed
Other: ext/descriptor support for various standard options, see #757
1 parent 2d8ce6e commit 9a23ded

File tree

10 files changed

+149
-71
lines changed

10 files changed

+149
-71
lines changed

bench/data/static_pbjs.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ $root.Test = (function() {
130130
if (message.long != null && message.hasOwnProperty("long"))
131131
writer.uint32(8).int64(message.long);
132132
if (message["enum"] != null && message.hasOwnProperty("enum"))
133-
writer.uint32(16).uint32(message["enum"]);
133+
writer.uint32(16).int32(message["enum"]);
134134
if (message.sint32 != null && message.hasOwnProperty("sint32"))
135135
writer.uint32(24).sint32(message.sint32);
136136
return writer;
@@ -147,7 +147,7 @@ $root.Test = (function() {
147147
message.long = reader.int64();
148148
break;
149149
case 2:
150-
message["enum"] = reader.uint32();
150+
message["enum"] = reader.int32();
151151
break;
152152
case 3:
153153
message.sint32 = reader.sint32();

cli/lib/tsd-jsdoc/publish.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ function getTypeOf(element) {
241241

242242
// begins writing the definition of the specified element
243243
function begin(element, is_interface) {
244-
writeComment(element.comment, is_interface || isInterface(element) || isClassLike(element) || isNamespace(element) || element.isEnum);
244+
writeComment(element.comment, is_interface || isInterface(element) || isClassLike(element) || isNamespace(element) || element.isEnum || element.scope === "global");
245245
if (element.scope !== "global" || options.module)
246246
return;
247247
write("export ");

ext/descriptor/README.md

+8-8
Original file line numberDiff line numberDiff line change
@@ -36,22 +36,22 @@ The extension adds `.fromDescriptor(descriptor[, syntax])` and `#toDescriptor([s
3636
| Descriptor type | protobuf.js type | Remarks
3737
|--------------------------|------------------|---------
3838
| FileDescriptorSet | Root |
39-
| FileDescriptorProto | Root | except dependencies, sourceCodeInfo
40-
| FileOptions | Root | not supported
39+
| FileDescriptorProto | Root | except dependencies
40+
| FileOptions | Root |
4141
| DescriptorProto | Type |
42-
| MessageOptions | Type | not supported
43-
| FieldDescriptorProto | Field | except defaultValue
42+
| MessageOptions | Type |
43+
| FieldDescriptorProto | Field |
4444
| FieldOptions | Field |
4545
| OneofDescriptorProto | OneOf |
46-
| OneofOptions | OneOf | not supported
46+
| OneofOptions | OneOf |
4747
| EnumDescriptorProto | Enum |
4848
| EnumValueDescriptorProto | Enum |
49-
| EnumOptions | Enum | only allowAlias
49+
| EnumOptions | Enum |
5050
| EnumValueOptions | Enum | not supported
5151
| ServiceDescriptorProto | Service |
52-
| ServiceOptions | Service | not supported
52+
| ServiceOptions | Service |
5353
| MethodDescriptorProto | Method |
54-
| MethodOptions | Method | not supported
54+
| MethodOptions | Method |
5555
| UninterpretedOption | | not supported
5656
| SourceCodeInfo | | not supported
5757
| GeneratedCodeInfo | | not supported

ext/descriptor/index.d.ts

+32-9
Original file line numberDiff line numberDiff line change
@@ -46,26 +46,29 @@ type ReservedRangeProperties = {
4646
end?: number;
4747
};
4848

49-
type FieldOptionsProperties = {
50-
packed?: boolean;
51-
};
52-
5349
type FieldDescriptorProtoProperties = {
5450
name?: string;
5551
number?: number;
56-
label?: FieldDescriptorProtoLabel;
57-
type?: FieldDescriptorProtoType;
52+
label?: FieldDescriptorProto_Label;
53+
type?: FieldDescriptorProto_Type;
5854
typeName?: string;
5955
extendee?: string;
60-
defaultValue?: any;
56+
defaultValue?: string;
6157
oneofIndex?: number;
6258
jsonName?: any;
6359
options?: FieldOptionsProperties;
6460
};
6561

66-
type FieldDescriptorProtoLabel = number;
62+
type FieldDescriptorProto_Label = number;
6763

68-
type FieldDescriptorProtoType = number;
64+
type FieldDescriptorProto_Type = number;
65+
66+
type FieldOptionsProperties = {
67+
packed?: boolean;
68+
jstype?: FieldOptions_JSType;
69+
};
70+
71+
type FieldOptions_JSType = number;
6972

7073
type EnumDescriptorProtoProperties = {
7174
name?: string;
@@ -102,23 +105,43 @@ type MethodDescriptorProtoProperties = {
102105
clientStreaming?: boolean;
103106
serverStreaming?: boolean;
104107
};
108+
105109
export const FileDescriptorSet: $protobuf.Type;
110+
106111
export const FileDescriptorProto: $protobuf.Type;
112+
107113
export const DescriptorProto: $protobuf.Type;
114+
108115
export const FieldDescriptorProto: $protobuf.Type;
116+
109117
export const OneofDescriptorProto: $protobuf.Type;
118+
110119
export const EnumDescriptorProto: $protobuf.Type;
120+
111121
export const ServiceDescriptorProto: $protobuf.Type;
122+
112123
export const EnumValueDescriptorProto: $protobuf.Type;
124+
113125
export const MethodDescriptorProto: $protobuf.Type;
126+
114127
export const FileOptions: $protobuf.Type;
128+
115129
export const MessageOptions: $protobuf.Type;
130+
116131
export const FieldOptions: $protobuf.Type;
132+
117133
export const OneofOptions: $protobuf.Type;
134+
118135
export const EnumOptions: $protobuf.Type;
136+
119137
export const EnumValueOptions: $protobuf.Type;
138+
120139
export const ServiceOptions: $protobuf.Type;
140+
121141
export const MethodOptions: $protobuf.Type;
142+
122143
export const UninterpretedOption: $protobuf.Type;
144+
123145
export const SourceCodeInfo: $protobuf.Type;
146+
124147
export const GeneratedCodeInfo: $protobuf.Type;

ext/descriptor/index.js

+71-21
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"use strict";
2-
var $protobuf = require("../..");
2+
var $protobuf = require("../.."); // requires the full library (uses parser exports)
33
module.exports = exports = $protobuf.descriptor = $protobuf.Root.fromJSON(require("../../google/protobuf/descriptor.json")).lookup(".google.protobuf");
44

55
var Namespace = $protobuf.Namespace,
@@ -73,6 +73,12 @@ Root.fromDescriptor = function fromDescriptor(descriptor) {
7373
if (fileDescriptor.extension)
7474
for (i = 0; i < fileDescriptor.extension.length; ++i)
7575
filePackage.add(Field.fromDescriptor(fileDescriptor.extension[i]));
76+
var opts = fromDescriptorOptions(fileDescriptor.options, exports.FileOptions);
77+
if (opts) {
78+
var ks = Object.keys(opts);
79+
for (i = 0; i < ks.length; ++i)
80+
filePackage.setOption(ks[i], opts[ks[i]]);
81+
}
7682
}
7783
}
7884

@@ -114,6 +120,9 @@ function Root_toDescriptorRecursive(ns, files, syntax) {
114120
else if (nested instanceof /* plain */ Namespace)
115121
Root_toDescriptorRecursive(nested, files, syntax); // requires new file
116122

123+
// Keep package-level options
124+
file.options = toDescriptorOptions(ns.options, exports.FileOptions);
125+
117126
// And keep the file only if there is at least one nested object
118127
if (file.messageType.length + file.enumType.length + file.extension.length + file.service.length)
119128
files.push(file);
@@ -180,7 +189,7 @@ Type.fromDescriptor = function fromDescriptor(descriptor, syntax) {
180189
descriptor = exports.DescriptorProto.decode(descriptor);
181190

182191
// Create the message type
183-
var type = new Type(descriptor.name.length ? descriptor.name : "Type" + unnamedMessageIndex++),
192+
var type = new Type(descriptor.name.length ? descriptor.name : "Type" + unnamedMessageIndex++, fromDescriptorOptions(descriptor.options, exports.MessageOptions)),
184193
i;
185194

186195
/* Oneofs */ if (descriptor.oneofDecl)
@@ -273,8 +282,7 @@ Type.prototype.toDescriptor = function toDescriptor(syntax) {
273282
/* Ranges */ else
274283
descriptor.reservedRange.push(exports.DescriptorProto.ReservedRange.create({ start: this.reserved[i][0], end: this.reserved[i][1] }));
275284

276-
if (this.options && this.options.map_entry)
277-
descriptor.options = exports.MessageOptions.create({ map_entry: true });
285+
descriptor.options = toDescriptorOptions(this.options, exports.MessageOptions);
278286

279287
return descriptor;
280288
};
@@ -392,10 +400,25 @@ Field.fromDescriptor = function fromDescriptor(descriptor, syntax) {
392400
descriptor.extendee.length ? descriptor.extendee : undefined
393401
);
394402

395-
if (descriptor.options)
396-
field.options = fromDescriptorOptions(descriptor.options, exports.FieldOptions);
397-
if (descriptor.defaultValue && descriptor.defaultValue.length)
398-
field.setOption("default", descriptor.defaultValue);
403+
field.options = fromDescriptorOptions(descriptor.options, exports.FieldOptions);
404+
405+
if (descriptor.defaultValue && descriptor.defaultValue.length) {
406+
var defaultValue = descriptor.defaultValue;
407+
switch (defaultValue) {
408+
case "true": case "TRUE":
409+
defaultValue = true;
410+
break;
411+
case "false": case "FALSE":
412+
defaultValue = false;
413+
break;
414+
default:
415+
var match = $protobuf.parse.numberRe.exec(defaultValue);
416+
if (match)
417+
defaultValue = parseInt(defaultValue);
418+
break;
419+
}
420+
field.setOption("default", defaultValue);
421+
}
399422

400423
if (packableDescriptorType(descriptor.type)) {
401424
if (syntax === "proto3") { // defaults to packed=true (internal preset is packed=true)
@@ -522,7 +545,7 @@ Enum.fromDescriptor = function fromDescriptor(descriptor) {
522545
return new Enum(
523546
descriptor.name && descriptor.name.length ? descriptor.name : "Enum" + unnamedEnumIndex++,
524547
values,
525-
descriptor.options && descriptor.options.allowAlias ? { allowAlias: true } : undefined
548+
fromDescriptorOptions(descriptor.options, exports.EnumOptions)
526549
);
527550
};
528551

@@ -540,7 +563,8 @@ Enum.prototype.toDescriptor = function toDescriptor() {
540563

541564
return exports.EnumDescriptorProto.create({
542565
name: this.name,
543-
value: values
566+
value: values,
567+
options: toDescriptorOptions(this.options, exports.EnumOptions)
544568
});
545569
};
546570

@@ -572,6 +596,7 @@ OneOf.fromDescriptor = function fromDescriptor(descriptor) {
572596
return new OneOf(
573597
// unnamedOneOfIndex is global, not per type, because we have no ref to a type here
574598
descriptor.name && descriptor.name.length ? descriptor.name : "oneof" + unnamedOneofIndex++
599+
// fromDescriptorOptions(descriptor.options, exports.OneofOptions) - only uninterpreted_option
575600
);
576601
};
577602

@@ -583,6 +608,7 @@ OneOf.fromDescriptor = function fromDescriptor(descriptor) {
583608
OneOf.prototype.toDescriptor = function toDescriptor() {
584609
return exports.OneofDescriptorProto.create({
585610
name: this.name
611+
// options: toDescriptorOptions(this.options, exports.OneofOptions) - only uninterpreted_option
586612
});
587613
};
588614

@@ -612,7 +638,7 @@ Service.fromDescriptor = function fromDescriptor(descriptor) {
612638
if (typeof descriptor.length === "number")
613639
descriptor = exports.ServiceDescriptorProto.decode(descriptor);
614640

615-
var service = new Service(descriptor.name && descriptor.name.length ? descriptor.name : "Service" + unnamedServiceIndex++);
641+
var service = new Service(descriptor.name && descriptor.name.length ? descriptor.name : "Service" + unnamedServiceIndex++, fromDescriptorOptions(descriptor.options, exports.ServiceOptions));
616642
if (descriptor.method)
617643
for (var i = 0; i < descriptor.method.length; ++i)
618644
service.add(Method.fromDescriptor(descriptor.method[i]));
@@ -634,7 +660,8 @@ Service.prototype.toDescriptor = function toDescriptor() {
634660

635661
return exports.ServiceDescriptorProto.create({
636662
name: this.name,
637-
methods: methods
663+
methods: methods,
664+
options: toDescriptorOptions(this.options, exports.ServiceOptions)
638665
});
639666
};
640667

@@ -674,7 +701,8 @@ Method.fromDescriptor = function fromDescriptor(descriptor) {
674701
descriptor.inputType,
675702
descriptor.outputType,
676703
Boolean(descriptor.clientStreaming),
677-
Boolean(descriptor.serverStreaming)
704+
Boolean(descriptor.serverStreaming),
705+
fromDescriptorOptions(descriptor.options, exports.MethodOptions)
678706
);
679707
};
680708

@@ -689,7 +717,8 @@ Method.prototype.toDescriptor = function toDescriptor() {
689717
inputType: this.resolvedRequestType ? this.resolvedRequestType.fullName : this.requestType,
690718
outputType: this.resolvedResponseType ? this.resolvedResponseType.fullName : this.responseType,
691719
clientStreaming: this.requestStream,
692-
serverStreaming: this.responseStream
720+
serverStreaming: this.responseStream,
721+
options: toDescriptorOptions(this.options, exports.MethodOptions)
693722
});
694723
};
695724

@@ -769,20 +798,34 @@ function toDescriptorType(type, resolvedType) {
769798

770799
// Converts descriptor options to an options object
771800
function fromDescriptorOptions(options, type) {
801+
if (!options)
802+
return undefined;
772803
var out = [];
773-
for (var i = 0, key; i < type.fieldsArray.length; ++i)
774-
if ((key = type._fieldsArray[i].name) !== "uninterpretedOption")
775-
if (options.hasOwnProperty(key)) // eslint-disable-line no-prototype-builtins
776-
out.push(key, options[key]);
804+
for (var i = 0, field, key, val; i < type.fieldsArray.length; ++i)
805+
if ((key = (field = type._fieldsArray[i]).name) !== "uninterpretedOption")
806+
if (options.hasOwnProperty(key)) { // eslint-disable-line no-prototype-builtins
807+
val = options[key];
808+
if (field.resolvedType instanceof Enum && typeof val === "number" && field.resolvedType.valuesById[val] !== undefined)
809+
val = field.resolvedType.valuesById[val];
810+
out.push(underScore(key), val);
811+
}
777812
return out.length ? $protobuf.util.toObject(out) : undefined;
778813
}
779814

780815
// Converts an options object to descriptor options
781816
function toDescriptorOptions(options, type) {
817+
if (!options)
818+
return undefined;
782819
var out = [];
783-
for (var i = 0, key; i < type.fieldsArray.length; ++i)
784-
if ((key = type._fieldsArray[i].name) !== "default")
785-
out.push(key, options[key]);
820+
for (var i = 0, ks = Object.keys(options), key, val; i < ks.length; ++i) {
821+
val = options[key = ks[i]];
822+
if (key === "default")
823+
continue;
824+
var field = type.fields[key];
825+
if (!field && !(field = type.fields[key = $protobuf.parse.camelCase(key)]))
826+
continue;
827+
out.push(key, val);
828+
}
786829
return out.length ? type.fromObject($protobuf.util.toObject(out)) : undefined;
787830
}
788831

@@ -805,6 +848,13 @@ function shortname(from, to) {
805848
return toPath.slice(j).join(".");
806849
}
807850

851+
// copied here from cli/targets/proto.js
852+
function underScore(str) {
853+
return str.substring(0,1)
854+
+ str.substring(1)
855+
.replace(/([A-Z])(?=[a-z]|$)/g, function($0, $1) { return "_" + $1.toLowerCase(); });
856+
}
857+
808858
// --- exports ---
809859

810860
/**

ext/descriptor/test.js

+7-5
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ var proto = require("../../google/protobuf/descriptor.json")/*{
2727
}
2828
}*/;
2929

30-
var root = protobuf.Root.fromJSON(proto).resolveAll();
30+
// var root = protobuf.Root.fromJSON(proto).resolveAll();
31+
var root = protobuf.loadSync("tests/data/google/protobuf/descriptor.proto").resolveAll();
3132

3233
// console.log("Original proto", JSON.stringify(root, null, 2));
3334

@@ -41,12 +42,13 @@ var root2 = protobuf.Root.fromDescriptor(buf, "proto2").resolveAll();
4142
// console.log("\nDecoded proto", JSON.stringify(root2, null, 2));
4243

4344
var diff = require("deep-diff").diff(root.toJSON(), root2.toJSON());
44-
if (diff)
45+
if (diff) {
4546
diff.forEach(function(diff) {
4647
console.log(diff.kind + " @ " + diff.path.join("."));
47-
console.log("lhs:", diff.lhs);
48-
console.log("rhs:", diff.rhs);
48+
console.log("lhs:", typeof diff.lhs, diff.lhs);
49+
console.log("rhs:", typeof diff.rhs, diff.rhs);
4950
console.log();
5051
});
51-
else
52+
process.exit(1);
53+
} else
5254
console.log("no differences");

src/parse.js

+3
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ function camelCase(str) {
3535
.replace(camelCaseRe, function($0, $1) { return $1.toUpperCase(); });
3636
}
3737

38+
parse.camelCase = camelCase;
39+
parse.numberRe = numberRe;
40+
3841
/**
3942
* Result object returned from {@link parse}.
4043
* @typedef ParserResult

0 commit comments

Comments
 (0)