ts-proto 1.95.0 → 1.97.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/CHANGELOG.md +28 -0
- package/README.markdown +8 -2
- package/build/enums.js +17 -4
- package/build/generate-grpc-js.js +1 -0
- package/build/main.js +48 -36
- package/build/options.js +2 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,31 @@
|
|
|
1
|
+
# [1.97.0](https://github.com/stephenh/ts-proto/compare/v1.96.1...v1.97.0) (2021-12-30)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* add an option to disable Exact types ([#456](https://github.com/stephenh/ts-proto/issues/456)) ([9c53d7e](https://github.com/stephenh/ts-proto/commit/9c53d7efb0252c7ea0af85a5d161ff94bcd69760))
|
|
7
|
+
|
|
8
|
+
## [1.96.1](https://github.com/stephenh/ts-proto/compare/v1.96.0...v1.96.1) (2021-12-28)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Performance Improvements
|
|
12
|
+
|
|
13
|
+
* optimize object creation in `decode`, `fromJSON` and `fromPartial` ([#457](https://github.com/stephenh/ts-proto/issues/457)) ([70832d3](https://github.com/stephenh/ts-proto/commit/70832d33bae82ecb3c5f87845d14e992a13437e4))
|
|
14
|
+
|
|
15
|
+
# [1.96.0](https://github.com/stephenh/ts-proto/compare/v1.95.1...v1.96.0) (2021-12-24)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* `enumsAsLiterals` option ([#450](https://github.com/stephenh/ts-proto/issues/450)) ([fcaade2](https://github.com/stephenh/ts-proto/commit/fcaade2855ae28ea3553a365556ccb92a9644d70))
|
|
21
|
+
|
|
22
|
+
## [1.95.1](https://github.com/stephenh/ts-proto/compare/v1.95.0...v1.95.1) (2021-12-23)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Bug Fixes
|
|
26
|
+
|
|
27
|
+
* Add service to the client constructor. ([#455](https://github.com/stephenh/ts-proto/issues/455)) ([8c32104](https://github.com/stephenh/ts-proto/commit/8c32104a8522cfe2febcf2338d51710021d837ff))
|
|
28
|
+
|
|
1
29
|
# [1.95.0](https://github.com/stephenh/ts-proto/compare/v1.94.0...v1.95.0) (2021-12-14)
|
|
2
30
|
|
|
3
31
|
|
package/README.markdown
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
> `ts-proto` transforms your `.proto` files into strongly-typed, idiomatic TypeScript files!
|
|
7
7
|
|
|
8
|
-
(Note, if you're a new user of ts-proto and using a modern TS setup with `esModuleInterop`, you need to also pass that as a `ts_proto_opt`.)
|
|
8
|
+
(Note, if you're a new user of ts-proto and using a modern TS setup with `esModuleInterop`, or want to use ts-proto in ESM / snowpack / vite, you need to also pass that as a `ts_proto_opt`.)
|
|
9
9
|
|
|
10
10
|
## Table of contents
|
|
11
11
|
|
|
@@ -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`
|
|
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
|
|
|
@@ -348,6 +348,12 @@ Generated code will be placed in the Gradle build directory.
|
|
|
348
348
|
|
|
349
349
|
- With `--ts_proto_opt=fileSuffix=<SUFFIX>`, ts-proto will emit generated files using the specified suffix. A `helloworld.proto` file with `fileSuffix=.pb` would be generated as `helloworld.pb.ts`. This is common behavior in other protoc plugins and provides a way to quickly glob all the generated files.
|
|
350
350
|
|
|
351
|
+
- With `--ts_proto_opt=enumsAsLiterals=true`, the generated enum types will be enum-ish object with `as const`.
|
|
352
|
+
|
|
353
|
+
- With `--ts_proto_opt=useExactTypes=false`, the generated `fromPartial` method will not use Exact types.
|
|
354
|
+
|
|
355
|
+
The default behavior is `useExactTypes=true`, which makes `fromPartial` use Exact type for its argument to make TypeScript reject any unknown properties.
|
|
356
|
+
|
|
351
357
|
### Only Types
|
|
352
358
|
|
|
353
359
|
If you're looking for `ts-proto` to generate only types for your Protobuf types then passing all three of `outputEncodeMethods`, `outputJsonMethods`, and `outputClientImpl` as `false` is probably what you want, i.e.:
|
package/build/enums.js
CHANGED
|
@@ -13,17 +13,30 @@ function generateEnum(ctx, fullName, enumDesc, sourceInfo) {
|
|
|
13
13
|
const { options } = ctx;
|
|
14
14
|
const chunks = [];
|
|
15
15
|
utils_1.maybeAddComment(sourceInfo, chunks, (_a = enumDesc.options) === null || _a === void 0 ? void 0 : _a.deprecated);
|
|
16
|
-
|
|
16
|
+
if (options.enumsAsLiterals) {
|
|
17
|
+
chunks.push(ts_poet_1.code `export const ${ts_poet_1.def(fullName)} = {`);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
chunks.push(ts_poet_1.code `export ${options.constEnums ? 'const ' : ''}enum ${ts_poet_1.def(fullName)} {`);
|
|
21
|
+
}
|
|
22
|
+
const delimiter = options.enumsAsLiterals ? ':' : '=';
|
|
17
23
|
enumDesc.value.forEach((valueDesc, index) => {
|
|
18
24
|
var _a;
|
|
19
25
|
const info = sourceInfo.lookup(sourceInfo_1.Fields.enum.value, index);
|
|
20
26
|
utils_1.maybeAddComment(info, chunks, (_a = valueDesc.options) === null || _a === void 0 ? void 0 : _a.deprecated, `${valueDesc.name} - `);
|
|
21
|
-
chunks.push(ts_poet_1.code `${valueDesc.name}
|
|
27
|
+
chunks.push(ts_poet_1.code `${valueDesc.name} ${delimiter} ${options.stringEnums ? `"${valueDesc.name}"` : valueDesc.number.toString()},`);
|
|
22
28
|
});
|
|
23
29
|
if (options.unrecognizedEnum)
|
|
24
30
|
chunks.push(ts_poet_1.code `
|
|
25
|
-
${UNRECOGNIZED_ENUM_NAME}
|
|
26
|
-
|
|
31
|
+
${UNRECOGNIZED_ENUM_NAME} ${delimiter} ${options.stringEnums ? `"${UNRECOGNIZED_ENUM_NAME}"` : UNRECOGNIZED_ENUM_VALUE.toString()},`);
|
|
32
|
+
if (options.enumsAsLiterals) {
|
|
33
|
+
chunks.push(ts_poet_1.code `} as const`);
|
|
34
|
+
chunks.push(ts_poet_1.code `\n`);
|
|
35
|
+
chunks.push(ts_poet_1.code `export type ${ts_poet_1.def(fullName)} = typeof ${ts_poet_1.def(fullName)}[keyof typeof ${ts_poet_1.def(fullName)}]`);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
chunks.push(ts_poet_1.code `}`);
|
|
39
|
+
}
|
|
27
40
|
if (options.outputJsonMethods || (options.stringEnums && options.outputEncodeMethods)) {
|
|
28
41
|
chunks.push(ts_poet_1.code `\n`);
|
|
29
42
|
chunks.push(generateEnumFromJson(ctx, fullName, enumDesc));
|
package/build/main.js
CHANGED
|
@@ -70,7 +70,7 @@ function generateFile(ctx, fileDesc) {
|
|
|
70
70
|
// then add the encoder/decoder/base instance
|
|
71
71
|
visit_1.visit(fileDesc, sourceInfo, (fullName, message, sInfo, fullProtoTypeName) => {
|
|
72
72
|
const fullTypeName = utils_1.maybePrefixPackage(fileDesc, fullProtoTypeName);
|
|
73
|
-
chunks.push(
|
|
73
|
+
chunks.push(generateBaseInstanceFactory(ctx, fullName, message, fullTypeName));
|
|
74
74
|
const staticMembers = [];
|
|
75
75
|
if (options.outputTypeRegistry) {
|
|
76
76
|
staticMembers.push(ts_poet_1.code `$type: '${fullTypeName}' as const`);
|
|
@@ -460,20 +460,39 @@ function generateOneofProperty(ctx, messageDesc, oneofIndex, sourceInfo) {
|
|
|
460
460
|
return prop;
|
|
461
461
|
*/
|
|
462
462
|
}
|
|
463
|
-
// Create a 'base' instance with default values for decode to use as a prototype
|
|
464
|
-
function
|
|
465
|
-
const fields =
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
.
|
|
463
|
+
// Create a function that constructs 'base' instance with default values for decode to use as a prototype
|
|
464
|
+
function generateBaseInstanceFactory(ctx, fullName, messageDesc, fullTypeName) {
|
|
465
|
+
const fields = [];
|
|
466
|
+
// When oneof=unions, we generate a single property with an ADT per `oneof` clause.
|
|
467
|
+
const processedOneofs = new Set();
|
|
468
|
+
for (const field of messageDesc.field) {
|
|
469
|
+
if (types_1.isWithinOneOfThatShouldBeUnion(ctx.options, field)) {
|
|
470
|
+
const { oneofIndex } = field;
|
|
471
|
+
if (!processedOneofs.has(oneofIndex)) {
|
|
472
|
+
processedOneofs.add(oneofIndex);
|
|
473
|
+
const name = case_1.maybeSnakeToCamel(messageDesc.oneofDecl[oneofIndex].name, ctx.options);
|
|
474
|
+
fields.push(ts_poet_1.code `${name}: undefined`);
|
|
475
|
+
}
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
470
478
|
const name = case_1.maybeSnakeToCamel(field.name, ctx.options);
|
|
471
|
-
|
|
472
|
-
|
|
479
|
+
const val = types_1.isWithinOneOf(field)
|
|
480
|
+
? 'undefined'
|
|
481
|
+
: types_1.isMapType(ctx, messageDesc, field)
|
|
482
|
+
? '{}'
|
|
483
|
+
: types_1.isRepeated(field)
|
|
484
|
+
? '[]'
|
|
485
|
+
: types_1.defaultValue(ctx, field);
|
|
486
|
+
fields.push(ts_poet_1.code `${name}: ${val}`);
|
|
487
|
+
}
|
|
473
488
|
if (ctx.options.outputTypeRegistry) {
|
|
474
489
|
fields.unshift(ts_poet_1.code `$type: '${fullTypeName}'`);
|
|
475
490
|
}
|
|
476
|
-
return ts_poet_1.code `
|
|
491
|
+
return ts_poet_1.code `
|
|
492
|
+
function createBase${fullName}(): ${fullName} {
|
|
493
|
+
return { ${ts_poet_1.joinCode(fields, { on: ',' })} };
|
|
494
|
+
}
|
|
495
|
+
`;
|
|
477
496
|
}
|
|
478
497
|
/** Creates a function to decode a message by loop overing the tags. */
|
|
479
498
|
function generateDecode(ctx, fullName, messageDesc) {
|
|
@@ -487,22 +506,8 @@ function generateDecode(ctx, fullName, messageDesc) {
|
|
|
487
506
|
): ${fullName} {
|
|
488
507
|
const reader = input instanceof ${Reader} ? input : new ${Reader}(input);
|
|
489
508
|
let end = length === undefined ? reader.len : reader.pos + length;
|
|
490
|
-
const message =
|
|
509
|
+
const message = createBase${fullName}();
|
|
491
510
|
`);
|
|
492
|
-
// initialize all lists
|
|
493
|
-
messageDesc.field.filter(types_1.isRepeated).forEach((field) => {
|
|
494
|
-
const name = case_1.maybeSnakeToCamel(field.name, options);
|
|
495
|
-
const value = types_1.isMapType(ctx, messageDesc, field) ? '{}' : '[]';
|
|
496
|
-
chunks.push(ts_poet_1.code `message.${name} = ${value};`);
|
|
497
|
-
});
|
|
498
|
-
// initialize all buffers
|
|
499
|
-
messageDesc.field
|
|
500
|
-
.filter((field) => !types_1.isRepeated(field) && !types_1.isWithinOneOf(field) && types_1.isBytes(field))
|
|
501
|
-
.forEach((field) => {
|
|
502
|
-
const value = options.env === options_1.EnvOption.NODE ? 'Buffer.alloc(0)' : 'new Uint8Array()';
|
|
503
|
-
const name = case_1.maybeSnakeToCamel(field.name, options);
|
|
504
|
-
chunks.push(ts_poet_1.code `message.${name} = ${value};`);
|
|
505
|
-
});
|
|
506
511
|
// start the tag loop
|
|
507
512
|
chunks.push(ts_poet_1.code `
|
|
508
513
|
while (reader.pos < end) {
|
|
@@ -567,28 +572,29 @@ function generateDecode(ctx, fullName, messageDesc) {
|
|
|
567
572
|
}
|
|
568
573
|
// and then use the snippet to handle repeated fields if necessary
|
|
569
574
|
if (types_1.isRepeated(field)) {
|
|
575
|
+
const maybeNonNullAssertion = ctx.options.useOptionals === 'all' ? '!' : '';
|
|
570
576
|
if (types_1.isMapType(ctx, messageDesc, field)) {
|
|
571
577
|
// We need a unique const within the `cast` statement
|
|
572
578
|
const varName = `entry${field.number}`;
|
|
573
579
|
chunks.push(ts_poet_1.code `
|
|
574
580
|
const ${varName} = ${readSnippet};
|
|
575
581
|
if (${varName}.value !== undefined) {
|
|
576
|
-
message.${fieldName}[${varName}.key] = ${varName}.value;
|
|
582
|
+
message.${fieldName}${maybeNonNullAssertion}[${varName}.key] = ${varName}.value;
|
|
577
583
|
}
|
|
578
584
|
`);
|
|
579
585
|
}
|
|
580
586
|
else if (types_1.packedType(field.type) === undefined) {
|
|
581
|
-
chunks.push(ts_poet_1.code `message.${fieldName}.push(${readSnippet});`);
|
|
587
|
+
chunks.push(ts_poet_1.code `message.${fieldName}${maybeNonNullAssertion}.push(${readSnippet});`);
|
|
582
588
|
}
|
|
583
589
|
else {
|
|
584
590
|
chunks.push(ts_poet_1.code `
|
|
585
591
|
if ((tag & 7) === 2) {
|
|
586
592
|
const end2 = reader.uint32() + reader.pos;
|
|
587
593
|
while (reader.pos < end2) {
|
|
588
|
-
message.${fieldName}.push(${readSnippet});
|
|
594
|
+
message.${fieldName}${maybeNonNullAssertion}.push(${readSnippet});
|
|
589
595
|
}
|
|
590
596
|
} else {
|
|
591
|
-
message.${fieldName}.push(${readSnippet});
|
|
597
|
+
message.${fieldName}${maybeNonNullAssertion}.push(${readSnippet});
|
|
592
598
|
}
|
|
593
599
|
`);
|
|
594
600
|
}
|
|
@@ -799,7 +805,7 @@ function generateFromJson(ctx, fullName, messageDesc) {
|
|
|
799
805
|
// create the basic function declaration
|
|
800
806
|
chunks.push(ts_poet_1.code `
|
|
801
807
|
fromJSON(${messageDesc.field.length > 0 ? 'object' : '_'}: any): ${fullName} {
|
|
802
|
-
const message =
|
|
808
|
+
const message = createBase${fullName}();
|
|
803
809
|
`);
|
|
804
810
|
// add a check for each incoming field
|
|
805
811
|
messageDesc.field.forEach((field) => {
|
|
@@ -1095,13 +1101,19 @@ function generateToJson(ctx, fullName, messageDesc) {
|
|
|
1095
1101
|
function generateFromPartial(ctx, fullName, messageDesc) {
|
|
1096
1102
|
const { options, utils, typeMap } = ctx;
|
|
1097
1103
|
const chunks = [];
|
|
1098
|
-
const Timestamp = utils_1.impProto(options, 'google/protobuf/timestamp', 'Timestamp');
|
|
1099
1104
|
// create the basic function declaration
|
|
1100
1105
|
const paramName = messageDesc.field.length > 0 ? 'object' : '_';
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1106
|
+
if (ctx.options.useExactTypes) {
|
|
1107
|
+
chunks.push(ts_poet_1.code `
|
|
1108
|
+
fromPartial<I extends ${utils.Exact}<${utils.DeepPartial}<${fullName}>, I>>(${paramName}: I): ${fullName} {
|
|
1109
|
+
`);
|
|
1110
|
+
}
|
|
1111
|
+
else {
|
|
1112
|
+
chunks.push(ts_poet_1.code `
|
|
1113
|
+
fromPartial(${paramName}: ${utils.DeepPartial}<${fullName}>): ${fullName} {
|
|
1114
|
+
`);
|
|
1115
|
+
}
|
|
1116
|
+
chunks.push(ts_poet_1.code `const message = createBase${fullName}();`);
|
|
1105
1117
|
// add a check for each incoming field
|
|
1106
1118
|
messageDesc.field.forEach((field) => {
|
|
1107
1119
|
const fieldName = case_1.maybeSnakeToCamel(field.name, options);
|
package/build/options.js
CHANGED
|
@@ -48,6 +48,7 @@ function defaultOptions() {
|
|
|
48
48
|
outputTypeRegistry: false,
|
|
49
49
|
stringEnums: false,
|
|
50
50
|
constEnums: false,
|
|
51
|
+
enumsAsLiterals: false,
|
|
51
52
|
outputClientImpl: true,
|
|
52
53
|
outputServices: ServiceOption.DEFAULT,
|
|
53
54
|
returnObservable: false,
|
|
@@ -60,6 +61,7 @@ function defaultOptions() {
|
|
|
60
61
|
outputSchema: false,
|
|
61
62
|
onlyTypes: false,
|
|
62
63
|
emitImportedFiles: true,
|
|
64
|
+
useExactTypes: true,
|
|
63
65
|
};
|
|
64
66
|
}
|
|
65
67
|
exports.defaultOptions = defaultOptions;
|