diff --git a/README.markdown b/README.markdown index 47b344c62..7726615a2 100644 --- a/README.markdown +++ b/README.markdown @@ -408,6 +408,14 @@ Generated code will be placed in the Gradle build directory. This is useful if you want "only types". +- With `--ts_proto_opt=outputEncodeIncludeTypes=regex`, the `Message.encode` method will only be output for types whose name matches the provided regular expression. + + This is useful if you want to limit `encode` methods to only select types. + +- With `--ts_proto_opt=outputDecodeIncludeTypes=regex`, the `Message.decode` method will only be output for types whose name matches the provided regular expression. + + This is useful if you want to limit `decode` methods to only select types. + - With `--ts_proto_opt=outputJsonMethods=false`, the `Message.fromJSON` and `Message.toJSON` methods for working with JSON-coded data will not be output. This is also useful if you want "only types". diff --git a/integration/output-decode-include-types/encode.proto b/integration/output-decode-include-types/encode.proto new file mode 100644 index 000000000..78048a564 --- /dev/null +++ b/integration/output-decode-include-types/encode.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; + +import "google/protobuf/wrappers.proto"; + +message EncodeWithDecodeMethod { + string encode = 1; +} + +message EncodeDontGenerateDecodeMethod { + string encode_dont_generate_decode_method = 1; +} \ No newline at end of file diff --git a/integration/output-decode-include-types/encode.ts b/integration/output-decode-include-types/encode.ts new file mode 100644 index 000000000..86327542a --- /dev/null +++ b/integration/output-decode-include-types/encode.ts @@ -0,0 +1,49 @@ +// Code generated by protoc-gen-ts_proto. DO NOT EDIT. +// source: encode.proto + +/* eslint-disable */ +import { BinaryReader } from "@bufbuild/protobuf/wire"; + +export const protobufPackage = ""; + +export interface EncodeWithDecodeMethod { + encode: string; +} + +export interface EncodeDontGenerateDecodeMethod { + encodeDontGenerateDecodeMethod: string; +} + +function createBaseEncodeWithDecodeMethod(): EncodeWithDecodeMethod { + return { encode: "" }; +} + +export const EncodeWithDecodeMethod: MessageFns = { + decode(input: BinaryReader | Uint8Array, length?: number): EncodeWithDecodeMethod { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseEncodeWithDecodeMethod(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.encode = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, +}; + +export interface MessageFns { + decode(input: BinaryReader | Uint8Array, length?: number): T; +} diff --git a/integration/output-decode-include-types/parameters.txt b/integration/output-decode-include-types/parameters.txt new file mode 100644 index 000000000..5507187bc --- /dev/null +++ b/integration/output-decode-include-types/parameters.txt @@ -0,0 +1 @@ +outputEncodeMethods=decode-only,outputPartialMethods=false,outputJsonMethods=false,nestJs=false,outputDecodeIncludeTypes=^EncodeWithDecodeMethod \ No newline at end of file diff --git a/integration/output-encode-decode-include-types-combined/parameters.txt b/integration/output-encode-decode-include-types-combined/parameters.txt new file mode 100644 index 000000000..bfc769774 --- /dev/null +++ b/integration/output-encode-decode-include-types-combined/parameters.txt @@ -0,0 +1 @@ +outputEncodeMethods=true,outputPartialMethods=false,outputJsonMethods=false,nestJs=false,outputEncodeIncludeTypes=^Request,outputDecodeIncludeTypes=^Response$ diff --git a/integration/output-encode-decode-include-types-combined/request_response.proto b/integration/output-encode-decode-include-types-combined/request_response.proto new file mode 100644 index 000000000..d31dd35cc --- /dev/null +++ b/integration/output-encode-decode-include-types-combined/request_response.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; + +import "google/protobuf/wrappers.proto"; + +message Request { + string request_id = 1; + + message Nested { + string more_data = 1; + } + + Nested nested = 2; +} + +message Response { + string response_id = 1; +} diff --git a/integration/output-encode-decode-include-types-combined/request_response.ts b/integration/output-encode-decode-include-types-combined/request_response.ts new file mode 100644 index 000000000..33466897c --- /dev/null +++ b/integration/output-encode-decode-include-types-combined/request_response.ts @@ -0,0 +1,88 @@ +// Code generated by protoc-gen-ts_proto. DO NOT EDIT. +// source: request_response.proto + +/* eslint-disable */ +import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"; + +export const protobufPackage = ""; + +export interface Request { + requestId: string; + nested: Request_Nested | undefined; +} + +export interface Request_Nested { + moreData: string; +} + +export interface Response { + responseId: string; +} + +export const Request: MessageFns = { + encode(message: Request, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.requestId !== "") { + writer.uint32(10).string(message.requestId); + } + if (message.nested !== undefined) { + Request_Nested.encode(message.nested, writer.uint32(18).fork()).join(); + } + return writer; + }, + + decode(_: BinaryReader | Uint8Array, length?: number): Request { + throw new Error("decode not generated for Request"); + }, +}; + +export const Request_Nested: MessageFns = { + encode(message: Request_Nested, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.moreData !== "") { + writer.uint32(10).string(message.moreData); + } + return writer; + }, + + decode(_: BinaryReader | Uint8Array, length?: number): Request_Nested { + throw new Error("decode not generated for Request_Nested"); + }, +}; + +function createBaseResponse(): Response { + return { responseId: "" }; +} + +export const Response: MessageFns = { + encode(_: Response, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + throw new Error("encode not generated for Response"); + }, + + decode(input: BinaryReader | Uint8Array, length?: number): Response { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.responseId = reader.string(); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, +}; + +export interface MessageFns { + encode(message: T, writer?: BinaryWriter): BinaryWriter; + decode(input: BinaryReader | Uint8Array, length?: number): T; +} diff --git a/integration/output-encode-include-types/encode.proto b/integration/output-encode-include-types/encode.proto new file mode 100644 index 000000000..7e4909a97 --- /dev/null +++ b/integration/output-encode-include-types/encode.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; + +import "google/protobuf/wrappers.proto"; + +message Encode { + string encode = 1; +} + +message DontGenerateEncode { + string dont_generate_encode = 1; +} diff --git a/integration/output-encode-include-types/encode.ts b/integration/output-encode-include-types/encode.ts new file mode 100644 index 000000000..5cfd950e4 --- /dev/null +++ b/integration/output-encode-include-types/encode.ts @@ -0,0 +1,28 @@ +// Code generated by protoc-gen-ts_proto. DO NOT EDIT. +// source: encode.proto + +/* eslint-disable */ +import { BinaryWriter } from "@bufbuild/protobuf/wire"; + +export const protobufPackage = ""; + +export interface Encode { + encode: string; +} + +export interface DontGenerateEncode { + dontGenerateEncode: string; +} + +export const Encode: MessageFns = { + encode(message: Encode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.encode !== "") { + writer.uint32(10).string(message.encode); + } + return writer; + }, +}; + +export interface MessageFns { + encode(message: T, writer?: BinaryWriter): BinaryWriter; +} diff --git a/integration/output-encode-include-types/parameters.txt b/integration/output-encode-include-types/parameters.txt new file mode 100644 index 000000000..dfcbb45bc --- /dev/null +++ b/integration/output-encode-include-types/parameters.txt @@ -0,0 +1 @@ +outputEncodeMethods=encode-only,outputPartialMethods=false,outputJsonMethods=false,nestJs=false,outputEncodeIncludeTypes=^Encode$ diff --git a/integration/output-encode-only/encode.ts b/integration/output-encode-only/encode.ts index 76c271b10..c5127560f 100644 --- a/integration/output-encode-only/encode.ts +++ b/integration/output-encode-only/encode.ts @@ -10,10 +10,6 @@ export interface Encode { encode: string; } -function createBaseEncode(): Encode { - return { encode: "" }; -} - export const Encode: MessageFns = { encode(message: Encode, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { if (message.encode !== "") { diff --git a/integration/output-encode-only/google/protobuf/wrappers.ts b/integration/output-encode-only/google/protobuf/wrappers.ts index 06356c050..033edf610 100644 --- a/integration/output-encode-only/google/protobuf/wrappers.ts +++ b/integration/output-encode-only/google/protobuf/wrappers.ts @@ -96,10 +96,6 @@ export interface BytesValue { value: Uint8Array; } -function createBaseDoubleValue(): DoubleValue { - return { value: 0 }; -} - export const DoubleValue: MessageFns = { encode(message: DoubleValue, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { if (message.value !== 0) { @@ -109,10 +105,6 @@ export const DoubleValue: MessageFns = { }, }; -function createBaseFloatValue(): FloatValue { - return { value: 0 }; -} - export const FloatValue: MessageFns = { encode(message: FloatValue, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { if (message.value !== 0) { @@ -122,10 +114,6 @@ export const FloatValue: MessageFns = { }, }; -function createBaseInt64Value(): Int64Value { - return { value: 0 }; -} - export const Int64Value: MessageFns = { encode(message: Int64Value, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { if (message.value !== 0) { @@ -135,10 +123,6 @@ export const Int64Value: MessageFns = { }, }; -function createBaseUInt64Value(): UInt64Value { - return { value: 0 }; -} - export const UInt64Value: MessageFns = { encode(message: UInt64Value, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { if (message.value !== 0) { @@ -148,10 +132,6 @@ export const UInt64Value: MessageFns = { }, }; -function createBaseInt32Value(): Int32Value { - return { value: 0 }; -} - export const Int32Value: MessageFns = { encode(message: Int32Value, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { if (message.value !== 0) { @@ -161,10 +141,6 @@ export const Int32Value: MessageFns = { }, }; -function createBaseUInt32Value(): UInt32Value { - return { value: 0 }; -} - export const UInt32Value: MessageFns = { encode(message: UInt32Value, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { if (message.value !== 0) { @@ -174,10 +150,6 @@ export const UInt32Value: MessageFns = { }, }; -function createBaseBoolValue(): BoolValue { - return { value: false }; -} - export const BoolValue: MessageFns = { encode(message: BoolValue, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { if (message.value !== false) { @@ -187,10 +159,6 @@ export const BoolValue: MessageFns = { }, }; -function createBaseStringValue(): StringValue { - return { value: "" }; -} - export const StringValue: MessageFns = { encode(message: StringValue, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { if (message.value !== "") { @@ -200,10 +168,6 @@ export const StringValue: MessageFns = { }, }; -function createBaseBytesValue(): BytesValue { - return { value: new Uint8Array(0) }; -} - export const BytesValue: MessageFns = { encode(message: BytesValue, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { if (message.value.length !== 0) { diff --git a/src/main.ts b/src/main.ts index 4fdc12157..35b133d22 100644 --- a/src/main.ts +++ b/src/main.ts @@ -210,7 +210,11 @@ export function generateFile(ctx: Context, fileDesc: FileDescriptorProto): [stri // Only decode, fromPartial, and wrap use the createBase method if ( - (options.outputEncodeMethods && options.outputEncodeMethods !== "encode-no-creation") || + (options.outputEncodeMethods && + options.outputEncodeMethods !== "encode-no-creation" && + options.outputEncodeMethods !== "encode-only" && + (options.outputDecodeIncludeTypes === "" || + new RegExp(options.outputDecodeIncludeTypes).test(fullTypeName))) || options.outputPartialMethods || outputWrapAndUnwrap ) { @@ -238,20 +242,36 @@ export function generateFile(ctx: Context, fileDesc: FileDescriptorProto): [stri options.outputEncodeMethods === "encode-only" || options.outputEncodeMethods === "encode-no-creation" ) { - staticMembers.push(generateEncode(ctx, fullName, message)); - - if (options.outputExtensions && options.unknownFields && message.extensionRange.length) { - staticMembers.push(generateSetExtension(ctx, fullName)); + if ( + options.outputEncodeIncludeTypes === "" || + new RegExp(options.outputEncodeIncludeTypes).test(fullTypeName) + ) { + staticMembers.push(generateEncode(ctx, fullName, message)); + + if (options.outputExtensions && options.unknownFields && message.extensionRange.length) { + staticMembers.push(generateSetExtension(ctx, fullName)); + } + } else if (options.outputEncodeMethods === true) { + staticMembers.push(generateEmptyEncode(fullName)); } } - if (options.outputEncodeMethods === true || options.outputEncodeMethods === "decode-only") { - staticMembers.push(generateDecode(ctx, fullName, message)); - if (options.outputExtensions && options.unknownFields && message.extensionRange.length) { - staticMembers.push(generateGetExtension(ctx, fullName)); + if (options.outputEncodeMethods === true || options.outputEncodeMethods === "decode-only") { + if ( + options.outputDecodeIncludeTypes === "" || + new RegExp(options.outputDecodeIncludeTypes).test(fullTypeName) + ) { + staticMembers.push(generateDecode(ctx, fullName, message)); + + if (options.outputExtensions && options.unknownFields && message.extensionRange.length) { + staticMembers.push(generateGetExtension(ctx, fullName)); + } + } else if (options.outputEncodeMethods === true) { + staticMembers.push(generateEmptyDecode(fullName)); } } } + if (options.useAsyncIterable) { staticMembers.push(generateEncodeTransform(ctx.utils, fullName)); staticMembers.push(generateDecodeTransform(ctx.utils, fullName)); @@ -1350,6 +1370,19 @@ function getDecodeReadSnippet(ctx: Context, field: FieldDescriptorProto) { return readSnippet; } +function generateEmptyDecode(fullName: string): Code { + const BinaryReader = imp("BinaryReader@@bufbuild/protobuf/wire"); + + return code` + decode( + _: ${BinaryReader} | Uint8Array, + length?: number, + ): ${fullName} { + throw new Error('decode not generated for ${fullName}'); + } + `; +} + /** Creates a function to decode a message by loop overing the tags. */ function generateDecode(ctx: Context, fullName: string, messageDesc: DescriptorProto): Code { const { options, currentFile } = ctx; @@ -1632,6 +1665,19 @@ function getEncodeWriteSnippet(ctx: Context, field: FieldDescriptorProto): (plac } } +function generateEmptyEncode(fullName: string): Code { + const BinaryWriter = imp("BinaryWriter@@bufbuild/protobuf/wire"); + + return code` + encode( + _: ${fullName}, + writer: ${BinaryWriter} = new ${BinaryWriter}(), + ): ${BinaryWriter} { + throw new Error('encode not generated for ${fullName}'); + } + `; +} + /** Creates a function to encode a message by loop overing the tags. */ function generateEncode(ctx: Context, fullName: string, messageDesc: DescriptorProto): Code { const { options, utils, typeMap, currentFile } = ctx; diff --git a/src/options.ts b/src/options.ts index fd687c7cf..ac6bfb85e 100644 --- a/src/options.ts +++ b/src/options.ts @@ -61,6 +61,8 @@ export type Options = { fileSuffix: string; importSuffix: string; outputEncodeMethods: true | false | "encode-only" | "decode-only" | "encode-no-creation"; + outputEncodeIncludeTypes: string; + outputDecodeIncludeTypes: string; outputJsonMethods: true | false | "to-only" | "from-only"; outputPartialMethods: boolean; outputTypeAnnotations: boolean | "static-only" | "optional"; @@ -132,6 +134,8 @@ export function defaultOptions(): Options { importSuffix: "", lowerCaseServiceMethods: false, outputEncodeMethods: true, + outputEncodeIncludeTypes: "", + outputDecodeIncludeTypes: "", outputJsonMethods: true, outputPartialMethods: true, outputTypeAnnotations: false, diff --git a/tests/options-test.ts b/tests/options-test.ts index e30b4a373..5eea15dd1 100644 --- a/tests/options-test.ts +++ b/tests/options-test.ts @@ -32,6 +32,8 @@ describe("options", () => { "oneof": "properties", "onlyTypes": false, "outputClientImpl": false, + "outputDecodeIncludeTypes": "", + "outputEncodeIncludeTypes": "", "outputEncodeMethods": false, "outputExtensions": false, "outputIndex": false,