ts-proto 1.161.0 → 1.162.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.markdown CHANGED
@@ -477,6 +477,8 @@ Generated code will be placed in the Gradle build directory.
477
477
 
478
478
  Note that, as indicated, this means Object.keys will not include set-by-default fields, so if you have code that iterates over messages keys in a generic fashion, it will have to also iterate over keys inherited from the prototype.
479
479
 
480
+ - With `--ts_proto_opt=useJsonName=true`, `json_name` defined in protofiles will be used instead of message field names.
481
+
480
482
  - With `--ts_proto_opt=useJsonWireFormat=true`, the generated code will reflect the JSON representation of Protobuf messages.
481
483
 
482
484
  Requires `onlyTypes=true`. Implies `useDate=string` and `stringEnums=true`. This option is to generate types that can be directly used with marshalling/unmarshalling Protobuf messages serialized as JSON.
package/build/main.js CHANGED
@@ -464,12 +464,13 @@ function makeTimestampMethods(options, longs, bytes) {
464
464
  const Timestamp = (0, utils_1.impProto)(options, "google/protobuf/timestamp", "Timestamp");
465
465
  let seconds = "date.getTime() / 1_000";
466
466
  let toNumberCode = "t.seconds";
467
+ const makeToNumberCode = (methodCall) => `t.seconds${options.useOptionals === "all" ? "?" : ""}.${methodCall}`;
467
468
  if (options.forceLong === options_1.LongOption.LONG) {
468
- toNumberCode = "t.seconds.toNumber()";
469
+ toNumberCode = makeToNumberCode("toNumber()");
469
470
  seconds = (0, ts_poet_1.code) `${longs.numberToLong}(date.getTime() / 1_000)`;
470
471
  }
471
472
  else if (options.forceLong === options_1.LongOption.BIGINT) {
472
- toNumberCode = (0, ts_poet_1.code) `${bytes.globalThis}.Number(t.seconds.toString())`;
473
+ toNumberCode = (0, ts_poet_1.code) `${bytes.globalThis}.Number(${makeToNumberCode("toString()")})`;
473
474
  seconds = (0, ts_poet_1.code) `BigInt(Math.trunc(date.getTime() / 1_000))`;
474
475
  }
475
476
  else if (options.forceLong === options_1.LongOption.STRING) {
@@ -614,10 +615,10 @@ function generateInterfaceDeclaration(ctx, fullName, messageDesc, sourceInfo, fu
614
615
  }
615
616
  const info = sourceInfo.lookup(sourceInfo_1.Fields.message.field, index);
616
617
  (0, utils_1.maybeAddComment)(info, chunks, (_a = fieldDesc.options) === null || _a === void 0 ? void 0 : _a.deprecated);
617
- const name = (0, case_1.maybeSnakeToCamel)(fieldDesc.name, options);
618
+ const { validatedPropertyName } = (0, utils_1.propertyNameComposition)(fieldDesc, options);
618
619
  const isOptional = (0, types_1.isOptionalProperty)(fieldDesc, messageDesc.options, options);
619
620
  const type = (0, types_1.toTypeName)(ctx, messageDesc, fieldDesc, isOptional);
620
- chunks.push((0, ts_poet_1.code) `${maybeReadonly(options)}${name}${isOptional ? "?" : ""}: ${type}, `);
621
+ chunks.push((0, ts_poet_1.code) `${maybeReadonly(options)}${validatedPropertyName}${isOptional ? "?" : ""}: ${type}, `);
621
622
  });
622
623
  if (ctx.options.unknownFields) {
623
624
  chunks.push((0, ts_poet_1.code) `_unknownFields?: {[key: number]: Uint8Array[]} | undefined,`);
@@ -668,7 +669,9 @@ function generateBaseInstanceFactory(ctx, fullName, messageDesc, fullTypeName) {
668
669
  const { oneofIndex } = field;
669
670
  if (!processedOneofs.has(oneofIndex)) {
670
671
  processedOneofs.add(oneofIndex);
671
- const name = (0, case_1.maybeSnakeToCamel)(messageDesc.oneofDecl[oneofIndex].name, ctx.options);
672
+ const name = options.useJsonName
673
+ ? (0, utils_1.propertyNameComposition)(field, options).validatedPropertyName
674
+ : (0, case_1.maybeSnakeToCamel)(messageDesc.oneofDecl[oneofIndex].name, ctx.options);
672
675
  fields.push((0, ts_poet_1.code) `${name}: undefined`);
673
676
  }
674
677
  continue;
@@ -676,7 +679,7 @@ function generateBaseInstanceFactory(ctx, fullName, messageDesc, fullTypeName) {
676
679
  if (!options.initializeFieldsAsUndefined && (0, types_1.isOptionalProperty)(field, messageDesc.options, options)) {
677
680
  continue;
678
681
  }
679
- const name = (0, case_1.maybeSnakeToCamel)(field.name, ctx.options);
682
+ const { validatedPropertyName } = (0, utils_1.propertyNameComposition)(field, options);
680
683
  const val = (0, types_1.isWithinOneOf)(field)
681
684
  ? "undefined"
682
685
  : (0, types_1.isMapType)(ctx, messageDesc, field)
@@ -686,7 +689,7 @@ function generateBaseInstanceFactory(ctx, fullName, messageDesc, fullTypeName) {
686
689
  : (0, types_1.isRepeated)(field)
687
690
  ? "[]"
688
691
  : (0, types_1.defaultValue)(ctx, field);
689
- fields.push((0, ts_poet_1.code) `${name}: ${val}`);
692
+ fields.push((0, ts_poet_1.code) `${validatedPropertyName}: ${val}`);
690
693
  }
691
694
  if ((0, options_1.addTypeToMessages)(options)) {
692
695
  fields.unshift((0, ts_poet_1.code) `$type: '${fullTypeName}'`);
@@ -794,7 +797,8 @@ function generateDecode(ctx, fullName, messageDesc) {
794
797
  `);
795
798
  // add a case for each incoming field
796
799
  messageDesc.field.forEach((field) => {
797
- const fieldName = (0, case_1.maybeSnakeToCamel)(field.name, options);
800
+ const { propertyName } = (0, utils_1.propertyNameComposition)(field, options);
801
+ const messageProperty = (0, utils_1.getPropertyAccessor)("message", propertyName);
798
802
  chunks.push((0, ts_poet_1.code) `case ${field.number}:`);
799
803
  const tag = ((field.number << 3) | (0, types_1.basicWireType)(field.type)) >>> 0;
800
804
  const tagCheck = (0, ts_poet_1.code) `
@@ -815,15 +819,15 @@ function generateDecode(ctx, fullName, messageDesc) {
815
819
  const generateMapType = (0, types_1.shouldGenerateJSMapType)(ctx, messageDesc, field);
816
820
  let valueSetterSnippet;
817
821
  if (generateMapType) {
818
- valueSetterSnippet = `message.${fieldName}${maybeNonNullAssertion}.set(${varName}.key, ${varName}.value)`;
822
+ valueSetterSnippet = `${messageProperty}${maybeNonNullAssertion}.set(${varName}.key, ${varName}.value)`;
819
823
  }
820
824
  else {
821
- valueSetterSnippet = `message.${fieldName}${maybeNonNullAssertion}[${varName}.key] = ${varName}.value`;
825
+ valueSetterSnippet = `${messageProperty}${maybeNonNullAssertion}[${varName}.key] = ${varName}.value`;
822
826
  }
823
827
  const initializerSnippet = initializerNecessary
824
828
  ? `
825
- if (message.${fieldName} === undefined) {
826
- message.${fieldName} = ${generateMapType ? "new Map()" : "{}"};
829
+ if (${messageProperty} === undefined) {
830
+ ${messageProperty} = ${generateMapType ? "new Map()" : "{}"};
827
831
  }`
828
832
  : "";
829
833
  chunks.push((0, ts_poet_1.code) `
@@ -838,15 +842,15 @@ function generateDecode(ctx, fullName, messageDesc) {
838
842
  else {
839
843
  const initializerSnippet = initializerNecessary
840
844
  ? `
841
- if (message.${fieldName} === undefined) {
842
- message.${fieldName} = [];
845
+ if (${messageProperty} === undefined) {
846
+ ${messageProperty} = [];
843
847
  }`
844
848
  : "";
845
849
  if ((0, types_1.packedType)(field.type) === undefined) {
846
850
  chunks.push((0, ts_poet_1.code) `
847
851
  ${tagCheck}
848
852
  ${initializerSnippet}
849
- message.${fieldName}${maybeNonNullAssertion}.push(${readSnippet});
853
+ ${messageProperty}${maybeNonNullAssertion}.push(${readSnippet});
850
854
  `);
851
855
  }
852
856
  else {
@@ -854,7 +858,7 @@ function generateDecode(ctx, fullName, messageDesc) {
854
858
  chunks.push((0, ts_poet_1.code) `
855
859
  if (tag === ${tag}) {
856
860
  ${initializerSnippet}
857
- message.${fieldName}${maybeNonNullAssertion}.push(${readSnippet});
861
+ ${messageProperty}${maybeNonNullAssertion}.push(${readSnippet});
858
862
 
859
863
  continue;
860
864
  }
@@ -863,7 +867,7 @@ function generateDecode(ctx, fullName, messageDesc) {
863
867
  ${initializerSnippet}
864
868
  const end2 = reader.uint32() + reader.pos;
865
869
  while (reader.pos < end2) {
866
- message.${fieldName}${maybeNonNullAssertion}.push(${readSnippet});
870
+ ${messageProperty}${maybeNonNullAssertion}.push(${readSnippet});
867
871
  }
868
872
 
869
873
  continue;
@@ -875,16 +879,18 @@ function generateDecode(ctx, fullName, messageDesc) {
875
879
  }
876
880
  }
877
881
  else if ((0, types_1.isWithinOneOfThatShouldBeUnion)(options, field)) {
878
- let oneofName = (0, case_1.maybeSnakeToCamel)(messageDesc.oneofDecl[field.oneofIndex].name, options);
882
+ const oneofNameWithMessage = options.useJsonName
883
+ ? messageProperty
884
+ : (0, utils_1.getPropertyAccessor)("message", (0, case_1.maybeSnakeToCamel)(messageDesc.oneofDecl[field.oneofIndex].name, options));
879
885
  chunks.push((0, ts_poet_1.code) `
880
886
  ${tagCheck}
881
- message.${oneofName} = { $case: '${fieldName}', ${fieldName}: ${readSnippet} };
887
+ ${oneofNameWithMessage} = { $case: '${propertyName}', ${propertyName}: ${readSnippet} };
882
888
  `);
883
889
  }
884
890
  else {
885
891
  chunks.push((0, ts_poet_1.code) `
886
892
  ${tagCheck}
887
- message.${fieldName} = ${readSnippet};
893
+ ${messageProperty} = ${readSnippet};
888
894
  `);
889
895
  }
890
896
  if (!(0, types_1.isRepeated)(field) || (0, types_1.packedType)(field.type) === undefined) {
@@ -1019,7 +1025,8 @@ function generateEncode(ctx, fullName, messageDesc) {
1019
1025
  .reduce((result, field) => ((result[field.oneofIndex] || (result[field.oneofIndex] = [])).push(field), result), {});
1020
1026
  // then add a case for each field
1021
1027
  messageDesc.field.forEach((field) => {
1022
- const fieldName = (0, case_1.maybeSnakeToCamel)(field.name, options);
1028
+ const { propertyName, validatedPropertyName } = (0, utils_1.propertyNameComposition)(field, options);
1029
+ const messageProperty = (0, utils_1.getPropertyAccessor)("message", propertyName);
1023
1030
  // get a generic writer.doSomething based on the basic type
1024
1031
  const writeSnippet = getEncodeWriteSnippet(ctx, field);
1025
1032
  const isOptional = (0, types_1.isOptionalProperty)(field, messageDesc.options, options);
@@ -1038,14 +1045,14 @@ function generateEncode(ctx, fullName, messageDesc) {
1038
1045
  const optionalAlternative = isOptional ? (useMapType ? " || new Map()" : " || {}") : "";
1039
1046
  if (useMapType) {
1040
1047
  chunks.push((0, ts_poet_1.code) `
1041
- (message.${fieldName}${optionalAlternative}).forEach((value, key) => {
1048
+ (${messageProperty}${optionalAlternative}).forEach((value, key) => {
1042
1049
  ${entryWriteSnippet}
1043
1050
  });
1044
1051
  `);
1045
1052
  }
1046
1053
  else {
1047
1054
  chunks.push((0, ts_poet_1.code) `
1048
- Object.entries(message.${fieldName}${optionalAlternative}).forEach(([key, value]) => {
1055
+ Object.entries(${messageProperty}${optionalAlternative}).forEach(([key, value]) => {
1049
1056
  ${entryWriteSnippet}
1050
1057
  });
1051
1058
  `);
@@ -1053,13 +1060,13 @@ function generateEncode(ctx, fullName, messageDesc) {
1053
1060
  }
1054
1061
  else if ((0, types_1.packedType)(field.type) === undefined) {
1055
1062
  const listWriteSnippet = (0, ts_poet_1.code) `
1056
- for (const v of message.${fieldName}) {
1063
+ for (const v of ${messageProperty}) {
1057
1064
  ${writeSnippet("v!")};
1058
1065
  }
1059
1066
  `;
1060
1067
  if (isOptional) {
1061
1068
  chunks.push((0, ts_poet_1.code) `
1062
- if (message.${fieldName} !== undefined && message.${fieldName}.length !== 0) {
1069
+ if (${messageProperty} !== undefined && ${messageProperty}.length !== 0) {
1063
1070
  ${listWriteSnippet}
1064
1071
  }
1065
1072
  `);
@@ -1077,14 +1084,14 @@ function generateEncode(ctx, fullName, messageDesc) {
1077
1084
  const toNumber = (0, types_1.getEnumMethod)(ctx, field.typeName, "ToNumber");
1078
1085
  const listWriteSnippet = (0, ts_poet_1.code) `
1079
1086
  writer.uint32(${tag}).fork();
1080
- for (const v of message.${fieldName}) {
1087
+ for (const v of ${messageProperty}) {
1081
1088
  writer.${(0, types_1.toReaderCall)(field)}(${toNumber}(v));
1082
1089
  }
1083
1090
  writer.ldelim();
1084
1091
  `;
1085
1092
  if (isOptional) {
1086
1093
  chunks.push((0, ts_poet_1.code) `
1087
- if (message.${fieldName} !== undefined && message.${fieldName}.length !== 0) {
1094
+ if (${messageProperty} !== undefined && ${messageProperty}.length !== 0) {
1088
1095
  ${listWriteSnippet}
1089
1096
  }
1090
1097
  `);
@@ -1099,7 +1106,7 @@ function generateEncode(ctx, fullName, messageDesc) {
1099
1106
  const rhs = (x) => ((0, types_1.isLong)(field) && options.forceLong === options_1.LongOption.BIGINT ? `${x}.toString()` : x);
1100
1107
  let listWriteSnippet = (0, ts_poet_1.code) `
1101
1108
  writer.uint32(${tag}).fork();
1102
- for (const v of message.${fieldName}) {
1109
+ for (const v of ${messageProperty}) {
1103
1110
  writer.${(0, types_1.toReaderCall)(field)}(${rhs("v")});
1104
1111
  }
1105
1112
  writer.ldelim();
@@ -1112,9 +1119,9 @@ function generateEncode(ctx, fullName, messageDesc) {
1112
1119
  case "sfixed64":
1113
1120
  listWriteSnippet = (0, ts_poet_1.code) `
1114
1121
  writer.uint32(${tag}).fork();
1115
- for (const v of message.${fieldName}) {
1122
+ for (const v of ${messageProperty}) {
1116
1123
  if (BigInt.asIntN(64, v) !== v) {
1117
- throw new Error('a value provided in array field ${fieldName} of type ${fieldType} is too large');
1124
+ throw new Error('a value provided in array field ${propertyName} of type ${fieldType} is too large');
1118
1125
  }
1119
1126
  writer.${(0, types_1.toReaderCall)(field)}(${rhs("v")});
1120
1127
  }
@@ -1125,9 +1132,9 @@ function generateEncode(ctx, fullName, messageDesc) {
1125
1132
  case "fixed64":
1126
1133
  listWriteSnippet = (0, ts_poet_1.code) `
1127
1134
  writer.uint32(${tag}).fork();
1128
- for (const v of message.${fieldName}) {
1135
+ for (const v of ${messageProperty}) {
1129
1136
  if (BigInt.asUintN(64, v) !== v) {
1130
- throw new Error('a value provided in array field ${fieldName} of type ${fieldType} is too large');
1137
+ throw new Error('a value provided in array field ${propertyName} of type ${fieldType} is too large');
1131
1138
  }
1132
1139
  writer.${(0, types_1.toReaderCall)(field)}(${rhs("v")});
1133
1140
  }
@@ -1140,7 +1147,7 @@ function generateEncode(ctx, fullName, messageDesc) {
1140
1147
  }
1141
1148
  if (isOptional) {
1142
1149
  chunks.push((0, ts_poet_1.code) `
1143
- if (message.${fieldName} !== undefined && message.${fieldName}.length !== 0) {
1150
+ if (${messageProperty} !== undefined && ${messageProperty}.length !== 0) {
1144
1151
  ${listWriteSnippet}
1145
1152
  }
1146
1153
  `);
@@ -1153,13 +1160,15 @@ function generateEncode(ctx, fullName, messageDesc) {
1153
1160
  else if ((0, types_1.isWithinOneOfThatShouldBeUnion)(options, field)) {
1154
1161
  if (!processedOneofs.has(field.oneofIndex)) {
1155
1162
  processedOneofs.add(field.oneofIndex);
1156
- const oneofName = (0, case_1.maybeSnakeToCamel)(messageDesc.oneofDecl[field.oneofIndex].name, options);
1157
- chunks.push((0, ts_poet_1.code) `switch (message.${oneofName}?.$case) {`);
1163
+ const oneofNameWithMessage = options.useJsonName
1164
+ ? messageProperty
1165
+ : (0, utils_1.getPropertyAccessor)("message", (0, case_1.maybeSnakeToCamel)(messageDesc.oneofDecl[field.oneofIndex].name, options));
1166
+ chunks.push((0, ts_poet_1.code) `switch (${oneofNameWithMessage}?.$case) {`);
1158
1167
  for (const oneOfField of oneOfFieldsDict[field.oneofIndex]) {
1159
1168
  const writeSnippet = getEncodeWriteSnippet(ctx, oneOfField);
1160
1169
  const oneOfFieldName = (0, case_1.maybeSnakeToCamel)(oneOfField.name, ctx.options);
1161
1170
  chunks.push((0, ts_poet_1.code) `case "${oneOfFieldName}":
1162
- ${writeSnippet(`message.${oneofName}.${oneOfFieldName}`)};
1171
+ ${writeSnippet(`${oneofNameWithMessage}.${oneOfFieldName}`)};
1163
1172
  break;`);
1164
1173
  }
1165
1174
  chunks.push((0, ts_poet_1.code) `}`);
@@ -1168,27 +1177,27 @@ function generateEncode(ctx, fullName, messageDesc) {
1168
1177
  else if ((0, types_1.isWithinOneOf)(field)) {
1169
1178
  // Oneofs don't have a default value check b/c they need to denote which-oneof presence
1170
1179
  chunks.push((0, ts_poet_1.code) `
1171
- if (message.${fieldName} !== undefined) {
1172
- ${writeSnippet(`message.${fieldName}`)};
1180
+ if (${messageProperty} !== undefined) {
1181
+ ${writeSnippet(`${messageProperty}`)};
1173
1182
  }
1174
1183
  `);
1175
1184
  }
1176
1185
  else if ((0, types_1.isMessage)(field)) {
1177
1186
  chunks.push((0, ts_poet_1.code) `
1178
- if (message.${fieldName} !== undefined) {
1179
- ${writeSnippet(`message.${fieldName}`)};
1187
+ if (${messageProperty} !== undefined) {
1188
+ ${writeSnippet(`${messageProperty}`)};
1180
1189
  }
1181
1190
  `);
1182
1191
  }
1183
1192
  else if ((0, types_1.isScalar)(field) || (0, types_1.isEnum)(field)) {
1184
1193
  chunks.push((0, ts_poet_1.code) `
1185
- if (${(0, types_1.notDefaultCheck)(ctx, field, messageDesc.options, `message.${fieldName}`)}) {
1186
- ${writeSnippet(`message.${fieldName}`)};
1194
+ if (${(0, types_1.notDefaultCheck)(ctx, field, messageDesc.options, `${messageProperty}`)}) {
1195
+ ${writeSnippet(`${messageProperty}`)};
1187
1196
  }
1188
1197
  `);
1189
1198
  }
1190
1199
  else {
1191
- chunks.push((0, ts_poet_1.code) `${writeSnippet(`message.${fieldName}`)};`);
1200
+ chunks.push((0, ts_poet_1.code) `${writeSnippet(`${messageProperty}`)};`);
1192
1201
  }
1193
1202
  });
1194
1203
  if (options.unknownFields) {
@@ -1460,7 +1469,7 @@ function generateFromJson(ctx, fullName, fullTypeName, messageDesc) {
1460
1469
  // add a check for each incoming field
1461
1470
  messageDesc.field.forEach((field) => {
1462
1471
  var _a;
1463
- const fieldName = (0, case_1.maybeSnakeToCamel)(field.name, options);
1472
+ const { validatedPropertyName: fieldName } = (0, utils_1.propertyNameComposition)(field, options);
1464
1473
  const jsonName = (0, utils_1.getFieldJsonName)(field, options);
1465
1474
  const jsonProperty = (0, utils_1.getPropertyAccessor)("object", jsonName);
1466
1475
  const jsonPropertyOptional = (0, utils_1.getPropertyAccessor)("object", jsonName, true);
@@ -1702,9 +1711,10 @@ function generateToJson(ctx, fullName, fullProtobufTypeName, messageDesc) {
1702
1711
  `);
1703
1712
  // then add a case for each field
1704
1713
  messageDesc.field.forEach((field) => {
1705
- const fieldName = (0, case_1.maybeSnakeToCamel)(field.name, options);
1714
+ const { validatedPropertyName: fieldName, propertyName } = (0, utils_1.propertyNameComposition)(field, options);
1706
1715
  const jsonName = (0, utils_1.getFieldJsonName)(field, options);
1707
1716
  const jsonProperty = (0, utils_1.getPropertyAccessor)("obj", jsonName);
1717
+ const messageProperty = (0, utils_1.getPropertyAccessor)("message", propertyName);
1708
1718
  const readSnippet = (from) => {
1709
1719
  if ((0, types_1.isEnum)(field)) {
1710
1720
  const toJson = (0, types_1.getEnumMethod)(ctx, field.typeName, "ToJSON");
@@ -1796,9 +1806,9 @@ function generateToJson(ctx, fullName, fullProtobufTypeName, messageDesc) {
1796
1806
  const i = convertToObjectKey(ctx, messageDesc, field, "k");
1797
1807
  if ((0, types_1.shouldGenerateJSMapType)(ctx, messageDesc, field)) {
1798
1808
  chunks.push((0, ts_poet_1.code) `
1799
- if (message.${fieldName}?.size) {
1809
+ if (${messageProperty}?.size) {
1800
1810
  ${jsonProperty} = {};
1801
- message.${fieldName}.forEach((v, k) => {
1811
+ ${messageProperty}.forEach((v, k) => {
1802
1812
  ${jsonProperty}[${i}] = ${readSnippet("v")};
1803
1813
  });
1804
1814
  }
@@ -1806,8 +1816,8 @@ function generateToJson(ctx, fullName, fullProtobufTypeName, messageDesc) {
1806
1816
  }
1807
1817
  else {
1808
1818
  chunks.push((0, ts_poet_1.code) `
1809
- if (message.${fieldName}) {
1810
- const entries = Object.entries(message.${fieldName});
1819
+ if (${messageProperty}) {
1820
+ const entries = Object.entries(${messageProperty});
1811
1821
  if (entries.length > 0) {
1812
1822
  ${jsonProperty} = {};
1813
1823
  entries.forEach(([k, v]) => {
@@ -1823,28 +1833,30 @@ function generateToJson(ctx, fullName, fullProtobufTypeName, messageDesc) {
1823
1833
  const transformElement = readSnippet("e");
1824
1834
  const maybeMap = transformElement.toCodeString([]) !== "e" ? (0, ts_poet_1.code) `.map(e => ${transformElement})` : "";
1825
1835
  chunks.push((0, ts_poet_1.code) `
1826
- if (message.${fieldName}?.length) {
1827
- ${jsonProperty} = message.${fieldName}${maybeMap};
1836
+ if (${messageProperty}?.length) {
1837
+ ${jsonProperty} = ${messageProperty}${maybeMap};
1828
1838
  }
1829
1839
  `);
1830
1840
  }
1831
1841
  else if ((0, types_1.isWithinOneOfThatShouldBeUnion)(options, field)) {
1832
1842
  // oneofs in a union are only output as `oneof name = ...`
1833
- const oneofName = (0, case_1.maybeSnakeToCamel)(messageDesc.oneofDecl[field.oneofIndex].name, options);
1843
+ const oneofNameWithMessage = options.useJsonName
1844
+ ? messageProperty
1845
+ : (0, utils_1.getPropertyAccessor)("message", (0, case_1.maybeSnakeToCamel)(messageDesc.oneofDecl[field.oneofIndex].name, options));
1834
1846
  chunks.push((0, ts_poet_1.code) `
1835
- if (message.${oneofName}?.$case === '${fieldName}') {
1836
- ${jsonProperty} = ${readSnippet(`message.${oneofName}.${fieldName}`)};
1847
+ if (${oneofNameWithMessage}?.$case === '${fieldName}') {
1848
+ ${jsonProperty} = ${readSnippet(`${oneofNameWithMessage}.${fieldName}`)};
1837
1849
  }
1838
1850
  `);
1839
1851
  }
1840
1852
  else {
1841
1853
  let emitDefaultValuesForJson = ctx.options.emitDefaultValues.includes("json-methods");
1842
1854
  const check = ((0, types_1.isScalar)(field) || (0, types_1.isEnum)(field)) && !((0, types_1.isWithinOneOf)(field) || emitDefaultValuesForJson)
1843
- ? (0, types_1.notDefaultCheck)(ctx, field, messageDesc.options, `message.${fieldName}`)
1844
- : `message.${fieldName} !== undefined`;
1855
+ ? (0, types_1.notDefaultCheck)(ctx, field, messageDesc.options, `${messageProperty}`)
1856
+ : `${messageProperty} !== undefined`;
1845
1857
  chunks.push((0, ts_poet_1.code) `
1846
1858
  if (${check}) {
1847
- ${jsonProperty} = ${readSnippet(`message.${fieldName}`)};
1859
+ ${jsonProperty} = ${readSnippet(`${messageProperty}`)};
1848
1860
  }
1849
1861
  `);
1850
1862
  }
@@ -1890,7 +1902,9 @@ function generateFromPartial(ctx, fullName, messageDesc) {
1890
1902
  chunks.push((0, ts_poet_1.code) `const message = ${createBase}${maybeAsAny(options)};`);
1891
1903
  // add a check for each incoming field
1892
1904
  messageDesc.field.forEach((field) => {
1893
- const fieldName = (0, case_1.maybeSnakeToCamel)(field.name, options);
1905
+ const { propertyName } = (0, utils_1.propertyNameComposition)(field, options);
1906
+ const messageProperty = (0, utils_1.getPropertyAccessor)("message", propertyName);
1907
+ const objectProperty = (0, utils_1.getPropertyAccessor)("object", propertyName);
1894
1908
  const readSnippet = (from) => {
1895
1909
  if (((0, types_1.isLong)(field) || (0, types_1.isLongValueType)(field)) && options.forceLong === options_1.LongOption.LONG) {
1896
1910
  return (0, ts_poet_1.code) `Long.fromValue(${from})`;
@@ -1961,13 +1975,13 @@ function generateFromPartial(ctx, fullName, messageDesc) {
1961
1975
  const fieldType = (0, types_1.toTypeName)(ctx, messageDesc, field);
1962
1976
  const i = convertFromObjectKey(ctx, messageDesc, field, "key");
1963
1977
  const noValueSnippet = noDefaultValue
1964
- ? `(object.${fieldName} === undefined || object.${fieldName} === null) ? undefined : `
1978
+ ? `(${objectProperty} === undefined || ${objectProperty} === null) ? undefined : `
1965
1979
  : "";
1966
1980
  if ((0, types_1.shouldGenerateJSMapType)(ctx, messageDesc, field)) {
1967
1981
  chunks.push((0, ts_poet_1.code) `
1968
- message.${fieldName} = ${noValueSnippet} (() => {
1982
+ ${messageProperty} = ${noValueSnippet} (() => {
1969
1983
  const m = new Map();
1970
- (object.${fieldName} as ${fieldType} ?? new Map()).forEach((value, key) => {
1984
+ (${objectProperty} as ${fieldType} ?? new Map()).forEach((value, key) => {
1971
1985
  if (value !== undefined) {
1972
1986
  m.set(key, ${readSnippet("value")});
1973
1987
  }
@@ -1978,7 +1992,7 @@ function generateFromPartial(ctx, fullName, messageDesc) {
1978
1992
  }
1979
1993
  else {
1980
1994
  chunks.push((0, ts_poet_1.code) `
1981
- message.${fieldName} = ${noValueSnippet} Object.entries(object.${fieldName} ?? {}).reduce<${fieldType}>((acc, [key, value]) => {
1995
+ ${messageProperty} = ${noValueSnippet} Object.entries(${objectProperty} ?? {}).reduce<${fieldType}>((acc, [key, value]) => {
1982
1996
  if (value !== undefined) {
1983
1997
  acc[${i}] = ${readSnippet("value")};
1984
1998
  }
@@ -1990,33 +2004,37 @@ function generateFromPartial(ctx, fullName, messageDesc) {
1990
2004
  else {
1991
2005
  const fallback = noDefaultValue ? "undefined" : "[]";
1992
2006
  chunks.push((0, ts_poet_1.code) `
1993
- message.${fieldName} = object.${fieldName}?.map((e) => ${readSnippet("e")}) || ${fallback};
2007
+ ${messageProperty} = ${objectProperty}?.map((e) => ${readSnippet("e")}) || ${fallback};
1994
2008
  `);
1995
2009
  }
1996
2010
  }
1997
2011
  else if ((0, types_1.isWithinOneOfThatShouldBeUnion)(options, field)) {
1998
- let oneofName = (0, case_1.maybeSnakeToCamel)(messageDesc.oneofDecl[field.oneofIndex].name, options);
1999
- const v = readSnippet(`object.${oneofName}.${fieldName}`);
2012
+ const oneofName = options.useJsonName
2013
+ ? propertyName
2014
+ : (0, case_1.maybeSnakeToCamel)(messageDesc.oneofDecl[field.oneofIndex].name, options);
2015
+ const oneofNameWithMessage = (0, utils_1.getPropertyAccessor)("message", oneofName);
2016
+ const oneofNameWithObject = (0, utils_1.getPropertyAccessor)("object", oneofName);
2017
+ const v = readSnippet(`${oneofNameWithObject}.${propertyName}`);
2000
2018
  chunks.push((0, ts_poet_1.code) `
2001
2019
  if (
2002
- object.${oneofName}?.$case === '${fieldName}'
2003
- && object.${oneofName}?.${fieldName} !== undefined
2004
- && object.${oneofName}?.${fieldName} !== null
2020
+ ${oneofNameWithObject}?.$case === '${propertyName}'
2021
+ && ${oneofNameWithObject}?.${propertyName} !== undefined
2022
+ && ${oneofNameWithObject}?.${propertyName} !== null
2005
2023
  ) {
2006
- message.${oneofName} = { $case: '${fieldName}', ${fieldName}: ${v} };
2024
+ ${oneofNameWithMessage} = { $case: '${propertyName}', ${propertyName}: ${v} };
2007
2025
  }
2008
2026
  `);
2009
2027
  }
2010
2028
  else if (readSnippet(`x`).toCodeString([]) == "x") {
2011
2029
  // An optimized case of the else below that works when `readSnippet` returns the plain input
2012
2030
  const fallback = (0, types_1.isWithinOneOf)(field) || noDefaultValue ? "undefined" : (0, types_1.defaultValue)(ctx, field);
2013
- chunks.push((0, ts_poet_1.code) `message.${fieldName} = object.${fieldName} ?? ${fallback};`);
2031
+ chunks.push((0, ts_poet_1.code) `${messageProperty} = ${objectProperty} ?? ${fallback};`);
2014
2032
  }
2015
2033
  else {
2016
2034
  const fallback = (0, types_1.isWithinOneOf)(field) || noDefaultValue ? "undefined" : (0, types_1.defaultValue)(ctx, field);
2017
2035
  chunks.push((0, ts_poet_1.code) `
2018
- message.${fieldName} = (object.${fieldName} !== undefined && object.${fieldName} !== null)
2019
- ? ${readSnippet(`object.${fieldName}`)}
2036
+ ${messageProperty} = (${objectProperty} !== undefined && ${objectProperty} !== null)
2037
+ ? ${readSnippet(`${objectProperty}`)}
2020
2038
  : ${fallback};
2021
2039
  `);
2022
2040
  }
package/build/options.js CHANGED
@@ -77,6 +77,7 @@ function defaultOptions() {
77
77
  useAsyncIterable: false,
78
78
  unknownFields: false,
79
79
  usePrototypeForDefaults: false,
80
+ useJsonName: false,
80
81
  useJsonWireFormat: false,
81
82
  useNumericEnumForJson: false,
82
83
  initializeFieldsAsUndefined: true,
package/build/utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.impProto = exports.impFile = exports.getPropertyAccessor = exports.getFieldJsonName = exports.FormattedMethodDescriptor = exports.assertInstanceOf = exports.maybePrefixPackage = exports.prefixDisableLinter = exports.maybeAddComment = exports.upperFirst = exports.lowerFirst = exports.singular = exports.fail = exports.readToBuffer = exports.generateIndexFiles = exports.protoFilesToGenerate = void 0;
3
+ exports.impProto = exports.impFile = exports.getPropertyAccessor = exports.propertyNameComposition = exports.getFieldJsonName = exports.FormattedMethodDescriptor = exports.assertInstanceOf = exports.maybePrefixPackage = exports.prefixDisableLinter = exports.maybeAddComment = exports.upperFirst = exports.lowerFirst = exports.singular = exports.fail = exports.readToBuffer = exports.generateIndexFiles = exports.protoFilesToGenerate = void 0;
4
4
  const path = require("path");
5
5
  const ts_poet_1 = require("ts-poet");
6
6
  const options_1 = require("./options");
@@ -181,6 +181,10 @@ class FormattedMethodDescriptor {
181
181
  }
182
182
  exports.FormattedMethodDescriptor = FormattedMethodDescriptor;
183
183
  function getFieldJsonName(field, options) {
184
+ // use "json_name" defined in a proto file
185
+ if (options.useJsonName) {
186
+ return field.jsonName;
187
+ }
184
188
  // jsonName will be camelCased by the protocol compiler, plus can be overridden by the user,
185
189
  // so just use that instead of our own maybeSnakeToCamel
186
190
  if (options.snakeToCamel.includes("json")) {
@@ -195,6 +199,30 @@ function getFieldJsonName(field, options) {
195
199
  }
196
200
  }
197
201
  exports.getFieldJsonName = getFieldJsonName;
202
+ function propertyNameComposition(field, options) {
203
+ if (options.useJsonName) {
204
+ const jsonName = field.jsonName;
205
+ return {
206
+ propertyName: jsonName,
207
+ validatedPropertyName: validateObjectProperty(jsonName),
208
+ };
209
+ }
210
+ const propertyName = (0, case_1.maybeSnakeToCamel)(field.name, options);
211
+ return {
212
+ propertyName,
213
+ validatedPropertyName: propertyName,
214
+ };
215
+ }
216
+ exports.propertyNameComposition = propertyNameComposition;
217
+ /**
218
+ * https://github.com/eslint-community/eslint-plugin-security/blob/main/docs/the-dangers-of-square-bracket-notation.md
219
+ */
220
+ function isValidateObjectProperty(propertyName) {
221
+ return /^[a-zA-Z_$][\w$]*$/.test(propertyName);
222
+ }
223
+ function validateObjectProperty(propertyName) {
224
+ return isValidateObjectProperty(propertyName) ? propertyName : JSON.stringify(propertyName);
225
+ }
198
226
  /**
199
227
  * Returns a snippet for reading an object's property, such as `foo.bar`, or `foo['bar']` if the property name contains unusual characters.
200
228
  * For simplicity, we don't match the ECMA 5/6 rules for valid identifiers exactly, and return array syntax liberally.
@@ -203,10 +231,9 @@ exports.getFieldJsonName = getFieldJsonName;
203
231
  * @param optional
204
232
  */
205
233
  function getPropertyAccessor(objectName, propertyName, optional = false) {
206
- let validIdentifier = /^[a-zA-Z_$][\w$]*$/;
207
- return validIdentifier.test(propertyName)
234
+ return isValidateObjectProperty(propertyName)
208
235
  ? `${objectName}${optional ? "?" : ""}.${propertyName}`
209
- : `${objectName}${optional ? "?." : ""}[${JSON.stringify(propertyName)}]`;
236
+ : `${objectName}${optional ? "?." : ""}[${validateObjectProperty(propertyName)}]`;
210
237
  }
211
238
  exports.getPropertyAccessor = getPropertyAccessor;
212
239
  function impFile(options, spec) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-proto",
3
- "version": "1.161.0",
3
+ "version": "1.162.0",
4
4
  "description": "",
5
5
  "main": "build/plugin.js",
6
6
  "repository": "github:stephenh/ts-proto",