ts-proto 1.161.1 → 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
@@ -615,10 +615,10 @@ function generateInterfaceDeclaration(ctx, fullName, messageDesc, sourceInfo, fu
615
615
  }
616
616
  const info = sourceInfo.lookup(sourceInfo_1.Fields.message.field, index);
617
617
  (0, utils_1.maybeAddComment)(info, chunks, (_a = fieldDesc.options) === null || _a === void 0 ? void 0 : _a.deprecated);
618
- const name = (0, case_1.maybeSnakeToCamel)(fieldDesc.name, options);
618
+ const { validatedPropertyName } = (0, utils_1.propertyNameComposition)(fieldDesc, options);
619
619
  const isOptional = (0, types_1.isOptionalProperty)(fieldDesc, messageDesc.options, options);
620
620
  const type = (0, types_1.toTypeName)(ctx, messageDesc, fieldDesc, isOptional);
621
- 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}, `);
622
622
  });
623
623
  if (ctx.options.unknownFields) {
624
624
  chunks.push((0, ts_poet_1.code) `_unknownFields?: {[key: number]: Uint8Array[]} | undefined,`);
@@ -669,7 +669,9 @@ function generateBaseInstanceFactory(ctx, fullName, messageDesc, fullTypeName) {
669
669
  const { oneofIndex } = field;
670
670
  if (!processedOneofs.has(oneofIndex)) {
671
671
  processedOneofs.add(oneofIndex);
672
- 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);
673
675
  fields.push((0, ts_poet_1.code) `${name}: undefined`);
674
676
  }
675
677
  continue;
@@ -677,7 +679,7 @@ function generateBaseInstanceFactory(ctx, fullName, messageDesc, fullTypeName) {
677
679
  if (!options.initializeFieldsAsUndefined && (0, types_1.isOptionalProperty)(field, messageDesc.options, options)) {
678
680
  continue;
679
681
  }
680
- const name = (0, case_1.maybeSnakeToCamel)(field.name, ctx.options);
682
+ const { validatedPropertyName } = (0, utils_1.propertyNameComposition)(field, options);
681
683
  const val = (0, types_1.isWithinOneOf)(field)
682
684
  ? "undefined"
683
685
  : (0, types_1.isMapType)(ctx, messageDesc, field)
@@ -687,7 +689,7 @@ function generateBaseInstanceFactory(ctx, fullName, messageDesc, fullTypeName) {
687
689
  : (0, types_1.isRepeated)(field)
688
690
  ? "[]"
689
691
  : (0, types_1.defaultValue)(ctx, field);
690
- fields.push((0, ts_poet_1.code) `${name}: ${val}`);
692
+ fields.push((0, ts_poet_1.code) `${validatedPropertyName}: ${val}`);
691
693
  }
692
694
  if ((0, options_1.addTypeToMessages)(options)) {
693
695
  fields.unshift((0, ts_poet_1.code) `$type: '${fullTypeName}'`);
@@ -795,7 +797,8 @@ function generateDecode(ctx, fullName, messageDesc) {
795
797
  `);
796
798
  // add a case for each incoming field
797
799
  messageDesc.field.forEach((field) => {
798
- 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);
799
802
  chunks.push((0, ts_poet_1.code) `case ${field.number}:`);
800
803
  const tag = ((field.number << 3) | (0, types_1.basicWireType)(field.type)) >>> 0;
801
804
  const tagCheck = (0, ts_poet_1.code) `
@@ -816,15 +819,15 @@ function generateDecode(ctx, fullName, messageDesc) {
816
819
  const generateMapType = (0, types_1.shouldGenerateJSMapType)(ctx, messageDesc, field);
817
820
  let valueSetterSnippet;
818
821
  if (generateMapType) {
819
- valueSetterSnippet = `message.${fieldName}${maybeNonNullAssertion}.set(${varName}.key, ${varName}.value)`;
822
+ valueSetterSnippet = `${messageProperty}${maybeNonNullAssertion}.set(${varName}.key, ${varName}.value)`;
820
823
  }
821
824
  else {
822
- valueSetterSnippet = `message.${fieldName}${maybeNonNullAssertion}[${varName}.key] = ${varName}.value`;
825
+ valueSetterSnippet = `${messageProperty}${maybeNonNullAssertion}[${varName}.key] = ${varName}.value`;
823
826
  }
824
827
  const initializerSnippet = initializerNecessary
825
828
  ? `
826
- if (message.${fieldName} === undefined) {
827
- message.${fieldName} = ${generateMapType ? "new Map()" : "{}"};
829
+ if (${messageProperty} === undefined) {
830
+ ${messageProperty} = ${generateMapType ? "new Map()" : "{}"};
828
831
  }`
829
832
  : "";
830
833
  chunks.push((0, ts_poet_1.code) `
@@ -839,15 +842,15 @@ function generateDecode(ctx, fullName, messageDesc) {
839
842
  else {
840
843
  const initializerSnippet = initializerNecessary
841
844
  ? `
842
- if (message.${fieldName} === undefined) {
843
- message.${fieldName} = [];
845
+ if (${messageProperty} === undefined) {
846
+ ${messageProperty} = [];
844
847
  }`
845
848
  : "";
846
849
  if ((0, types_1.packedType)(field.type) === undefined) {
847
850
  chunks.push((0, ts_poet_1.code) `
848
851
  ${tagCheck}
849
852
  ${initializerSnippet}
850
- message.${fieldName}${maybeNonNullAssertion}.push(${readSnippet});
853
+ ${messageProperty}${maybeNonNullAssertion}.push(${readSnippet});
851
854
  `);
852
855
  }
853
856
  else {
@@ -855,7 +858,7 @@ function generateDecode(ctx, fullName, messageDesc) {
855
858
  chunks.push((0, ts_poet_1.code) `
856
859
  if (tag === ${tag}) {
857
860
  ${initializerSnippet}
858
- message.${fieldName}${maybeNonNullAssertion}.push(${readSnippet});
861
+ ${messageProperty}${maybeNonNullAssertion}.push(${readSnippet});
859
862
 
860
863
  continue;
861
864
  }
@@ -864,7 +867,7 @@ function generateDecode(ctx, fullName, messageDesc) {
864
867
  ${initializerSnippet}
865
868
  const end2 = reader.uint32() + reader.pos;
866
869
  while (reader.pos < end2) {
867
- message.${fieldName}${maybeNonNullAssertion}.push(${readSnippet});
870
+ ${messageProperty}${maybeNonNullAssertion}.push(${readSnippet});
868
871
  }
869
872
 
870
873
  continue;
@@ -876,16 +879,18 @@ function generateDecode(ctx, fullName, messageDesc) {
876
879
  }
877
880
  }
878
881
  else if ((0, types_1.isWithinOneOfThatShouldBeUnion)(options, field)) {
879
- 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));
880
885
  chunks.push((0, ts_poet_1.code) `
881
886
  ${tagCheck}
882
- message.${oneofName} = { $case: '${fieldName}', ${fieldName}: ${readSnippet} };
887
+ ${oneofNameWithMessage} = { $case: '${propertyName}', ${propertyName}: ${readSnippet} };
883
888
  `);
884
889
  }
885
890
  else {
886
891
  chunks.push((0, ts_poet_1.code) `
887
892
  ${tagCheck}
888
- message.${fieldName} = ${readSnippet};
893
+ ${messageProperty} = ${readSnippet};
889
894
  `);
890
895
  }
891
896
  if (!(0, types_1.isRepeated)(field) || (0, types_1.packedType)(field.type) === undefined) {
@@ -1020,7 +1025,8 @@ function generateEncode(ctx, fullName, messageDesc) {
1020
1025
  .reduce((result, field) => ((result[field.oneofIndex] || (result[field.oneofIndex] = [])).push(field), result), {});
1021
1026
  // then add a case for each field
1022
1027
  messageDesc.field.forEach((field) => {
1023
- 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);
1024
1030
  // get a generic writer.doSomething based on the basic type
1025
1031
  const writeSnippet = getEncodeWriteSnippet(ctx, field);
1026
1032
  const isOptional = (0, types_1.isOptionalProperty)(field, messageDesc.options, options);
@@ -1039,14 +1045,14 @@ function generateEncode(ctx, fullName, messageDesc) {
1039
1045
  const optionalAlternative = isOptional ? (useMapType ? " || new Map()" : " || {}") : "";
1040
1046
  if (useMapType) {
1041
1047
  chunks.push((0, ts_poet_1.code) `
1042
- (message.${fieldName}${optionalAlternative}).forEach((value, key) => {
1048
+ (${messageProperty}${optionalAlternative}).forEach((value, key) => {
1043
1049
  ${entryWriteSnippet}
1044
1050
  });
1045
1051
  `);
1046
1052
  }
1047
1053
  else {
1048
1054
  chunks.push((0, ts_poet_1.code) `
1049
- Object.entries(message.${fieldName}${optionalAlternative}).forEach(([key, value]) => {
1055
+ Object.entries(${messageProperty}${optionalAlternative}).forEach(([key, value]) => {
1050
1056
  ${entryWriteSnippet}
1051
1057
  });
1052
1058
  `);
@@ -1054,13 +1060,13 @@ function generateEncode(ctx, fullName, messageDesc) {
1054
1060
  }
1055
1061
  else if ((0, types_1.packedType)(field.type) === undefined) {
1056
1062
  const listWriteSnippet = (0, ts_poet_1.code) `
1057
- for (const v of message.${fieldName}) {
1063
+ for (const v of ${messageProperty}) {
1058
1064
  ${writeSnippet("v!")};
1059
1065
  }
1060
1066
  `;
1061
1067
  if (isOptional) {
1062
1068
  chunks.push((0, ts_poet_1.code) `
1063
- if (message.${fieldName} !== undefined && message.${fieldName}.length !== 0) {
1069
+ if (${messageProperty} !== undefined && ${messageProperty}.length !== 0) {
1064
1070
  ${listWriteSnippet}
1065
1071
  }
1066
1072
  `);
@@ -1078,14 +1084,14 @@ function generateEncode(ctx, fullName, messageDesc) {
1078
1084
  const toNumber = (0, types_1.getEnumMethod)(ctx, field.typeName, "ToNumber");
1079
1085
  const listWriteSnippet = (0, ts_poet_1.code) `
1080
1086
  writer.uint32(${tag}).fork();
1081
- for (const v of message.${fieldName}) {
1087
+ for (const v of ${messageProperty}) {
1082
1088
  writer.${(0, types_1.toReaderCall)(field)}(${toNumber}(v));
1083
1089
  }
1084
1090
  writer.ldelim();
1085
1091
  `;
1086
1092
  if (isOptional) {
1087
1093
  chunks.push((0, ts_poet_1.code) `
1088
- if (message.${fieldName} !== undefined && message.${fieldName}.length !== 0) {
1094
+ if (${messageProperty} !== undefined && ${messageProperty}.length !== 0) {
1089
1095
  ${listWriteSnippet}
1090
1096
  }
1091
1097
  `);
@@ -1100,7 +1106,7 @@ function generateEncode(ctx, fullName, messageDesc) {
1100
1106
  const rhs = (x) => ((0, types_1.isLong)(field) && options.forceLong === options_1.LongOption.BIGINT ? `${x}.toString()` : x);
1101
1107
  let listWriteSnippet = (0, ts_poet_1.code) `
1102
1108
  writer.uint32(${tag}).fork();
1103
- for (const v of message.${fieldName}) {
1109
+ for (const v of ${messageProperty}) {
1104
1110
  writer.${(0, types_1.toReaderCall)(field)}(${rhs("v")});
1105
1111
  }
1106
1112
  writer.ldelim();
@@ -1113,9 +1119,9 @@ function generateEncode(ctx, fullName, messageDesc) {
1113
1119
  case "sfixed64":
1114
1120
  listWriteSnippet = (0, ts_poet_1.code) `
1115
1121
  writer.uint32(${tag}).fork();
1116
- for (const v of message.${fieldName}) {
1122
+ for (const v of ${messageProperty}) {
1117
1123
  if (BigInt.asIntN(64, v) !== v) {
1118
- 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');
1119
1125
  }
1120
1126
  writer.${(0, types_1.toReaderCall)(field)}(${rhs("v")});
1121
1127
  }
@@ -1126,9 +1132,9 @@ function generateEncode(ctx, fullName, messageDesc) {
1126
1132
  case "fixed64":
1127
1133
  listWriteSnippet = (0, ts_poet_1.code) `
1128
1134
  writer.uint32(${tag}).fork();
1129
- for (const v of message.${fieldName}) {
1135
+ for (const v of ${messageProperty}) {
1130
1136
  if (BigInt.asUintN(64, v) !== v) {
1131
- 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');
1132
1138
  }
1133
1139
  writer.${(0, types_1.toReaderCall)(field)}(${rhs("v")});
1134
1140
  }
@@ -1141,7 +1147,7 @@ function generateEncode(ctx, fullName, messageDesc) {
1141
1147
  }
1142
1148
  if (isOptional) {
1143
1149
  chunks.push((0, ts_poet_1.code) `
1144
- if (message.${fieldName} !== undefined && message.${fieldName}.length !== 0) {
1150
+ if (${messageProperty} !== undefined && ${messageProperty}.length !== 0) {
1145
1151
  ${listWriteSnippet}
1146
1152
  }
1147
1153
  `);
@@ -1154,13 +1160,15 @@ function generateEncode(ctx, fullName, messageDesc) {
1154
1160
  else if ((0, types_1.isWithinOneOfThatShouldBeUnion)(options, field)) {
1155
1161
  if (!processedOneofs.has(field.oneofIndex)) {
1156
1162
  processedOneofs.add(field.oneofIndex);
1157
- const oneofName = (0, case_1.maybeSnakeToCamel)(messageDesc.oneofDecl[field.oneofIndex].name, options);
1158
- 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) {`);
1159
1167
  for (const oneOfField of oneOfFieldsDict[field.oneofIndex]) {
1160
1168
  const writeSnippet = getEncodeWriteSnippet(ctx, oneOfField);
1161
1169
  const oneOfFieldName = (0, case_1.maybeSnakeToCamel)(oneOfField.name, ctx.options);
1162
1170
  chunks.push((0, ts_poet_1.code) `case "${oneOfFieldName}":
1163
- ${writeSnippet(`message.${oneofName}.${oneOfFieldName}`)};
1171
+ ${writeSnippet(`${oneofNameWithMessage}.${oneOfFieldName}`)};
1164
1172
  break;`);
1165
1173
  }
1166
1174
  chunks.push((0, ts_poet_1.code) `}`);
@@ -1169,27 +1177,27 @@ function generateEncode(ctx, fullName, messageDesc) {
1169
1177
  else if ((0, types_1.isWithinOneOf)(field)) {
1170
1178
  // Oneofs don't have a default value check b/c they need to denote which-oneof presence
1171
1179
  chunks.push((0, ts_poet_1.code) `
1172
- if (message.${fieldName} !== undefined) {
1173
- ${writeSnippet(`message.${fieldName}`)};
1180
+ if (${messageProperty} !== undefined) {
1181
+ ${writeSnippet(`${messageProperty}`)};
1174
1182
  }
1175
1183
  `);
1176
1184
  }
1177
1185
  else if ((0, types_1.isMessage)(field)) {
1178
1186
  chunks.push((0, ts_poet_1.code) `
1179
- if (message.${fieldName} !== undefined) {
1180
- ${writeSnippet(`message.${fieldName}`)};
1187
+ if (${messageProperty} !== undefined) {
1188
+ ${writeSnippet(`${messageProperty}`)};
1181
1189
  }
1182
1190
  `);
1183
1191
  }
1184
1192
  else if ((0, types_1.isScalar)(field) || (0, types_1.isEnum)(field)) {
1185
1193
  chunks.push((0, ts_poet_1.code) `
1186
- if (${(0, types_1.notDefaultCheck)(ctx, field, messageDesc.options, `message.${fieldName}`)}) {
1187
- ${writeSnippet(`message.${fieldName}`)};
1194
+ if (${(0, types_1.notDefaultCheck)(ctx, field, messageDesc.options, `${messageProperty}`)}) {
1195
+ ${writeSnippet(`${messageProperty}`)};
1188
1196
  }
1189
1197
  `);
1190
1198
  }
1191
1199
  else {
1192
- chunks.push((0, ts_poet_1.code) `${writeSnippet(`message.${fieldName}`)};`);
1200
+ chunks.push((0, ts_poet_1.code) `${writeSnippet(`${messageProperty}`)};`);
1193
1201
  }
1194
1202
  });
1195
1203
  if (options.unknownFields) {
@@ -1461,7 +1469,7 @@ function generateFromJson(ctx, fullName, fullTypeName, messageDesc) {
1461
1469
  // add a check for each incoming field
1462
1470
  messageDesc.field.forEach((field) => {
1463
1471
  var _a;
1464
- const fieldName = (0, case_1.maybeSnakeToCamel)(field.name, options);
1472
+ const { validatedPropertyName: fieldName } = (0, utils_1.propertyNameComposition)(field, options);
1465
1473
  const jsonName = (0, utils_1.getFieldJsonName)(field, options);
1466
1474
  const jsonProperty = (0, utils_1.getPropertyAccessor)("object", jsonName);
1467
1475
  const jsonPropertyOptional = (0, utils_1.getPropertyAccessor)("object", jsonName, true);
@@ -1703,9 +1711,10 @@ function generateToJson(ctx, fullName, fullProtobufTypeName, messageDesc) {
1703
1711
  `);
1704
1712
  // then add a case for each field
1705
1713
  messageDesc.field.forEach((field) => {
1706
- const fieldName = (0, case_1.maybeSnakeToCamel)(field.name, options);
1714
+ const { validatedPropertyName: fieldName, propertyName } = (0, utils_1.propertyNameComposition)(field, options);
1707
1715
  const jsonName = (0, utils_1.getFieldJsonName)(field, options);
1708
1716
  const jsonProperty = (0, utils_1.getPropertyAccessor)("obj", jsonName);
1717
+ const messageProperty = (0, utils_1.getPropertyAccessor)("message", propertyName);
1709
1718
  const readSnippet = (from) => {
1710
1719
  if ((0, types_1.isEnum)(field)) {
1711
1720
  const toJson = (0, types_1.getEnumMethod)(ctx, field.typeName, "ToJSON");
@@ -1797,9 +1806,9 @@ function generateToJson(ctx, fullName, fullProtobufTypeName, messageDesc) {
1797
1806
  const i = convertToObjectKey(ctx, messageDesc, field, "k");
1798
1807
  if ((0, types_1.shouldGenerateJSMapType)(ctx, messageDesc, field)) {
1799
1808
  chunks.push((0, ts_poet_1.code) `
1800
- if (message.${fieldName}?.size) {
1809
+ if (${messageProperty}?.size) {
1801
1810
  ${jsonProperty} = {};
1802
- message.${fieldName}.forEach((v, k) => {
1811
+ ${messageProperty}.forEach((v, k) => {
1803
1812
  ${jsonProperty}[${i}] = ${readSnippet("v")};
1804
1813
  });
1805
1814
  }
@@ -1807,8 +1816,8 @@ function generateToJson(ctx, fullName, fullProtobufTypeName, messageDesc) {
1807
1816
  }
1808
1817
  else {
1809
1818
  chunks.push((0, ts_poet_1.code) `
1810
- if (message.${fieldName}) {
1811
- const entries = Object.entries(message.${fieldName});
1819
+ if (${messageProperty}) {
1820
+ const entries = Object.entries(${messageProperty});
1812
1821
  if (entries.length > 0) {
1813
1822
  ${jsonProperty} = {};
1814
1823
  entries.forEach(([k, v]) => {
@@ -1824,28 +1833,30 @@ function generateToJson(ctx, fullName, fullProtobufTypeName, messageDesc) {
1824
1833
  const transformElement = readSnippet("e");
1825
1834
  const maybeMap = transformElement.toCodeString([]) !== "e" ? (0, ts_poet_1.code) `.map(e => ${transformElement})` : "";
1826
1835
  chunks.push((0, ts_poet_1.code) `
1827
- if (message.${fieldName}?.length) {
1828
- ${jsonProperty} = message.${fieldName}${maybeMap};
1836
+ if (${messageProperty}?.length) {
1837
+ ${jsonProperty} = ${messageProperty}${maybeMap};
1829
1838
  }
1830
1839
  `);
1831
1840
  }
1832
1841
  else if ((0, types_1.isWithinOneOfThatShouldBeUnion)(options, field)) {
1833
1842
  // oneofs in a union are only output as `oneof name = ...`
1834
- 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));
1835
1846
  chunks.push((0, ts_poet_1.code) `
1836
- if (message.${oneofName}?.$case === '${fieldName}') {
1837
- ${jsonProperty} = ${readSnippet(`message.${oneofName}.${fieldName}`)};
1847
+ if (${oneofNameWithMessage}?.$case === '${fieldName}') {
1848
+ ${jsonProperty} = ${readSnippet(`${oneofNameWithMessage}.${fieldName}`)};
1838
1849
  }
1839
1850
  `);
1840
1851
  }
1841
1852
  else {
1842
1853
  let emitDefaultValuesForJson = ctx.options.emitDefaultValues.includes("json-methods");
1843
1854
  const check = ((0, types_1.isScalar)(field) || (0, types_1.isEnum)(field)) && !((0, types_1.isWithinOneOf)(field) || emitDefaultValuesForJson)
1844
- ? (0, types_1.notDefaultCheck)(ctx, field, messageDesc.options, `message.${fieldName}`)
1845
- : `message.${fieldName} !== undefined`;
1855
+ ? (0, types_1.notDefaultCheck)(ctx, field, messageDesc.options, `${messageProperty}`)
1856
+ : `${messageProperty} !== undefined`;
1846
1857
  chunks.push((0, ts_poet_1.code) `
1847
1858
  if (${check}) {
1848
- ${jsonProperty} = ${readSnippet(`message.${fieldName}`)};
1859
+ ${jsonProperty} = ${readSnippet(`${messageProperty}`)};
1849
1860
  }
1850
1861
  `);
1851
1862
  }
@@ -1891,7 +1902,9 @@ function generateFromPartial(ctx, fullName, messageDesc) {
1891
1902
  chunks.push((0, ts_poet_1.code) `const message = ${createBase}${maybeAsAny(options)};`);
1892
1903
  // add a check for each incoming field
1893
1904
  messageDesc.field.forEach((field) => {
1894
- 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);
1895
1908
  const readSnippet = (from) => {
1896
1909
  if (((0, types_1.isLong)(field) || (0, types_1.isLongValueType)(field)) && options.forceLong === options_1.LongOption.LONG) {
1897
1910
  return (0, ts_poet_1.code) `Long.fromValue(${from})`;
@@ -1962,13 +1975,13 @@ function generateFromPartial(ctx, fullName, messageDesc) {
1962
1975
  const fieldType = (0, types_1.toTypeName)(ctx, messageDesc, field);
1963
1976
  const i = convertFromObjectKey(ctx, messageDesc, field, "key");
1964
1977
  const noValueSnippet = noDefaultValue
1965
- ? `(object.${fieldName} === undefined || object.${fieldName} === null) ? undefined : `
1978
+ ? `(${objectProperty} === undefined || ${objectProperty} === null) ? undefined : `
1966
1979
  : "";
1967
1980
  if ((0, types_1.shouldGenerateJSMapType)(ctx, messageDesc, field)) {
1968
1981
  chunks.push((0, ts_poet_1.code) `
1969
- message.${fieldName} = ${noValueSnippet} (() => {
1982
+ ${messageProperty} = ${noValueSnippet} (() => {
1970
1983
  const m = new Map();
1971
- (object.${fieldName} as ${fieldType} ?? new Map()).forEach((value, key) => {
1984
+ (${objectProperty} as ${fieldType} ?? new Map()).forEach((value, key) => {
1972
1985
  if (value !== undefined) {
1973
1986
  m.set(key, ${readSnippet("value")});
1974
1987
  }
@@ -1979,7 +1992,7 @@ function generateFromPartial(ctx, fullName, messageDesc) {
1979
1992
  }
1980
1993
  else {
1981
1994
  chunks.push((0, ts_poet_1.code) `
1982
- message.${fieldName} = ${noValueSnippet} Object.entries(object.${fieldName} ?? {}).reduce<${fieldType}>((acc, [key, value]) => {
1995
+ ${messageProperty} = ${noValueSnippet} Object.entries(${objectProperty} ?? {}).reduce<${fieldType}>((acc, [key, value]) => {
1983
1996
  if (value !== undefined) {
1984
1997
  acc[${i}] = ${readSnippet("value")};
1985
1998
  }
@@ -1991,33 +2004,37 @@ function generateFromPartial(ctx, fullName, messageDesc) {
1991
2004
  else {
1992
2005
  const fallback = noDefaultValue ? "undefined" : "[]";
1993
2006
  chunks.push((0, ts_poet_1.code) `
1994
- message.${fieldName} = object.${fieldName}?.map((e) => ${readSnippet("e")}) || ${fallback};
2007
+ ${messageProperty} = ${objectProperty}?.map((e) => ${readSnippet("e")}) || ${fallback};
1995
2008
  `);
1996
2009
  }
1997
2010
  }
1998
2011
  else if ((0, types_1.isWithinOneOfThatShouldBeUnion)(options, field)) {
1999
- let oneofName = (0, case_1.maybeSnakeToCamel)(messageDesc.oneofDecl[field.oneofIndex].name, options);
2000
- 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}`);
2001
2018
  chunks.push((0, ts_poet_1.code) `
2002
2019
  if (
2003
- object.${oneofName}?.$case === '${fieldName}'
2004
- && object.${oneofName}?.${fieldName} !== undefined
2005
- && object.${oneofName}?.${fieldName} !== null
2020
+ ${oneofNameWithObject}?.$case === '${propertyName}'
2021
+ && ${oneofNameWithObject}?.${propertyName} !== undefined
2022
+ && ${oneofNameWithObject}?.${propertyName} !== null
2006
2023
  ) {
2007
- message.${oneofName} = { $case: '${fieldName}', ${fieldName}: ${v} };
2024
+ ${oneofNameWithMessage} = { $case: '${propertyName}', ${propertyName}: ${v} };
2008
2025
  }
2009
2026
  `);
2010
2027
  }
2011
2028
  else if (readSnippet(`x`).toCodeString([]) == "x") {
2012
2029
  // An optimized case of the else below that works when `readSnippet` returns the plain input
2013
2030
  const fallback = (0, types_1.isWithinOneOf)(field) || noDefaultValue ? "undefined" : (0, types_1.defaultValue)(ctx, field);
2014
- chunks.push((0, ts_poet_1.code) `message.${fieldName} = object.${fieldName} ?? ${fallback};`);
2031
+ chunks.push((0, ts_poet_1.code) `${messageProperty} = ${objectProperty} ?? ${fallback};`);
2015
2032
  }
2016
2033
  else {
2017
2034
  const fallback = (0, types_1.isWithinOneOf)(field) || noDefaultValue ? "undefined" : (0, types_1.defaultValue)(ctx, field);
2018
2035
  chunks.push((0, ts_poet_1.code) `
2019
- message.${fieldName} = (object.${fieldName} !== undefined && object.${fieldName} !== null)
2020
- ? ${readSnippet(`object.${fieldName}`)}
2036
+ ${messageProperty} = (${objectProperty} !== undefined && ${objectProperty} !== null)
2037
+ ? ${readSnippet(`${objectProperty}`)}
2021
2038
  : ${fallback};
2022
2039
  `);
2023
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.1",
3
+ "version": "1.162.0",
4
4
  "description": "",
5
5
  "main": "build/plugin.js",
6
6
  "repository": "github:stephenh/ts-proto",