ts-proto 1.112.2 → 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,10 @@
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
+
1
8
  ## [1.112.2](https://github.com/stephenh/ts-proto/compare/v1.112.1...v1.112.2) (2022-05-18)
2
9
 
3
10
 
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.2",
3
+ "version": "1.113.0",
4
4
  "description": "",
5
5
  "main": "build/plugin.js",
6
6
  "repository": "github:stephenh/ts-proto",