ts-proto 1.112.0 → 1.113.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 CHANGED
@@ -1,3 +1,24 @@
1
+ # [1.113.0](https://github.com/stephenh/ts-proto/compare/v1.112.2...v1.113.0) (2022-05-27)
2
+
3
+
4
+ ### Features
5
+
6
+ * add options to schema output ([#437](https://github.com/stephenh/ts-proto/issues/437)) ([e8e4e39](https://github.com/stephenh/ts-proto/commit/e8e4e3937292e07280f5154b584e60124118f093))
7
+
8
+ ## [1.112.2](https://github.com/stephenh/ts-proto/compare/v1.112.1...v1.112.2) (2022-05-18)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * enum type returns 'UNRECOGNIZED' or '-1' in xxxToJSON/xxxToNumber ([#566](https://github.com/stephenh/ts-proto/issues/566)) ([19911a1](https://github.com/stephenh/ts-proto/commit/19911a1c62e3fe5c8a0eb0a795489340512898da))
14
+
15
+ ## [1.112.1](https://github.com/stephenh/ts-proto/compare/v1.112.0...v1.112.1) (2022-05-06)
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * use Long.fromValue instead of Long.fromString ([#562](https://github.com/stephenh/ts-proto/issues/562)) ([c99891e](https://github.com/stephenh/ts-proto/commit/c99891e0fec6d9b7225025eb0c5bd7393e80af36))
21
+
1
22
  # [1.112.0](https://github.com/stephenh/ts-proto/compare/v1.111.0...v1.112.0) (2022-05-02)
2
23
 
3
24
 
package/build/enums.js CHANGED
@@ -43,11 +43,11 @@ function generateEnum(ctx, fullName, enumDesc, sourceInfo) {
43
43
  }
44
44
  if (options.outputJsonMethods) {
45
45
  chunks.push(ts_poet_1.code `\n`);
46
- chunks.push(generateEnumToJson(fullName, enumDesc));
46
+ chunks.push(generateEnumToJson(ctx, fullName, enumDesc));
47
47
  }
48
48
  if (options.stringEnums && options.outputEncodeMethods) {
49
49
  chunks.push(ts_poet_1.code `\n`);
50
- chunks.push(generateEnumToNumber(fullName, enumDesc));
50
+ chunks.push(generateEnumToNumber(ctx, fullName, enumDesc));
51
51
  }
52
52
  return ts_poet_1.joinCode(chunks, { on: '\n' });
53
53
  }
@@ -87,7 +87,8 @@ function generateEnumFromJson(ctx, fullName, enumDesc) {
87
87
  }
88
88
  exports.generateEnumFromJson = generateEnumFromJson;
89
89
  /** Generates a function with a big switch statement to encode our enum -> JSON. */
90
- function generateEnumToJson(fullName, enumDesc) {
90
+ function generateEnumToJson(ctx, fullName, enumDesc) {
91
+ const { options, utils } = ctx;
91
92
  const chunks = [];
92
93
  const functionName = case_1.camelCase(fullName) + 'ToJSON';
93
94
  chunks.push(ts_poet_1.code `export function ${ts_poet_1.def(functionName)}(object: ${fullName}): string {`);
@@ -95,14 +96,28 @@ function generateEnumToJson(fullName, enumDesc) {
95
96
  for (const valueDesc of enumDesc.value) {
96
97
  chunks.push(ts_poet_1.code `case ${fullName}.${valueDesc.name}: return "${valueDesc.name}";`);
97
98
  }
98
- chunks.push(ts_poet_1.code `default: return "UNKNOWN";`);
99
+ if (options.unrecognizedEnum) {
100
+ chunks.push(ts_poet_1.code `
101
+ case ${fullName}.${UNRECOGNIZED_ENUM_NAME}:
102
+ default:
103
+ return "${UNRECOGNIZED_ENUM_NAME}";
104
+ `);
105
+ }
106
+ else {
107
+ // We use globalThis to avoid conflicts on protobuf types named `Error`.
108
+ chunks.push(ts_poet_1.code `
109
+ default:
110
+ throw new ${utils.globalThis}.Error("Unrecognized enum value " + object + " for enum ${fullName}");
111
+ `);
112
+ }
99
113
  chunks.push(ts_poet_1.code `}`);
100
114
  chunks.push(ts_poet_1.code `}`);
101
115
  return ts_poet_1.joinCode(chunks, { on: '\n' });
102
116
  }
103
117
  exports.generateEnumToJson = generateEnumToJson;
104
118
  /** Generates a function with a big switch statement to encode our string enum -> int value. */
105
- function generateEnumToNumber(fullName, enumDesc) {
119
+ function generateEnumToNumber(ctx, fullName, enumDesc) {
120
+ const { options, utils } = ctx;
106
121
  const chunks = [];
107
122
  const functionName = case_1.camelCase(fullName) + 'ToNumber';
108
123
  chunks.push(ts_poet_1.code `export function ${ts_poet_1.def(functionName)}(object: ${fullName}): number {`);
@@ -110,7 +125,20 @@ function generateEnumToNumber(fullName, enumDesc) {
110
125
  for (const valueDesc of enumDesc.value) {
111
126
  chunks.push(ts_poet_1.code `case ${fullName}.${valueDesc.name}: return ${valueDesc.number};`);
112
127
  }
113
- chunks.push(ts_poet_1.code `default: return 0;`);
128
+ if (options.unrecognizedEnum) {
129
+ chunks.push(ts_poet_1.code `
130
+ case ${fullName}.${UNRECOGNIZED_ENUM_NAME}:
131
+ default:
132
+ return ${UNRECOGNIZED_ENUM_VALUE};
133
+ `);
134
+ }
135
+ else {
136
+ // We use globalThis to avoid conflicts on protobuf types named `Error`.
137
+ chunks.push(ts_poet_1.code `
138
+ default:
139
+ throw new ${utils.globalThis}.Error("Unrecognized enum value " + object + " for enum ${fullName}");
140
+ `);
141
+ }
114
142
  chunks.push(ts_poet_1.code `}`);
115
143
  chunks.push(ts_poet_1.code `}`);
116
144
  return ts_poet_1.joinCode(chunks, { on: '\n' });
package/build/main.js CHANGED
@@ -948,7 +948,7 @@ function generateFromJson(ctx, fullName, fullTypeName, messageDesc) {
948
948
  }
949
949
  else if (types_1.isLong(field) && options.forceLong === options_1.LongOption.LONG) {
950
950
  const cstr = case_1.capitalize(types_1.basicTypeName(ctx, field, { keepValueType: true }).toCodeString());
951
- return ts_poet_1.code `${cstr}.fromString(${from})`;
951
+ return ts_poet_1.code `${cstr}.fromValue(${from})`;
952
952
  }
953
953
  else {
954
954
  const cstr = case_1.capitalize(types_1.basicTypeName(ctx, field, { keepValueType: true }).toCodeString());
package/build/schema.js CHANGED
@@ -5,16 +5,50 @@ const ts_proto_descriptors_1 = require("ts-proto-descriptors");
5
5
  const ts_poet_1 = require("ts-poet");
6
6
  const visit_1 = require("./visit");
7
7
  const utils_1 = require("./utils");
8
+ const types_1 = require("./types");
9
+ const minimal_1 = require("protobufjs/minimal");
8
10
  const fileDescriptorProto = ts_poet_1.imp('FileDescriptorProto@ts-proto-descriptors');
11
+ const extensionCache = {};
9
12
  function generateSchema(ctx, fileDesc, sourceInfo) {
10
13
  var _a;
11
14
  const { options } = ctx;
12
15
  const chunks = [];
16
+ fileDesc.extension.forEach((extension) => {
17
+ if (!(extension.extendee in extensionCache)) {
18
+ extensionCache[extension.extendee] = {};
19
+ }
20
+ extensionCache[extension.extendee][extension.number] = extension;
21
+ });
13
22
  chunks.push(ts_poet_1.code `
23
+ type ProtoMetaMessageOptions = {
24
+ options?: { [key: string]: any };
25
+ fields?: { [key: string]: { [key: string]: any } };
26
+ oneof?: { [key: string]: { [key: string]: any } };
27
+ nested?: { [key: string]: ProtoMetaMessageOptions };
28
+ };
29
+
14
30
  export interface ProtoMetadata {
15
31
  fileDescriptor: ${fileDescriptorProto};
16
32
  references: { [key: string]: any };
17
33
  dependencies?: ProtoMetadata[];
34
+ options?: {
35
+ options?: { [key: string]: any };
36
+ services?: {
37
+ [key: string]: {
38
+ options?: { [key: string]: any };
39
+ methods?: { [key: string]: { [key: string]: any } };
40
+ }
41
+ };
42
+ messages?: {
43
+ [key: string]: ProtoMetaMessageOptions;
44
+ };
45
+ enums?: {
46
+ [key: string]: {
47
+ options?: { [key: string]: any };
48
+ values?: { [key: string]: { [key: string]: any } };
49
+ };
50
+ };
51
+ };
18
52
  }
19
53
  `);
20
54
  const references = [];
@@ -42,13 +76,167 @@ function generateSchema(ctx, fileDesc, sourceInfo) {
42
76
  descriptor.sourceCodeInfo = {
43
77
  location: ((_a = descriptor.sourceCodeInfo) === null || _a === void 0 ? void 0 : _a.location.filter((loc) => loc['leadingComments'] || loc['trailingComments'])) || [],
44
78
  };
79
+ let fileOptions;
80
+ if (fileDesc.options) {
81
+ fileOptions = encodedOptionsToOptions(ctx, '.google.protobuf.FileOptions', fileDesc.options['_unknownFields']);
82
+ delete fileDesc.options['_unknownFields'];
83
+ }
84
+ const messagesOptions = [];
85
+ (fileDesc.messageType || []).forEach((message) => {
86
+ const resolvedMessage = resolveMessageOptions(ctx, message);
87
+ if (resolvedMessage) {
88
+ messagesOptions.push(resolvedMessage);
89
+ }
90
+ });
91
+ const servicesOptions = [];
92
+ (fileDesc.service || []).forEach((service) => {
93
+ const methodsOptions = [];
94
+ service.method.forEach((method) => {
95
+ if (method.options) {
96
+ const methodOptions = encodedOptionsToOptions(ctx, '.google.protobuf.MethodOptions', method.options['_unknownFields']);
97
+ delete method.options['_unknownFields'];
98
+ if (methodOptions) {
99
+ methodsOptions.push(ts_poet_1.code `'${method.name}': ${methodOptions}`);
100
+ }
101
+ }
102
+ });
103
+ let serviceOptions;
104
+ if (service.options) {
105
+ serviceOptions = encodedOptionsToOptions(ctx, '.google.protobuf.ServiceOptions', service.options['_unknownFields']);
106
+ delete service.options['_unknownFields'];
107
+ }
108
+ if (methodsOptions.length > 0 || serviceOptions) {
109
+ servicesOptions.push(ts_poet_1.code `
110
+ '${service.name}': {
111
+ options: ${serviceOptions},
112
+ methods: {${ts_poet_1.joinCode(methodsOptions, { on: ',' })}}
113
+ }
114
+ `);
115
+ }
116
+ });
117
+ const enumsOptions = [];
118
+ (fileDesc.enumType || []).forEach((Enum) => {
119
+ const valuesOptions = [];
120
+ Enum.value.forEach((value) => {
121
+ if (value.options) {
122
+ const valueOptions = encodedOptionsToOptions(ctx, '.google.protobuf.EnumValueOptions', value.options['_unknownFields']);
123
+ delete value.options['_unknownFields'];
124
+ if (valueOptions) {
125
+ valuesOptions.push(ts_poet_1.code `'${value.name}': ${valueOptions}`);
126
+ }
127
+ }
128
+ });
129
+ let enumOptions;
130
+ if (Enum.options) {
131
+ enumOptions = encodedOptionsToOptions(ctx, '.google.protobuf.EnumOptions', Enum.options['_unknownFields']);
132
+ delete Enum.options['_unknownFields'];
133
+ }
134
+ if (valuesOptions.length > 0 || enumOptions) {
135
+ enumsOptions.push(ts_poet_1.code `
136
+ '${Enum.name}': {
137
+ options: ${enumOptions},
138
+ values: {${ts_poet_1.joinCode(valuesOptions, { on: ',' })}}
139
+ }
140
+ `);
141
+ }
142
+ });
45
143
  chunks.push(ts_poet_1.code `
46
144
  export const ${ts_poet_1.def('protoMetadata')}: ProtoMetadata = {
47
145
  fileDescriptor: ${fileDescriptorProto}.fromPartial(${descriptor}),
48
146
  references: { ${ts_poet_1.joinCode(references, { on: ',' })} },
49
147
  dependencies: [${ts_poet_1.joinCode(dependencies, { on: ',' })}],
148
+ ${fileOptions || messagesOptions.length > 0 || servicesOptions.length > 0 || enumsOptions.length > 0
149
+ ? ts_poet_1.code `options: {
150
+ ${fileOptions ? ts_poet_1.code `options: ${fileOptions},` : ''}
151
+ ${messagesOptions.length > 0 ? ts_poet_1.code `messages: {${ts_poet_1.joinCode(messagesOptions, { on: ',' })}},` : ''}
152
+ ${servicesOptions.length > 0 ? ts_poet_1.code `services: {${ts_poet_1.joinCode(servicesOptions, { on: ',' })}},` : ''}
153
+ ${enumsOptions.length > 0 ? ts_poet_1.code `enums: {${ts_poet_1.joinCode(enumsOptions, { on: ',' })}}` : ''}
154
+ }`
155
+ : ''}
50
156
  }
51
157
  `);
52
158
  return chunks;
53
159
  }
54
160
  exports.generateSchema = generateSchema;
161
+ function getExtensionValue(ctx, extension, data) {
162
+ if (extension.type == ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_MESSAGE) {
163
+ const typeName = types_1.basicTypeName(ctx, extension);
164
+ const resultBuffer = Buffer.concat(data.map((d) => {
165
+ // Skip length byte
166
+ const reader = new minimal_1.Reader(d);
167
+ reader.uint32();
168
+ return reader.buf.slice(reader.pos);
169
+ }));
170
+ const result = resultBuffer.toString('base64');
171
+ return ts_poet_1.code `'${extension.name}': ${typeName}.decode(Buffer.from('${result}', 'base64'))`;
172
+ }
173
+ else {
174
+ const reader = new minimal_1.Reader(data[0]);
175
+ let value = reader[types_1.toReaderCall(extension)]();
176
+ if (typeof value === 'string') {
177
+ value = ts_poet_1.code `"${value}"`;
178
+ }
179
+ return ts_poet_1.code `'${extension.name}': ${value}`;
180
+ }
181
+ }
182
+ /** Takes the protoc's input of options as proto-encoded messages, and turns them into embedded-able-in-source-code representations. */
183
+ function encodedOptionsToOptions(ctx, extendee, encodedOptions) {
184
+ if (!encodedOptions) {
185
+ return undefined;
186
+ }
187
+ const resultOptions = [];
188
+ for (const [key, value] of Object.entries(encodedOptions)) {
189
+ const extension = extensionCache[extendee][parseInt(key, 10) >>> 3];
190
+ resultOptions.push(getExtensionValue(ctx, extension, value));
191
+ }
192
+ if (resultOptions.length == 0) {
193
+ return undefined;
194
+ }
195
+ return ts_poet_1.code `{${ts_poet_1.joinCode(resultOptions, { on: ',' })}}`;
196
+ }
197
+ function resolveMessageOptions(ctx, message) {
198
+ const fieldsOptions = [];
199
+ message.field.forEach((field) => {
200
+ if (field.options) {
201
+ const fieldOptions = encodedOptionsToOptions(ctx, '.google.protobuf.FieldOptions', field.options['_unknownFields']);
202
+ delete field.options['_unknownFields'];
203
+ if (fieldOptions) {
204
+ fieldsOptions.push(ts_poet_1.code `'${field.name}': ${fieldOptions}`);
205
+ }
206
+ }
207
+ });
208
+ const oneOfsOptions = [];
209
+ message.oneofDecl.forEach((oneOf) => {
210
+ if (oneOf.options) {
211
+ const oneOfOptions = encodedOptionsToOptions(ctx, '.google.protobuf.OneofOptions', oneOf.options['_unknownFields']);
212
+ delete oneOf.options['_unknownFields'];
213
+ if (oneOfOptions) {
214
+ oneOfsOptions.push(ts_poet_1.code `'${oneOf.name}': ${oneOfOptions}`);
215
+ }
216
+ }
217
+ });
218
+ let nestedOptions = [];
219
+ if (message.nestedType && message.nestedType.length > 0) {
220
+ message.nestedType.forEach((nested) => {
221
+ const resolvedMessage = resolveMessageOptions(ctx, nested);
222
+ if (resolvedMessage) {
223
+ nestedOptions.push(resolvedMessage);
224
+ }
225
+ });
226
+ }
227
+ let messageOptions;
228
+ if (message.options) {
229
+ messageOptions = encodedOptionsToOptions(ctx, '.google.protobuf.MessageOptions', message.options['_unknownFields']);
230
+ delete message.options['_unknownFields'];
231
+ }
232
+ if (fieldsOptions.length > 0 || oneOfsOptions.length > 0 || nestedOptions.length > 0 || messageOptions) {
233
+ return ts_poet_1.code `
234
+ '${message.name}': {
235
+ ${messageOptions ? ts_poet_1.code `options: ${messageOptions},` : ''}
236
+ ${fieldsOptions.length > 0 ? ts_poet_1.code `fields: {${ts_poet_1.joinCode(fieldsOptions, { on: ',' })}},` : ''}
237
+ ${oneOfsOptions.length > 0 ? ts_poet_1.code `oneof: {${ts_poet_1.joinCode(oneOfsOptions, { on: ',' })}},` : ''}
238
+ ${nestedOptions.length > 0 ? ts_poet_1.code `nested: {${ts_poet_1.joinCode(nestedOptions, { on: ',' })}},` : ''}
239
+ }
240
+ `;
241
+ }
242
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-proto",
3
- "version": "1.112.0",
3
+ "version": "1.113.0",
4
4
  "description": "",
5
5
  "main": "build/plugin.js",
6
6
  "repository": "github:stephenh/ts-proto",