Skip to content

feat!: add Edition 2024 Support #2053

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

Closed
wants to merge 13 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file modified cli/bin/pbjs
100644 → 100755
Empty file.
Empty file modified cli/bin/pbts
100644 → 100755
Empty file.
43 changes: 29 additions & 14 deletions cli/targets/proto.js
Original file line number Diff line number Diff line change
@@ -184,8 +184,12 @@ function buildType(type) {
}

function buildField(field, passExtend) {
if (field.partOf || field.declaringField || field.extend !== undefined && !passExtend)
if (field.partOf && !field.partOf.isProto3Optional) {
return;
}
if (field.declaringField || field.extend !== undefined && !passExtend) {
return;
}
if (first) {
first = false;
push("");
@@ -199,8 +203,10 @@ function buildField(field, passExtend) {
sb.push("map<" + field.keyType + ", " + field.type + ">");
else if (field.repeated)
sb.push("repeated", field.type);
else if (syntax === 2 || field.parent.group)
else if (syntax === 2)
sb.push(field.required ? "required" : "optional", field.type);
else if (syntax === 3 && field.hasPresence)
sb.push("optional", field.type);
else
sb.push(field.type);
sb.push(underScore(field.name), "=", field.id);
@@ -211,7 +217,7 @@ function buildField(field, passExtend) {
}

function buildGroup(field) {
push(field.rule + " group " + field.resolvedType.name + " = " + field.id + " {");
push((field.rule || "optional") + " group " + field.resolvedType.name + " = " + field.id + " {");
++indent;
buildOptions(field.resolvedType);
first = true;
@@ -223,20 +229,16 @@ function buildGroup(field) {
}

function buildFieldOptions(field) {
var keys;
if (!field.options || !(keys = Object.keys(field.options)).length)
return null;
var keys = [];
if (field.options) {
keys = Object.keys(field.options);
}
var sb = [];
keys.forEach(function(key) {
if (key === "proto3_optional" || key === "packed" || key === "features") return;

var val = field.options[key];
var wireType = types.packed[field.resolvedType instanceof Enum ? "int32" : field.type];
switch (key) {
case "packed":
val = Boolean(val);
// skip when not packable or syntax default
if (wireType === undefined || syntax === 3 === val)
return;
break;
case "default":
if (syntax === 3)
return;
@@ -253,6 +255,14 @@ function buildFieldOptions(field) {
}
sb.push(key + "=" + val);
});
var packable = types.packed[field.resolvedType instanceof Enum ? "int32" : field.type];
if (packable !== undefined) {
if (field.packed && syntax === 2) {
sb.push("packed=true");
} else if(!field.packed && syntax === 3) {
sb.push("packed=false");
}
}
return sb.length
? "[" + sb.join(", ") + "]"
: null;
@@ -282,6 +292,10 @@ function consolidateExtends(nested) {
}

function buildOneOf(oneof) {
if (oneof.isProto3Optional) {
return;
}

push("");
push("oneof " + underScore(oneof.name) + " {");
++indent; first = true;
@@ -311,11 +325,12 @@ function buildMethod(method) {
push(method.type + " " + method.name + " (" + (method.requestStream ? "stream " : "") + method.requestType + ") returns (" + (method.responseStream ? "stream " : "") + method.responseType + ");");
}

function buildOptions(object) {
function buildOptions(object, ignore = []) {
if (!object.options)
return;
first = true;
Object.keys(object.options).forEach(function(key) {
if (ignore.includes(key) || key === "features") return;
if (first) {
first = false;
push("");
86 changes: 14 additions & 72 deletions cli/targets/static.js
Original file line number Diff line number Diff line change
@@ -362,72 +362,12 @@ function toJsType(field, parentIsInterface = false) {
return type;
}

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 isNullable(field) {
return field.hasPresence && !field.required;
}

function buildType(ref, type) {

var syntax = syntaxForType(type);

if (config.comments) {
var typeDef = [
"Properties of " + aOrAn(type.name) + ".",
@@ -443,13 +383,15 @@ function buildType(ref, type) {
// 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;
if (field.optional) {
if (isNullable(field)) {
jsType = jsType + "|null|undefined";
nullable = true;
}
else {
jsType = jsType + "|undefined";
nullable = true;
}
}
}
else {
@@ -490,7 +432,7 @@ function buildType(ref, type) {
// 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) {
if (isNullable(field)) {
jsType = jsType + "|null";
}
}
@@ -514,7 +456,7 @@ function buildType(ref, type) {
// 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)
? isNullable(field)
: field.optional && config["null-defaults"];
if (field.repeated)
push(escapeName(type.name) + ".prototype" + prop + " = $util.emptyArray;"); // overwritten in constructor
@@ -546,7 +488,7 @@ function buildType(ref, type) {
}
oneof.resolve();
push("");
if (isOptionalOneOf(oneof, syntax)) {
if (oneof.isProto3Optional) {
push("// Virtual OneOf for proto3 optional field");
}
else {
Loading
Oops, something went wrong.
Loading
Oops, something went wrong.