From ab515cda322baeb94c7588117e4bb5bee6281874 Mon Sep 17 00:00:00 2001 From: Oliver Yasuna Date: Mon, 1 Jul 2024 10:25:59 -0400 Subject: [PATCH] feat: added the "typePrefix" and "typeSuffix" options. (#1069) Fixes #1033 --- README.markdown | 4 + integration/affixes/affixes.proto | 17 +++ integration/affixes/affixes.ts | 171 +++++++++++++++++++++++++++++ integration/affixes/parameters.txt | 1 + src/options.ts | 4 + src/visit.ts | 6 +- tests/options-test.ts | 2 + 7 files changed, 203 insertions(+), 2 deletions(-) create mode 100644 integration/affixes/affixes.proto create mode 100644 integration/affixes/affixes.ts create mode 100644 integration/affixes/parameters.txt diff --git a/README.markdown b/README.markdown index b866a8b63..62f95452a 100644 --- a/README.markdown +++ b/README.markdown @@ -555,6 +555,10 @@ Generated code will be placed in the Gradle build directory. - With `--ts_proto_opt=useNullAsOptional=true`, `undefined` values will be converted to `null`, and if you use `optional` label in your `.proto` file, the field will have `undefined` type as well. for example: +- With `--ts_proto_opt=typePrefix=MyPrefix`, the generated interfaces, enums, and factories will have a prefix of `MyPrefix` in their names. + +- With `--ts_proto_opt=typeSuffix=MySuffix`, the generated interfaces, enums, and factories will have a suffix of `MySuffix` in their names. + ```protobuf message ProfileInfo { int32 id = 1; diff --git a/integration/affixes/affixes.proto b/integration/affixes/affixes.proto new file mode 100644 index 000000000..0b40d8720 --- /dev/null +++ b/integration/affixes/affixes.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; + +package affixes; + +message AwesomeMessage { + message Inner { + } +} + +enum AwesomeEnum { + AWESOME_ENUM_AWESOME = 0; + AWESOME_ENUM_COOL = 1; + AWESOME_ENUM_JUST_OKAY = 2; +} + +service AwesomeService { +} diff --git a/integration/affixes/affixes.ts b/integration/affixes/affixes.ts new file mode 100644 index 000000000..0fcc20210 --- /dev/null +++ b/integration/affixes/affixes.ts @@ -0,0 +1,171 @@ +// Code generated by protoc-gen-ts_proto. DO NOT EDIT. +// source: affixes.proto + +/* eslint-disable */ +import * as _m0 from "protobufjs/minimal"; + +export const protobufPackage = "affixes"; + +export enum PrefixAwesomeEnumSuffix { + AWESOME_ENUM_AWESOME = 0, + AWESOME_ENUM_COOL = 1, + AWESOME_ENUM_JUST_OKAY = 2, + UNRECOGNIZED = -1, +} + +export function prefixAwesomeEnumSuffixFromJSON(object: any): PrefixAwesomeEnumSuffix { + switch (object) { + case 0: + case "AWESOME_ENUM_AWESOME": + return PrefixAwesomeEnumSuffix.AWESOME_ENUM_AWESOME; + case 1: + case "AWESOME_ENUM_COOL": + return PrefixAwesomeEnumSuffix.AWESOME_ENUM_COOL; + case 2: + case "AWESOME_ENUM_JUST_OKAY": + return PrefixAwesomeEnumSuffix.AWESOME_ENUM_JUST_OKAY; + case -1: + case "UNRECOGNIZED": + default: + return PrefixAwesomeEnumSuffix.UNRECOGNIZED; + } +} + +export function prefixAwesomeEnumSuffixToJSON(object: PrefixAwesomeEnumSuffix): string { + switch (object) { + case PrefixAwesomeEnumSuffix.AWESOME_ENUM_AWESOME: + return "AWESOME_ENUM_AWESOME"; + case PrefixAwesomeEnumSuffix.AWESOME_ENUM_COOL: + return "AWESOME_ENUM_COOL"; + case PrefixAwesomeEnumSuffix.AWESOME_ENUM_JUST_OKAY: + return "AWESOME_ENUM_JUST_OKAY"; + case PrefixAwesomeEnumSuffix.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} + +export interface PrefixAwesomeMessageSuffix { +} + +export interface PrefixAwesomeMessage_InnerSuffix { +} + +function createBasePrefixAwesomeMessageSuffix(): PrefixAwesomeMessageSuffix { + return {}; +} + +export const PrefixAwesomeMessageSuffix = { + encode(_: PrefixAwesomeMessageSuffix, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): PrefixAwesomeMessageSuffix { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBasePrefixAwesomeMessageSuffix(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(_: any): PrefixAwesomeMessageSuffix { + return {}; + }, + + toJSON(_: PrefixAwesomeMessageSuffix): unknown { + const obj: any = {}; + return obj; + }, + + create, I>>(base?: I): PrefixAwesomeMessageSuffix { + return PrefixAwesomeMessageSuffix.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(_: I): PrefixAwesomeMessageSuffix { + const message = createBasePrefixAwesomeMessageSuffix(); + return message; + }, +}; + +function createBasePrefixAwesomeMessage_InnerSuffix(): PrefixAwesomeMessage_InnerSuffix { + return {}; +} + +export const PrefixAwesomeMessage_InnerSuffix = { + encode(_: PrefixAwesomeMessage_InnerSuffix, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): PrefixAwesomeMessage_InnerSuffix { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBasePrefixAwesomeMessage_InnerSuffix(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(_: any): PrefixAwesomeMessage_InnerSuffix { + return {}; + }, + + toJSON(_: PrefixAwesomeMessage_InnerSuffix): unknown { + const obj: any = {}; + return obj; + }, + + create, I>>( + base?: I, + ): PrefixAwesomeMessage_InnerSuffix { + return PrefixAwesomeMessage_InnerSuffix.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>( + _: I, + ): PrefixAwesomeMessage_InnerSuffix { + const message = createBasePrefixAwesomeMessage_InnerSuffix(); + return message; + }, +}; + +export interface AwesomeService { +} + +export const AwesomeServiceServiceName = "affixes.AwesomeService"; +export class AwesomeServiceClientImpl implements AwesomeService { + private readonly rpc: Rpc; + private readonly service: string; + constructor(rpc: Rpc, opts?: { service?: string }) { + this.service = opts?.service || AwesomeServiceServiceName; + this.rpc = rpc; + } +} + +interface Rpc { + request(service: string, method: string, data: Uint8Array): Promise; +} + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; diff --git a/integration/affixes/parameters.txt b/integration/affixes/parameters.txt new file mode 100644 index 000000000..23adf2d99 --- /dev/null +++ b/integration/affixes/parameters.txt @@ -0,0 +1 @@ +typePrefix=Prefix,typeSuffix=Suffix diff --git a/src/options.ts b/src/options.ts index 55f6d69b9..c98ffc1bd 100644 --- a/src/options.ts +++ b/src/options.ts @@ -104,6 +104,8 @@ export type Options = { annotateFilesWithVersion: boolean; noDefaultsForOptionals: boolean; bigIntLiteral: boolean; + typePrefix: string; + typeSuffix: string; }; export function defaultOptions(): Options { @@ -172,6 +174,8 @@ export function defaultOptions(): Options { annotateFilesWithVersion: true, noDefaultsForOptionals: false, bigIntLiteral: true, + typePrefix: "", + typeSuffix: "", }; } diff --git a/src/visit.ts b/src/visit.ts index 39e144f59..8ae055be6 100644 --- a/src/visit.ts +++ b/src/visit.ts @@ -39,8 +39,9 @@ export function visit( const protoFullName = protoPrefix + enumDesc.name; // I.e. FooBar_ZazInner const tsFullName = tsPrefix + maybeSnakeToCamel(enumDesc.name, options); + const tsFullNameWithAffixes = `${options.typePrefix}${tsFullName}${options.typeSuffix}`; const nestedSourceInfo = sourceInfo.open(childEnumType, index); - enumFn(tsFullName, enumDesc, nestedSourceInfo, protoFullName); + enumFn(tsFullNameWithAffixes, enumDesc, nestedSourceInfo, protoFullName); }); const messages = "messageType" in proto ? proto.messageType : proto.nestedType; @@ -51,8 +52,9 @@ export function visit( const protoFullName = protoPrefix + message.name; // I.e. FooBar_ZazInner const tsFullName = tsPrefix + maybeSnakeToCamel(messageName(message), options); + const tsFullNameWithAffixes = `${options.typePrefix}${tsFullName}${options.typeSuffix}`; const nestedSourceInfo = sourceInfo.open(childType, index); - messageFn(tsFullName, message, nestedSourceInfo, protoFullName); + messageFn(tsFullNameWithAffixes, message, nestedSourceInfo, protoFullName); const delim = options.useSnakeTypeName ? "_" : ""; visit(message, nestedSourceInfo, messageFn, options, enumFn, tsFullName + delim, protoFullName + "."); }); diff --git a/tests/options-test.ts b/tests/options-test.ts index f43cce537..e30b4a373 100644 --- a/tests/options-test.ts +++ b/tests/options-test.ts @@ -51,6 +51,8 @@ describe("options", () => { "keys", ], "stringEnums": false, + "typePrefix": "", + "typeSuffix": "", "unknownFields": false, "unrecognizedEnum": true, "unrecognizedEnumName": "UNRECOGNIZED",