ts-proto 1.93.2 → 1.95.1

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/CHANGELOG.md CHANGED
@@ -1,3 +1,31 @@
1
+ ## [1.95.1](https://github.com/stephenh/ts-proto/compare/v1.95.0...v1.95.1) (2021-12-23)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * Add service to the client constructor. ([#455](https://github.com/stephenh/ts-proto/issues/455)) ([8c32104](https://github.com/stephenh/ts-proto/commit/8c32104a8522cfe2febcf2338d51710021d837ff))
7
+
8
+ # [1.95.0](https://github.com/stephenh/ts-proto/compare/v1.94.0...v1.95.0) (2021-12-14)
9
+
10
+
11
+ ### Features
12
+
13
+ * Add useOptionals=all to enable non-field members to be optional. ([#402](https://github.com/stephenh/ts-proto/issues/402)) ([e7b70cb](https://github.com/stephenh/ts-proto/commit/e7b70cbd7b9bd43bf9e6e54e25bc48c527718317))
14
+
15
+ # [1.94.0](https://github.com/stephenh/ts-proto/compare/v1.93.3...v1.94.0) (2021-12-14)
16
+
17
+
18
+ ### Features
19
+
20
+ * Round numbers in toJSON. ([#444](https://github.com/stephenh/ts-proto/issues/444)) ([bd2df7b](https://github.com/stephenh/ts-proto/commit/bd2df7b7176e961955ed1dcacb3602384e13ee45))
21
+
22
+ ## [1.93.3](https://github.com/stephenh/ts-proto/compare/v1.93.2...v1.93.3) (2021-12-13)
23
+
24
+
25
+ ### Bug Fixes
26
+
27
+ * support mutliple options in snakeToCamel flag ([#429](https://github.com/stephenh/ts-proto/issues/429)) ([cff6674](https://github.com/stephenh/ts-proto/commit/cff667406cba21676546fd91b04cf2cbc571ed7d)), closes [#423](https://github.com/stephenh/ts-proto/issues/423)
28
+
1
29
  ## [1.93.2](https://github.com/stephenh/ts-proto/compare/v1.93.1...v1.93.2) (2021-12-09)
2
30
 
3
31
 
package/README.markdown CHANGED
@@ -159,7 +159,7 @@ creating a class and calling the right getters/setters.
159
159
 
160
160
  (Configurable with the `useDate` parameter.)
161
161
 
162
- - `fromJSON`/`toJSON` support the [canonical Protobuf JS](https://developers.google.com/protocol-buffers/docs/proto3#json) format (i.e. timestamps are ISO strings)
162
+ - `fromJSON`/`toJSON` use the [proto3 canonical JSON encoding format](https://developers.google.com/protocol-buffers/docs/proto3#json) (e.g. timestamps are ISO strings), unlike [`protobufjs`](https://github.com/protobufjs/protobuf.js/issues/1304).
163
163
 
164
164
  # Auto-Batching / N+1 Prevention
165
165
 
@@ -289,9 +289,7 @@ Generated code will be placed in the Gradle build directory.
289
289
 
290
290
  - With `--ts_proto_opt=lowerCaseServiceMethods=true`, the method names of service methods will be lowered/camel-case, i.e. `service.findFoo` instead of `service.FindFoo`.
291
291
 
292
- - With `--ts_proto_opt=snakeToCamel=false`, fields will be kept snake case.
293
-
294
- Defaults to `true`.
292
+ - With `--ts_proto_opt=snakeToCamel=false`, fields will be kept snake case. `snakeToCamel` can also be set as string with `--ts_proto_opt=snakeToCamel=keys,json`. `keys` will keep field names as camelCase and `json` will keep json field names as camelCase. Empty string will keep field names as snake_case.
295
293
 
296
294
  - With `--ts_proto_opt=outputEncodeMethods=false`, the `Message.encode` and `Message.decode` methods for working with protobuf-encoded/binary data will not be output.
297
295
 
package/build/case.js CHANGED
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.camelCase = exports.capitalize = exports.camelToSnake = exports.maybeSnakeToCamel = void 0;
4
4
  function maybeSnakeToCamel(s, options) {
5
- if (options.snakeToCamel && s.includes('_')) {
5
+ if (options.snakeToCamel.includes('keys') && s.includes('_')) {
6
6
  return s
7
7
  .split('_')
8
8
  .map((word, i) => {
@@ -192,6 +192,7 @@ function generateClientConstructor(fileDesc, serviceDesc) {
192
192
  credentials: ${ChannelCredentials},
193
193
  options?: Partial<${ChannelOptions}>,
194
194
  ): ${serviceDesc.name}Client;
195
+ service: typeof ${serviceDesc.name}Service;
195
196
  }
196
197
  `;
197
198
  }
package/build/main.js CHANGED
@@ -19,6 +19,14 @@ const generate_generic_service_definition_1 = require("./generate-generic-servic
19
19
  function generateFile(ctx, fileDesc) {
20
20
  var _a;
21
21
  const { options, utils } = ctx;
22
+ if (options.useOptionals === false) {
23
+ console.warn("ts-proto: Passing useOptionals as a boolean option is deprecated and will be removed in a future version. Please pass the string 'none' instead of false.");
24
+ options.useOptionals = 'none';
25
+ }
26
+ else if (options.useOptionals === true) {
27
+ console.warn("ts-proto: Passing useOptionals as a boolean option is deprecated and will be removed in a future version. Please pass the string 'messages' instead of true.");
28
+ options.useOptionals = 'messages';
29
+ }
22
30
  // Google's protofiles are organized like Java, where package == the folder the file
23
31
  // is in, and file == a specific service within the package. I.e. you can have multiple
24
32
  // company/foo.proto and company/bar.proto files, where package would be 'company'.
@@ -388,10 +396,6 @@ function makeTimestampMethods(options, longs) {
388
396
  `);
389
397
  return { toTimestamp, fromTimestamp, fromJsonTimestamp };
390
398
  }
391
- // When useOptionals=true, non-scalar fields are translated into optional properties.
392
- function isOptionalProperty(field, options) {
393
- return (options.useOptionals && types_1.isMessage(field) && !types_1.isRepeated(field)) || field.proto3Optional;
394
- }
395
399
  // Create the interface with properties
396
400
  function generateInterfaceDeclaration(ctx, fullName, messageDesc, sourceInfo, fullTypeName) {
397
401
  var _a;
@@ -419,7 +423,7 @@ function generateInterfaceDeclaration(ctx, fullName, messageDesc, sourceInfo, fu
419
423
  utils_1.maybeAddComment(info, chunks, (_a = fieldDesc.options) === null || _a === void 0 ? void 0 : _a.deprecated);
420
424
  const name = case_1.maybeSnakeToCamel(fieldDesc.name, options);
421
425
  const type = types_1.toTypeName(ctx, messageDesc, fieldDesc);
422
- const q = isOptionalProperty(fieldDesc, options) ? '?' : '';
426
+ const q = types_1.isOptionalProperty(fieldDesc, messageDesc.options, options) ? '?' : '';
423
427
  chunks.push(ts_poet_1.code `${name}${q}: ${type}, `);
424
428
  });
425
429
  chunks.push(ts_poet_1.code `}`);
@@ -662,6 +666,7 @@ function generateEncode(ctx, fullName, messageDesc) {
662
666
  else {
663
667
  throw new Error(`Unhandled field ${field}`);
664
668
  }
669
+ const isOptional = types_1.isOptionalProperty(field, messageDesc.options, options);
665
670
  if (types_1.isRepeated(field)) {
666
671
  if (types_1.isMapType(ctx, messageDesc, field)) {
667
672
  const valueType = typeMap.get(field.typeName)[2].field[1];
@@ -673,18 +678,29 @@ function generateEncode(ctx, fullName, messageDesc) {
673
678
  }
674
679
  `
675
680
  : writeSnippet(`{ ${maybeTypeField} key: key as any, value }`);
681
+ const optionalAlternative = isOptional ? ' || {}' : '';
676
682
  chunks.push(ts_poet_1.code `
677
- Object.entries(message.${fieldName}).forEach(([key, value]) => {
683
+ Object.entries(message.${fieldName}${optionalAlternative}).forEach(([key, value]) => {
678
684
  ${entryWriteSnippet}
679
685
  });
680
686
  `);
681
687
  }
682
688
  else if (types_1.packedType(field.type) === undefined) {
683
- chunks.push(ts_poet_1.code `
689
+ const listWriteSnippet = ts_poet_1.code `
684
690
  for (const v of message.${fieldName}) {
685
691
  ${writeSnippet('v!')};
686
692
  }
687
- `);
693
+ `;
694
+ if (isOptional) {
695
+ chunks.push(ts_poet_1.code `
696
+ if (message.${fieldName} !== undefined && message.${fieldName}.length !== 0) {
697
+ ${listWriteSnippet}
698
+ }
699
+ `);
700
+ }
701
+ else {
702
+ chunks.push(listWriteSnippet);
703
+ }
688
704
  }
689
705
  else if (types_1.isEnum(field) && options.stringEnums) {
690
706
  // This is a lot like the `else` clause, but we wrap `fooToNumber` around it.
@@ -693,24 +709,44 @@ function generateEncode(ctx, fullName, messageDesc) {
693
709
  // (i.e. just one tag and multiple values).
694
710
  const tag = ((field.number << 3) | 2) >>> 0;
695
711
  const toNumber = types_1.getEnumMethod(ctx, field.typeName, 'ToNumber');
696
- chunks.push(ts_poet_1.code `
712
+ const listWriteSnippet = ts_poet_1.code `
697
713
  writer.uint32(${tag}).fork();
698
714
  for (const v of message.${fieldName}) {
699
715
  writer.${types_1.toReaderCall(field)}(${toNumber}(v));
700
716
  }
701
717
  writer.ldelim();
702
- `);
718
+ `;
719
+ if (isOptional) {
720
+ chunks.push(ts_poet_1.code `
721
+ if (message.${fieldName} !== undefined && message.${fieldName}.length !== 0) {
722
+ ${listWriteSnippet}
723
+ }
724
+ `);
725
+ }
726
+ else {
727
+ chunks.push(listWriteSnippet);
728
+ }
703
729
  }
704
730
  else {
705
731
  // Ideally we'd reuse `writeSnippet` but it has tagging embedded inside of it.
706
732
  const tag = ((field.number << 3) | 2) >>> 0;
707
- chunks.push(ts_poet_1.code `
733
+ const listWriteSnippet = ts_poet_1.code `
708
734
  writer.uint32(${tag}).fork();
709
735
  for (const v of message.${fieldName}) {
710
736
  writer.${types_1.toReaderCall(field)}(v);
711
737
  }
712
738
  writer.ldelim();
713
- `);
739
+ `;
740
+ if (isOptional) {
741
+ chunks.push(ts_poet_1.code `
742
+ if (message.${fieldName} !== undefined && message.${fieldName}.length !== 0) {
743
+ ${listWriteSnippet}
744
+ }
745
+ `);
746
+ }
747
+ else {
748
+ chunks.push(listWriteSnippet);
749
+ }
714
750
  }
715
751
  }
716
752
  else if (types_1.isWithinOneOfThatShouldBeUnion(options, field)) {
@@ -738,7 +774,7 @@ function generateEncode(ctx, fullName, messageDesc) {
738
774
  }
739
775
  else if (types_1.isScalar(field) || types_1.isEnum(field)) {
740
776
  chunks.push(ts_poet_1.code `
741
- if (${types_1.notDefaultCheck(ctx, field, `message.${fieldName}`)}) {
777
+ if (${types_1.notDefaultCheck(ctx, field, messageDesc.options, `message.${fieldName}`)}) {
742
778
  ${writeSnippet(`message.${fieldName}`)};
743
779
  }
744
780
  `);
@@ -980,6 +1016,9 @@ function generateToJson(ctx, fullName, messageDesc) {
980
1016
  else if (types_1.isLong(valueType) && options.forceLong === options_1.LongOption.LONG) {
981
1017
  return ts_poet_1.code `${from}.toString()`;
982
1018
  }
1019
+ else if (types_1.isWholeNumber(valueType) && !(types_1.isLong(valueType) && options.forceLong === options_1.LongOption.STRING)) {
1020
+ return ts_poet_1.code `Math.round(${from})`;
1021
+ }
983
1022
  else if (types_1.isScalar(valueType) || types_1.isValueType(ctx, valueType)) {
984
1023
  return ts_poet_1.code `${from}`;
985
1024
  }
@@ -1010,6 +1049,9 @@ function generateToJson(ctx, fullName, messageDesc) {
1010
1049
  const v = types_1.isWithinOneOf(field) ? 'undefined' : types_1.defaultValue(ctx, field);
1011
1050
  return ts_poet_1.code `(${from} || ${v}).toString()`;
1012
1051
  }
1052
+ else if (types_1.isWholeNumber(field) && !(types_1.isLong(field) && options.forceLong === options_1.LongOption.STRING)) {
1053
+ return ts_poet_1.code `Math.round(${from})`;
1054
+ }
1013
1055
  else {
1014
1056
  return ts_poet_1.code `${from}`;
1015
1057
  }
package/build/options.js CHANGED
@@ -34,9 +34,9 @@ var ServiceOption;
34
34
  function defaultOptions() {
35
35
  return {
36
36
  context: false,
37
- snakeToCamel: true,
37
+ snakeToCamel: ['json', 'keys'],
38
38
  forceLong: LongOption.NUMBER,
39
- useOptionals: false,
39
+ useOptionals: 'none',
40
40
  useDate: DateOption.DATE,
41
41
  oneof: OneofOption.PROPERTIES,
42
42
  esModuleInterop: false,
@@ -100,6 +100,12 @@ function optionsFromParameter(parameter) {
100
100
  // Treat useDate=false as TIMESTAMP
101
101
  options.useDate = DateOption.TIMESTAMP;
102
102
  }
103
+ if (options.snakeToCamel === false) {
104
+ options.snakeToCamel = [];
105
+ }
106
+ else if (options.snakeToCamel === true) {
107
+ options.snakeToCamel = ['keys', 'json'];
108
+ }
103
109
  return options;
104
110
  }
105
111
  exports.optionsFromParameter = optionsFromParameter;
package/build/types.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.detectBatchMethod = exports.responsePromiseOrObservable = exports.responseObservable = exports.responsePromise = exports.responseType = exports.requestType = exports.rawRequestType = exports.detectMapType = exports.toTypeName = exports.getEnumMethod = exports.messageToTypeName = exports.wrapperTypeName = exports.valueTypeName = exports.isEmptyType = exports.isLongValueType = exports.isStructTypeName = exports.isStructType = exports.isListValueTypeName = exports.isListValueType = exports.isBytesValueType = exports.isAnyValueTypeName = exports.isAnyValueType = exports.isValueType = exports.isTimestamp = exports.isMapType = exports.isLong = exports.isRepeated = exports.isWithinOneOfThatShouldBeUnion = exports.isWithinOneOf = exports.isEnum = exports.isMessage = exports.isBytes = exports.isPrimitive = exports.isScalar = exports.createTypeMap = exports.notDefaultCheck = exports.defaultValue = exports.packedType = exports.toReaderCall = exports.basicTypeName = exports.basicLongWireType = exports.basicWireType = void 0;
3
+ exports.detectBatchMethod = exports.responsePromiseOrObservable = exports.responseObservable = exports.responsePromise = exports.responseType = exports.requestType = exports.rawRequestType = exports.detectMapType = exports.toTypeName = exports.getEnumMethod = exports.messageToTypeName = exports.wrapperTypeName = exports.valueTypeName = exports.isEmptyType = exports.isLongValueType = exports.isStructTypeName = exports.isStructType = exports.isListValueTypeName = exports.isListValueType = exports.isBytesValueType = exports.isAnyValueTypeName = exports.isAnyValueType = exports.isValueType = exports.isTimestamp = exports.isMapType = exports.isWholeNumber = exports.isLong = exports.isRepeated = exports.isWithinOneOfThatShouldBeUnion = exports.isWithinOneOf = exports.isEnum = exports.isMessage = exports.isBytes = exports.isPrimitive = exports.isOptionalProperty = exports.isScalar = exports.createTypeMap = exports.notDefaultCheck = exports.defaultValue = exports.packedType = exports.toReaderCall = exports.basicTypeName = exports.basicLongWireType = exports.basicWireType = void 0;
4
4
  const ts_proto_descriptors_1 = require("ts-proto-descriptors");
5
5
  const ts_poet_1 = require("ts-poet");
6
6
  const options_1 = require("./options");
@@ -225,8 +225,10 @@ function defaultValue(ctx, field) {
225
225
  }
226
226
  exports.defaultValue = defaultValue;
227
227
  /** Creates code that checks that the field is not the default value. Supports scalars and enums. */
228
- function notDefaultCheck(ctx, field, place) {
228
+ function notDefaultCheck(ctx, field, messageOptions, place) {
229
229
  const { typeMap, options } = ctx;
230
+ const isOptional = isOptionalProperty(field, messageOptions, options);
231
+ const maybeNotUndefinedAnd = isOptional ? `${place} !== undefined && ` : '';
230
232
  switch (field.type) {
231
233
  case ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_DOUBLE:
232
234
  case ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_FLOAT:
@@ -235,7 +237,7 @@ function notDefaultCheck(ctx, field, place) {
235
237
  case ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_SINT32:
236
238
  case ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_FIXED32:
237
239
  case ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_SFIXED32:
238
- return ts_poet_1.code `${place} !== 0`;
240
+ return ts_poet_1.code `${maybeNotUndefinedAnd} ${place} !== 0`;
239
241
  case ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_ENUM:
240
242
  // proto3 enforces enums starting at 0, however proto2 does not, so we have
241
243
  // to probe and see if zero is an allowed value. If it's not, pick the first one.
@@ -245,10 +247,10 @@ function notDefaultCheck(ctx, field, place) {
245
247
  const zerothValue = enumProto.value.find((v) => v.number === 0) || enumProto.value[0];
246
248
  if (options.stringEnums) {
247
249
  const enumType = messageToTypeName(ctx, field.typeName);
248
- return ts_poet_1.code `${place} !== ${enumType}.${zerothValue.name}`;
250
+ return ts_poet_1.code `${maybeNotUndefinedAnd} ${place} !== ${enumType}.${zerothValue.name}`;
249
251
  }
250
252
  else {
251
- return ts_poet_1.code `${place} !== ${zerothValue.number}`;
253
+ return ts_poet_1.code `${maybeNotUndefinedAnd} ${place} !== ${zerothValue.number}`;
252
254
  }
253
255
  case ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_UINT64:
254
256
  case ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_FIXED64:
@@ -256,20 +258,20 @@ function notDefaultCheck(ctx, field, place) {
256
258
  case ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_SINT64:
257
259
  case ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_SFIXED64:
258
260
  if (options.forceLong === options_1.LongOption.LONG) {
259
- return ts_poet_1.code `!${place}.isZero()`;
261
+ return ts_poet_1.code `${maybeNotUndefinedAnd} !${place}.isZero()`;
260
262
  }
261
263
  else if (options.forceLong === options_1.LongOption.STRING) {
262
- return ts_poet_1.code `${place} !== "0"`;
264
+ return ts_poet_1.code `${maybeNotUndefinedAnd} ${place} !== "0"`;
263
265
  }
264
266
  else {
265
- return ts_poet_1.code `${place} !== 0`;
267
+ return ts_poet_1.code `${maybeNotUndefinedAnd} ${place} !== 0`;
266
268
  }
267
269
  case ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_BOOL:
268
270
  return ts_poet_1.code `${place} === true`;
269
271
  case ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_STRING:
270
- return ts_poet_1.code `${place} !== ""`;
272
+ return ts_poet_1.code `${maybeNotUndefinedAnd} ${place} !== ""`;
271
273
  case ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_BYTES:
272
- return ts_poet_1.code `${place}.length !== 0`;
274
+ return ts_poet_1.code `${maybeNotUndefinedAnd} ${place}.length !== 0`;
273
275
  default:
274
276
  throw new Error('Not implemented for the given type.');
275
277
  }
@@ -314,6 +316,18 @@ function isScalar(field) {
314
316
  return scalarTypes.includes(field.type);
315
317
  }
316
318
  exports.isScalar = isScalar;
319
+ // When useOptionals='messages', non-scalar fields are translated into optional
320
+ // properties. When useOptionals='all', all fields are translated into
321
+ // optional properties, with the exception of map Entry key/values, which must
322
+ // always be present.
323
+ function isOptionalProperty(field, messageOptions, options) {
324
+ const optionalMessages = options.useOptionals === true || options.useOptionals === 'messages' || options.useOptionals === 'all';
325
+ const optionalAll = options.useOptionals === 'all';
326
+ return ((optionalMessages && isMessage(field) && !isRepeated(field)) ||
327
+ (optionalAll && !(messageOptions === null || messageOptions === void 0 ? void 0 : messageOptions.mapEntry)) ||
328
+ field.proto3Optional);
329
+ }
330
+ exports.isOptionalProperty = isOptionalProperty;
317
331
  /** This includes all scalars, enums and the [groups type](https://developers.google.com/protocol-buffers/docs/reference/java/com/google/protobuf/DescriptorProtos.FieldDescriptorProto.Type.html#TYPE_GROUP) */
318
332
  function isPrimitive(field) {
319
333
  return !isMessage(field);
@@ -347,6 +361,19 @@ function isLong(field) {
347
361
  return basicLongWireType(field.type) !== undefined;
348
362
  }
349
363
  exports.isLong = isLong;
364
+ function isWholeNumber(field) {
365
+ return (field.type === ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_INT32 ||
366
+ field.type === ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_INT64 ||
367
+ field.type === ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_UINT32 ||
368
+ field.type === ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_UINT64 ||
369
+ field.type === ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_SINT32 ||
370
+ field.type === ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_SINT64 ||
371
+ field.type === ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_FIXED32 ||
372
+ field.type === ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_FIXED64 ||
373
+ field.type === ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_SFIXED32 ||
374
+ field.type === ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_SFIXED64);
375
+ }
376
+ exports.isWholeNumber = isWholeNumber;
350
377
  function isMapType(ctx, messageDesc, field) {
351
378
  return detectMapType(ctx, messageDesc, field) !== undefined;
352
379
  }
@@ -462,11 +489,14 @@ function messageToTypeName(ctx, protoType, typeOptions = {}) {
462
489
  // them to basic built-in types, we union the type with undefined to
463
490
  // indicate the value is optional. Exceptions:
464
491
  // - If the field is repeated, values cannot be undefined.
465
- // - If useOptionals=true, all non-scalar types are already optional
466
- // properties, so there's no need for that union.
492
+ // - If useOptionals='messages' or useOptionals='all', all non-scalar types
493
+ // are already optional properties, so there's no need for that union.
467
494
  let valueType = valueTypeName(ctx, protoType);
468
495
  if (!typeOptions.keepValueType && valueType) {
469
- if (!!typeOptions.repeated || options.useOptionals) {
496
+ if (!!typeOptions.repeated ||
497
+ options.useOptionals === true ||
498
+ options.useOptionals === 'messages' ||
499
+ options.useOptionals === 'all') {
470
500
  return valueType;
471
501
  }
472
502
  return ts_poet_1.code `${valueType} | undefined`;
@@ -509,18 +539,21 @@ function toTypeName(ctx, messageDesc, field) {
509
539
  // in messageToTypeName, so no need to consider them for that here.
510
540
  return type;
511
541
  }
512
- // By default (useOptionals=false, oneof=properties), non-scalar fields
542
+ // By default (useOptionals='none', oneof=properties), non-scalar fields
513
543
  // outside oneofs and all fields within a oneof clause need to be unioned
514
544
  // with `undefined` to indicate the value is optional.
515
545
  //
516
- // When useOptionals=true, non-scalar fields are translated to optional
517
- // properties, so no need for the union with `undefined` here.
546
+ // When useOptionals='messages' or useOptionals='all', non-scalar fields are
547
+ // translated to optional properties, so no need for the union with
548
+ // `undefined` here.
518
549
  //
519
550
  // When oneof=unions, we generate a single property for the entire `oneof`
520
551
  // clause, spelling each option out inside a large type union. No need for
521
552
  // union with `undefined` here, either.
522
553
  const { options } = ctx;
523
- if ((!isWithinOneOf(field) && isMessage(field) && !options.useOptionals) ||
554
+ if ((!isWithinOneOf(field) &&
555
+ isMessage(field) &&
556
+ (options.useOptionals === false || options.useOptionals === 'none')) ||
524
557
  (isWithinOneOf(field) && options.oneof === options_1.OneofOption.PROPERTIES) ||
525
558
  (isWithinOneOf(field) && field.proto3Optional)) {
526
559
  return ts_poet_1.code `${type} | undefined`;
package/build/utils.js CHANGED
@@ -145,12 +145,14 @@ class FormattedMethodDescriptor {
145
145
  }
146
146
  exports.FormattedMethodDescriptor = FormattedMethodDescriptor;
147
147
  function determineFieldJsonName(field, options) {
148
- // By default jsonName is camelCased by the protocol compilier unless the user has
149
- // set a "json_name" option on this field.
150
- if (field.jsonName.length > 0) {
148
+ // jsonName will be camelCased by the protocol compiler, plus can be overridden by the user,
149
+ // so just use that instead of our own maybeSnakeToCamel
150
+ if (options.snakeToCamel.includes('json')) {
151
151
  return field.jsonName;
152
152
  }
153
- return case_1.maybeSnakeToCamel(field.name, options);
153
+ else {
154
+ return field.name;
155
+ }
154
156
  }
155
157
  exports.determineFieldJsonName = determineFieldJsonName;
156
158
  function impProto(options, module, type) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-proto",
3
- "version": "1.93.2",
3
+ "version": "1.95.1",
4
4
  "description": "",
5
5
  "main": "build/plugin.js",
6
6
  "repository": "github:stephenh/ts-proto",