Skip to content

Commit 2bd0b43

Browse files
New implementation that explicitly respects the optional keyword
1 parent 94556f9 commit 2bd0b43

File tree

4 files changed

+31
-21
lines changed

4 files changed

+31
-21
lines changed

cli/README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,7 @@ Translates between file formats and generates static code.
6868
--force-long Enforces the use of 'Long' for s-/u-/int64 and s-/fixed64 fields.
6969
--force-number Enforces the use of 'number' for s-/u-/int64 and s-/fixed64 fields.
7070
--force-message Enforces the use of message instances instead of plain objects.
71-
--null-defaults Default value for optional fields is null instead of zero value.
72-
--pb3-optional Make type declarations respect optional fields for PB3.
71+
--force-optional Enforces nullable types for fields marked as optional (proto2 and proto3)
7372
7473
usage: pbjs [options] file1.proto file2.json ... (or pipe) other | pbjs [options] -
7574
```

cli/pbjs.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ exports.main = function main(args, callback) {
6262
"force-number": false,
6363
"force-enum-string": false,
6464
"force-message": false,
65-
"null-defaults": false,
66-
"pb3-optional": false
65+
"force-optional": false,
66+
"null-defaults": false
6767
}
6868
});
6969

@@ -147,9 +147,9 @@ exports.main = function main(args, callback) {
147147
" --force-long Enforces the use of 'Long' for s-/u-/int64 and s-/fixed64 fields.",
148148
" --force-number Enforces the use of 'number' for s-/u-/int64 and s-/fixed64 fields.",
149149
" --force-message Enforces the use of message instances instead of plain objects.",
150+
" --force-optional Enforces nullable types for fields marked as optional (proto2 and proto3)",
150151
"",
151-
" --null-defaults Default value for optional fields is null instead of zero value.",
152-
" --pb3-optional Make type declarations respect optional fields for PB3.",
152+
" --null-defaults Default value for optional fields is null instead of zero value (deprecated, use --force-optional instead)",
153153
"",
154154
"usage: " + chalk.bold.green("pbjs") + " [options] file1.proto file2.json ..." + chalk.gray(" (or pipe) ") + "other | " + chalk.bold.green("pbjs") + " [options] -",
155155
""

cli/targets/static.js

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -354,14 +354,6 @@ function toJsType(field) {
354354
return type;
355355
}
356356

357-
function handleOptionalFields(jsType, field) {
358-
359-
if (field.optional && !field.map && !field.repeated && field.resolvedType instanceof Type || field.partOf)
360-
return jsType + "|null|undefined";
361-
else
362-
return jsType
363-
}
364-
365357
function buildType(ref, type) {
366358

367359
if (config.comments) {
@@ -375,10 +367,13 @@ function buildType(ref, type) {
375367
prop = prop.substring(1, prop.charAt(0) === "[" ? prop.length - 1 : prop.length);
376368
var jsType = toJsType(field);
377369

378-
// Hide the fix for PB3 optional fields behind a config flag, it is an API change in generated output
379-
if (config.pb3Optional) {
380-
jsType = handleOptionalFields(jsType, field);
370+
// New behaviour - fields explicitly marked as optional and members of a one-of are nullable
371+
// Maps and repeated fields are not explicitly optional, they default to empty instances
372+
if (config["force-optional"]) {
373+
if (field.explicitOptional || field.partOf)
374+
jsType = jsType + "|null|undefined";
381375
}
376+
// Old behaviour - field.optional is true for all fields in proto3
382377
else {
383378
if (field.optional)
384379
jsType = jsType + "|null";
@@ -411,10 +406,13 @@ function buildType(ref, type) {
411406
push("");
412407
var jsType = toJsType(field);
413408

414-
// Hide the fix for PB3 optional fields behind a config flag, it is an API change in generated output
415-
if (config.pb3Optional) {
416-
jsType = handleOptionalFields(jsType, field);
409+
// New behaviour - fields explicitly marked as optional and members of a one-of are nullable
410+
// Maps and repeated fields are not explicitly optional, they default to empty instances
411+
if (config["force-optional"]) {
412+
if (field.explicitOptional || field.partOf)
413+
jsType = jsType + "|null|undefined";
417414
}
415+
// Old behaviour - field.optional is true for all fields in proto3
418416
else {
419417
if (field.optional && !field.map && !field.repeated && (field.resolvedType instanceof Type || config["null-defaults"]) || field.partOf)
420418
jsType = jsType + "|null|undefined";
@@ -430,11 +428,16 @@ function buildType(ref, type) {
430428
push("");
431429
firstField = false;
432430
}
431+
// New behaviour sets a null default when the optional keyword is used explicitly
432+
// Old behaviour considers all proto3 fields optional and uses the null-defaults config flag
433+
var nullDefault = config["force-optional"]
434+
? field.explicitOptional
435+
: field.optional && config["null-defaults"];
433436
if (field.repeated)
434437
push(escapeName(type.name) + ".prototype" + prop + " = $util.emptyArray;"); // overwritten in constructor
435438
else if (field.map)
436439
push(escapeName(type.name) + ".prototype" + prop + " = $util.emptyObject;"); // overwritten in constructor
437-
else if (field.partOf || field.optional && config["null-defaults"])
440+
else if (field.partOf || nullDefault)
438441
push(escapeName(type.name) + ".prototype" + prop + " = null;"); // do not set default value for oneof members
439442
else if (field.long)
440443
push(escapeName(type.name) + ".prototype" + prop + " = $util.Long ? $util.Long.fromBits("

src/field.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,14 @@ function Field(name, id, type, rule, extend, options, comment) {
117117
*/
118118
this.optional = !this.required;
119119

120+
/**
121+
* Whether this field is explicitly marked as optional
122+
* Proto3 fields must use the optional keyword
123+
* Maps and repeated fields are not explicitly optional
124+
* @type {boolean}
125+
*/
126+
this.explicitOptional = rule === "optional";
127+
120128
/**
121129
* Whether this field is repeated.
122130
* @type {boolean}

0 commit comments

Comments
 (0)