protobufjs-cli
Advanced tools
Comparing version 1.1.2 to 1.1.3
# Changelog | ||
## [1.1.3](https://github.com/protobufjs/protobuf.js/compare/protobufjs-cli-v1.1.2...protobufjs-cli-v1.1.3) (2024-08-16) | ||
### Bug Fixes | ||
* handle nullability for optional fields ([59569c1](https://github.com/protobufjs/protobuf.js/commit/59569c12c85c1c7b783ace9a71775b1d05a08e9c)) | ||
## [1.1.2](https://github.com/protobufjs/protobuf.js/compare/protobufjs-cli-v1.1.1...protobufjs-cli-v1.1.2) (2023-08-21) | ||
@@ -4,0 +11,0 @@ |
@@ -395,3 +395,3 @@ "use strict"; | ||
else if (element.properties && element.properties.length) | ||
element.properties.forEach(writeProperty); | ||
element.properties.forEach((property) => writeProperty(property)); | ||
--indent; | ||
@@ -398,0 +398,0 @@ write("}"); |
{ | ||
"name": "protobufjs-cli", | ||
"description": "Translates between file formats and generates static code as well as TypeScript definitions.", | ||
"version": "1.1.2", | ||
"version": "1.1.3", | ||
"author": "Daniel Wirtz <dcode+protobufjs@dcode.io>", | ||
@@ -6,0 +6,0 @@ "repository": { |
@@ -44,3 +44,3 @@ "use strict"; | ||
string: [ "target", "out", "path", "wrap", "dependency", "root", "lint", "filter" ], | ||
boolean: [ "create", "encode", "decode", "verify", "convert", "delimited", "typeurl", "beautify", "comments", "service", "es6", "sparse", "keep-case", "alt-comment", "force-long", "force-number", "force-enum-string", "force-message", "null-defaults" ], | ||
boolean: [ "create", "encode", "decode", "verify", "convert", "delimited", "typeurl", "beautify", "comments", "service", "es6", "sparse", "keep-case", "alt-comment", "force-long", "force-number", "force-enum-string", "force-message", "null-defaults", "null-semantics"], | ||
default: { | ||
@@ -67,2 +67,3 @@ target: "json", | ||
"null-defaults": false, | ||
"null-semantics": false | ||
} | ||
@@ -153,2 +154,3 @@ }); | ||
" --null-defaults Default value for optional fields is null instead of zero value.", | ||
" --null-semantics Make nullable fields match protobuf semantics (overrides --null-defaults).", | ||
"", | ||
@@ -155,0 +157,0 @@ "usage: " + chalk.bold.green("pbjs") + " [options] file1.proto file2.json ..." + chalk.gray(" (or pipe) ") + "other | " + chalk.bold.green("pbjs") + " [options] -", |
@@ -71,2 +71,5 @@ protobufjs-cli | ||
--force-message Enforces the use of message instances instead of plain objects. | ||
--null-defaults Default value for optional fields is null instead of zero value. | ||
--null-semantics Make nullable fields match protobuf semantics (overrides --null-defaults). | ||
@@ -73,0 +76,0 @@ usage: pbjs [options] file1.proto file2.json ... (or pipe) other | pbjs [options] - |
@@ -314,5 +314,11 @@ "use strict"; | ||
function toJsType(field) { | ||
function toJsType(field, parentIsInterface = false) { | ||
var type; | ||
// With null semantics, interfaces are composed from interfaces and messages from messages | ||
// Without null semantics, child types depend on the --force-message flag | ||
var asInterface = config["null-semantics"] | ||
? parentIsInterface && !(field.resolvedType instanceof protobuf.Enum) | ||
: !(field.resolvedType instanceof protobuf.Enum || config.forceMessage); | ||
switch (field.type) { | ||
@@ -345,6 +351,8 @@ case "double": | ||
default: | ||
if (field.resolve().resolvedType) | ||
type = exportName(field.resolvedType, !(field.resolvedType instanceof protobuf.Enum || config.forceMessage)); | ||
else | ||
if (field.resolve().resolvedType) { | ||
type = exportName(field.resolvedType, asInterface); | ||
} | ||
else { | ||
type = "*"; // should not happen | ||
} | ||
break; | ||
@@ -359,4 +367,68 @@ } | ||
function syntaxForType(type) { | ||
var syntax = null; | ||
var namespace = type; | ||
while (syntax === null && namespace !== null) { | ||
if (namespace.options != null && "syntax" in namespace.options) { | ||
syntax = namespace.options["syntax"]; | ||
} | ||
else { | ||
namespace = namespace.parent; | ||
} | ||
} | ||
return syntax !== null ? syntax : "proto2"; | ||
} | ||
function isExplicitPresence(field, syntax) { | ||
// In proto3, optional fields are explicit | ||
if (syntax === "proto3") { | ||
return field.options != null && field.options["proto3_optional"] === true; | ||
} | ||
// In proto2, fields are explicitly optional if they are not part of a map, array or oneOf group | ||
if (syntax === "proto2") { | ||
return field.optional && !(field.partOf || field.repeated || field.map); | ||
} | ||
throw new Error("Unknown proto syntax: [" + syntax + "]"); | ||
} | ||
function isImplicitPresence(field, syntax) { | ||
// In proto3, everything not marked optional has implicit presence (including maps and repeated fields) | ||
if (syntax === "proto3") { | ||
return field.options == null || field.options["proto3_optional"] !== true; | ||
} | ||
// In proto2, nothing has implicit presence | ||
if (syntax === "proto2") { | ||
return false; | ||
} | ||
throw new Error("Unknown proto syntax: [" + syntax + "]"); | ||
} | ||
function isOptionalOneOf(oneof, syntax) { | ||
if (syntax === "proto2") { | ||
return false; | ||
} | ||
if (oneof.fieldsArray == null || oneof.fieldsArray.length !== 1) { | ||
return false; | ||
} | ||
var field = oneof.fieldsArray[0]; | ||
return field.options != null && field.options["proto3_optional"] === true; | ||
} | ||
function buildType(ref, type) { | ||
var syntax = syntaxForType(type); | ||
if (config.comments) { | ||
@@ -371,6 +443,26 @@ var typeDef = [ | ||
prop = prop.substring(1, prop.charAt(0) === "[" ? prop.length - 1 : prop.length); | ||
var jsType = toJsType(field); | ||
if (field.optional) | ||
jsType = jsType + "|null"; | ||
typeDef.push("@property {" + jsType + "} " + (field.optional ? "[" + prop + "]" : prop) + " " + (field.comment || type.name + " " + field.name)); | ||
var jsType = toJsType(field, /* parentIsInterface = */ true); | ||
var nullable = false; | ||
if (config["null-semantics"]) { | ||
// With semantic nulls, only explicit optional fields and one-of members can be set to null | ||
// Implicit fields (proto3), maps and lists can be omitted, but if specified must be non-null | ||
// Implicit fields will take their default value when the message is constructed | ||
if (isExplicitPresence(field, syntax) || field.partOf) { | ||
jsType = jsType + "|null|undefined"; | ||
nullable = true; | ||
} | ||
else if (isImplicitPresence(field, syntax) || field.repeated || field.map) { | ||
jsType = jsType + "|undefined"; | ||
nullable = true; | ||
} | ||
} | ||
else { | ||
// Without semantic nulls, everything is optional in proto3 | ||
// Do not allow |undefined to keep backwards compatibility | ||
if (field.optional) { | ||
jsType = jsType + "|null"; | ||
nullable = true; | ||
} | ||
} | ||
typeDef.push("@property {" + jsType + "} " + (nullable ? "[" + prop + "]" : prop) + " " + (field.comment || type.name + " " + field.name)); | ||
}); | ||
@@ -400,5 +492,18 @@ push(""); | ||
push(""); | ||
var jsType = toJsType(field); | ||
if (field.optional && !field.map && !field.repeated && (field.resolvedType instanceof Type || config["null-defaults"]) || field.partOf) | ||
jsType = jsType + "|null|undefined"; | ||
var jsType = toJsType(field, /* parentIsInterface = */ false); | ||
if (config["null-semantics"]) { | ||
// With semantic nulls, fields are nullable if they are explicitly optional or part of a one-of | ||
// Maps, repeated values and fields with implicit defaults are never null after construction | ||
// Members are never undefined, at a minimum they are initialized to null | ||
if (isExplicitPresence(field, syntax) || field.partOf) { | ||
jsType = jsType + "|null"; | ||
} | ||
} | ||
else { | ||
// Without semantic nulls, everything is optional in proto3 | ||
// Keep |undefined for backwards compatibility | ||
if (field.optional && !field.map && !field.repeated && (field.resolvedType instanceof Type || config["null-defaults"]) || field.partOf) { | ||
jsType = jsType + "|null|undefined"; | ||
} | ||
} | ||
pushComment([ | ||
@@ -414,2 +519,7 @@ field.comment || type.name + " " + field.name + ".", | ||
} | ||
// With semantic nulls, only explict optional fields and one-of members are null by default | ||
// Otherwise use field.optional, which doesn't consider proto3, maps, repeated fields etc. | ||
var nullDefault = config["null-semantics"] | ||
? isExplicitPresence(field, syntax) | ||
: field.optional && config["null-defaults"]; | ||
if (field.repeated) | ||
@@ -419,3 +529,3 @@ push(escapeName(type.name) + ".prototype" + prop + " = $util.emptyArray;"); // overwritten in constructor | ||
push(escapeName(type.name) + ".prototype" + prop + " = $util.emptyObject;"); // overwritten in constructor | ||
else if (field.partOf || field.optional && config["null-defaults"]) | ||
else if (field.partOf || nullDefault) | ||
push(escapeName(type.name) + ".prototype" + prop + " = null;"); // do not set default value for oneof members | ||
@@ -446,8 +556,13 @@ else if (field.long) | ||
push(""); | ||
pushComment([ | ||
oneof.comment || type.name + " " + oneof.name + ".", | ||
"@member {" + oneof.oneof.map(JSON.stringify).join("|") + "|undefined} " + escapeName(oneof.name), | ||
"@memberof " + exportName(type), | ||
"@instance" | ||
]); | ||
if (isOptionalOneOf(oneof, syntax)) { | ||
push("// Virtual OneOf for proto3 optional field"); | ||
} | ||
else { | ||
pushComment([ | ||
oneof.comment || type.name + " " + oneof.name + ".", | ||
"@member {" + oneof.oneof.map(JSON.stringify).join("|") + "|undefined} " + escapeName(oneof.name), | ||
"@memberof " + exportName(type), | ||
"@instance" | ||
]); | ||
} | ||
push("Object.defineProperty(" + escapeName(type.name) + ".prototype, " + JSON.stringify(oneof.name) +", {"); | ||
@@ -454,0 +569,0 @@ ++indent; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
117290
2603
177