ts-proto 2.21.0 → 2.22.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
@@ -39,6 +39,7 @@ Any tips or tricks for others on the migration would also be appreciated!
39
39
  - [Highlights](#highlights)
40
40
  - [Auto-Batching / N+1 Prevention](#auto-batching--n1-prevention)
41
41
  - [Usage](#usage)
42
+ - [Options file](#options-file)
42
43
  - [Supported options](#supported-options)
43
44
  - [NestJS Support](#nestjs-support)
44
45
  - [Watch Mode](#watch-mode)
@@ -309,6 +310,22 @@ protobuf {
309
310
 
310
311
  Generated code will be placed in the Gradle build directory.
311
312
 
313
+ ### Options file
314
+
315
+ If you pass many options, you can put them in a JSON file and pass its path with `--ts_proto_opt=optionsFile=<path>`, e.g. `--ts_proto_opt=optionsFile=ts-proto.json` with a `ts-proto.json` of:
316
+
317
+ ```json
318
+ {
319
+ "esModuleInterop": true,
320
+ "forceLong": "bigint",
321
+ "outputServices": ["nice-grpc", "generic-definitions"],
322
+ "useDate": "string",
323
+ "Mgoogle/protobuf/empty.proto": "./google3/protobuf/empty"
324
+ }
325
+ ```
326
+
327
+ The keys and values are the same as the `--ts_proto_opt` options below, and options that can be passed multiple times (like `outputServices`) are given as arrays. The path is relative to the directory that `protoc` is run from, and any options also passed as `--ts_proto_opt`s take precedence over the ones in the file. Since the plugin reads the file itself, this needs a local `protoc-gen-ts_proto`, i.e. it doesn't work with the remote Buf Registry plugin.
328
+
312
329
  ### Supported options
313
330
 
314
331
  - With `--ts_proto_opt=globalThisPolyfill=true`, ts-proto will include a polyfill for globalThis.
package/build/src/main.js CHANGED
@@ -1932,7 +1932,11 @@ function valueTypeFromJson(ctx, field, from) {
1932
1932
  return (0, ts_poet_1.code) `BigInt(${from})`;
1933
1933
  }
1934
1934
  else if ((0, types_1.isBytesValueType)(field)) {
1935
- return (0, ts_poet_1.code) `new ${(0, case_1.capitalize)(valueType.toCodeString([]))}(${from})`;
1935
+ // BytesValue is a base64 string in JSON, like a plain `bytes` field.
1936
+ if (options.env === options_1.EnvOption.NODE) {
1937
+ return (0, ts_poet_1.code) `Buffer.from(${ctx.utils.bytesFromBase64}(${from}))`;
1938
+ }
1939
+ return (0, ts_poet_1.code) `${ctx.utils.bytesFromBase64}(${from})`;
1936
1940
  }
1937
1941
  else {
1938
1942
  return (0, ts_poet_1.code) `${(0, case_1.capitalize)(valueType.toCodeString([]))}(${from})`;
@@ -2555,7 +2559,7 @@ function generateToJson(ctx, fullName, fullProtobufTypeName, messageDesc) {
2555
2559
  else if ((0, types_1.isMessage)(field) && !(0, types_1.isValueType)(ctx, field) && !(0, types_1.isMapType)(ctx, messageDesc, field)) {
2556
2560
  return (0, ts_poet_1.code) `${(0, types_1.getMessageMethod)(ctx, field.typeName, "toJSON")}(${from})`;
2557
2561
  }
2558
- else if ((0, types_1.isBytes)(field)) {
2562
+ else if ((0, types_1.isBytes)(field) || (0, types_1.isBytesValueType)(field)) {
2559
2563
  return (0, ts_poet_1.code) `${utils.base64FromBytes}(${from})`;
2560
2564
  }
2561
2565
  else if (isLongValueTypeAsObject(options, field)) {
@@ -5,6 +5,7 @@ exports.defaultOptions = defaultOptions;
5
5
  exports.optionsFromParameter = optionsFromParameter;
6
6
  exports.getTsPoetOpts = getTsPoetOpts;
7
7
  exports.addTypeToMessages = addTypeToMessages;
8
+ const fs_1 = require("fs");
8
9
  var LongOption;
9
10
  (function (LongOption) {
10
11
  LongOption["NUMBER"] = "number";
@@ -156,7 +157,7 @@ const nestJsOptions = {
156
157
  function optionsFromParameter(parameter) {
157
158
  const options = defaultOptions();
158
159
  if (parameter) {
159
- const parsed = parseParameter(parameter);
160
+ const parsed = withOptionsFile(parseParameter(parameter));
160
161
  if (parsed.nestJs) {
161
162
  Object.assign(options, nestJsOptions);
162
163
  }
@@ -323,6 +324,62 @@ function parseParameter(parameter) {
323
324
  function parseParamValue(value) {
324
325
  return value === "true" ? true : value === "false" ? false : value;
325
326
  }
327
+ // Merges the options from an `optionsFile=<path>` JSON file, if one was given, into the
328
+ // options parsed from the protoc parameter, which take precedence over the file's values.
329
+ function withOptionsFile(parsed) {
330
+ const { optionsFile, ...params } = parsed;
331
+ if (optionsFile === undefined) {
332
+ return parsed;
333
+ }
334
+ if (typeof optionsFile !== "string" || optionsFile === "") {
335
+ throw new Error(`optionsFile must be set to a single file path, but got '${optionsFile}'`);
336
+ }
337
+ const fromFile = readOptionsFile(optionsFile);
338
+ return { ...fromFile, ...params, M: { ...fromFile.M, ...params.M } };
339
+ }
340
+ // Reads a JSON object of options, e.g. `{ "useDate": "string", "outputServices": ["grpc-js", "generic-definitions"] }`,
341
+ // and returns the same values that `parseParameter` would for the equivalent protoc parameters.
342
+ function readOptionsFile(path) {
343
+ let json;
344
+ try {
345
+ json = JSON.parse((0, fs_1.readFileSync)(path, "utf-8"));
346
+ }
347
+ catch (e) {
348
+ throw new Error(`Could not read optionsFile '${path}': ${e.message}`);
349
+ }
350
+ if (typeof json !== "object" || json === null || Array.isArray(json)) {
351
+ throw new Error(`optionsFile '${path}' must contain a JSON object of options`);
352
+ }
353
+ const options = { M: {} };
354
+ for (const [key, value] of Object.entries(json)) {
355
+ if (key === "optionsFile") {
356
+ throw new Error(`optionsFile '${path}' can't set optionsFile`);
357
+ }
358
+ else if (key.charAt(0) === "M") {
359
+ if (typeof value !== "string") {
360
+ throw new Error(`optionsFile '${path}' must set ${key} to a string, but got ${JSON.stringify(value)}`);
361
+ }
362
+ options.M[key.substring(1)] = value;
363
+ }
364
+ else if (isOptionValue(value)) {
365
+ options[key] = normalizeOptionValue(value);
366
+ }
367
+ else if (Array.isArray(value) && value.length > 0 && value.every(isOptionValue)) {
368
+ // A single-element array is the same as passing the option once
369
+ options[key] = value.length === 1 ? normalizeOptionValue(value[0]) : value.map(normalizeOptionValue);
370
+ }
371
+ else {
372
+ throw new Error(`optionsFile '${path}' must set ${key} to a string, number, boolean, or non-empty array of them, but got ${JSON.stringify(value)}`);
373
+ }
374
+ }
375
+ return options;
376
+ }
377
+ function isOptionValue(value) {
378
+ return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
379
+ }
380
+ function normalizeOptionValue(value) {
381
+ return parseParamValue(String(value));
382
+ }
326
383
  function getTsPoetOpts(options, tsProtoVersion, protocVersion, fileName) {
327
384
  const { importSuffix, esModuleInterop } = options;
328
385
  const pbjs = "protobufjs/minimal" + importSuffix;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-proto",
3
- "version": "2.21.0",
3
+ "version": "2.22.0",
4
4
  "description": "",
5
5
  "main": "build/src/plugin.js",
6
6
  "repository": "github:stephenh/ts-proto",