Skip to content

Commit 04b14eb

Browse files
authored
Merge pull request #2183 from install/template
proto-loader-gen-types Typename templates
2 parents c023d05 + d81ec8e commit 04b14eb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+452
-422
lines changed

packages/proto-loader/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ Options:
8888
-O, --outDir Directory in which to output files [string] [required]
8989
--grpcLib The gRPC implementation library that these types will
9090
be used with [string] [required]
91+
--inputTemplate Template for mapping input or "permissive" type names
92+
[string] [default: "%s"]
93+
--outputTemplate Template for mapping output or "restricted" type names
94+
[string] [default: "%s__Output"]
9195
```
9296

9397
### Example Usage

packages/proto-loader/bin/proto-loader-gen-types.ts

Lines changed: 55 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,25 @@ import * as yargs from 'yargs';
2626
import camelCase = require('lodash.camelcase');
2727
import { loadProtosWithOptions, addCommonProtos } from '../src/util';
2828

29+
const templateStr = "%s";
30+
const useNameFmter = ({outputTemplate, inputTemplate}: GeneratorOptions) => {
31+
if (outputTemplate === inputTemplate) {
32+
throw new Error('inputTemplate and outputTemplate must differ')
33+
}
34+
return {
35+
outputName: (n: string) => outputTemplate.replace(templateStr, n),
36+
inputName: (n: string) => inputTemplate.replace(templateStr, n)
37+
};
38+
}
39+
2940
type GeneratorOptions = Protobuf.IParseOptions & Protobuf.IConversionOptions & {
3041
includeDirs?: string[];
3142
grpcLib: string;
3243
outDir: string;
3344
verbose?: boolean;
3445
includeComments?: boolean;
46+
inputTemplate: string;
47+
outputTemplate: string;
3548
}
3649

3750
class TextFormatter {
@@ -114,15 +127,16 @@ function getTypeInterfaceName(type: Protobuf.Type | Protobuf.Enum | Protobuf.Ser
114127
return type.fullName.replace(/\./g, '_');
115128
}
116129

117-
function getImportLine(dependency: Protobuf.Type | Protobuf.Enum | Protobuf.Service, from?: Protobuf.Type | Protobuf.Service) {
130+
function getImportLine(dependency: Protobuf.Type | Protobuf.Enum | Protobuf.Service, from: Protobuf.Type | Protobuf.Service | undefined, options: GeneratorOptions) {
118131
const filePath = from === undefined ? './' + getImportPath(dependency) : getRelativeImportPath(from, dependency);
132+
const {outputName, inputName} = useNameFmter(options);
119133
const typeInterfaceName = getTypeInterfaceName(dependency);
120134
let importedTypes: string;
121135
/* If the dependency is defined within a message, it will be generated in that
122136
* message's file and exported using its typeInterfaceName. */
123137
if (dependency.parent instanceof Protobuf.Type) {
124138
if (dependency instanceof Protobuf.Type) {
125-
importedTypes = `${typeInterfaceName}, ${typeInterfaceName}__Output`;
139+
importedTypes = `${inputName(typeInterfaceName)}, ${outputName(typeInterfaceName)}`;
126140
} else if (dependency instanceof Protobuf.Enum) {
127141
importedTypes = `${typeInterfaceName}`;
128142
} else if (dependency instanceof Protobuf.Service) {
@@ -132,7 +146,7 @@ function getImportLine(dependency: Protobuf.Type | Protobuf.Enum | Protobuf.Serv
132146
}
133147
} else {
134148
if (dependency instanceof Protobuf.Type) {
135-
importedTypes = `${dependency.name} as ${typeInterfaceName}, ${dependency.name}__Output as ${typeInterfaceName}__Output`;
149+
importedTypes = `${inputName(dependency.name)} as ${inputName(typeInterfaceName)}, ${outputName(dependency.name)} as ${outputName(typeInterfaceName)}`;
136150
} else if (dependency instanceof Protobuf.Enum) {
137151
importedTypes = `${dependency.name} as ${typeInterfaceName}`;
138152
} else if (dependency instanceof Protobuf.Service) {
@@ -170,7 +184,8 @@ function formatComment(formatter: TextFormatter, comment?: string | null) {
170184

171185
// GENERATOR FUNCTIONS
172186

173-
function getTypeNamePermissive(fieldType: string, resolvedType: Protobuf.Type | Protobuf.Enum | null, repeated: boolean, map: boolean): string {
187+
function getTypeNamePermissive(fieldType: string, resolvedType: Protobuf.Type | Protobuf.Enum | null, repeated: boolean, map: boolean, options: GeneratorOptions): string {
188+
const {inputName} = useNameFmter(options);
174189
switch (fieldType) {
175190
case 'double':
176191
case 'float':
@@ -200,18 +215,18 @@ function getTypeNamePermissive(fieldType: string, resolvedType: Protobuf.Type |
200215
const typeInterfaceName = getTypeInterfaceName(resolvedType);
201216
if (resolvedType instanceof Protobuf.Type) {
202217
if (repeated || map) {
203-
return typeInterfaceName;
218+
return inputName(typeInterfaceName);
204219
} else {
205-
return `${typeInterfaceName} | null`;
220+
return `${inputName(typeInterfaceName)} | null`;
206221
}
207222
} else {
208223
return `${typeInterfaceName} | keyof typeof ${typeInterfaceName}`;
209224
}
210225
}
211226
}
212227

213-
function getFieldTypePermissive(field: Protobuf.FieldBase): string {
214-
const valueType = getTypeNamePermissive(field.type, field.resolvedType, field.repeated, field.map);
228+
function getFieldTypePermissive(field: Protobuf.FieldBase, options: GeneratorOptions): string {
229+
const valueType = getTypeNamePermissive(field.type, field.resolvedType, field.repeated, field.map, options);
215230
if (field instanceof Protobuf.MapField) {
216231
const keyType = field.keyType === 'string' ? 'string' : 'number';
217232
return `{[key: ${keyType}]: ${valueType}}`;
@@ -221,23 +236,24 @@ function getFieldTypePermissive(field: Protobuf.FieldBase): string {
221236
}
222237

223238
function generatePermissiveMessageInterface(formatter: TextFormatter, messageType: Protobuf.Type, options: GeneratorOptions, nameOverride?: string) {
239+
const {inputName} = useNameFmter(options);
224240
if (options.includeComments) {
225241
formatComment(formatter, messageType.comment);
226242
}
227243
if (messageType.fullName === '.google.protobuf.Any') {
228244
/* This describes the behavior of the Protobuf.js Any wrapper fromObject
229245
* replacement function */
230-
formatter.writeLine('export type Any = AnyExtension | {');
246+
formatter.writeLine(`export type ${inputName('Any')} = AnyExtension | {`);
231247
formatter.writeLine(' type_url: string;');
232248
formatter.writeLine(' value: Buffer | Uint8Array | string;');
233249
formatter.writeLine('}');
234250
return;
235251
}
236-
formatter.writeLine(`export interface ${nameOverride ?? messageType.name} {`);
252+
formatter.writeLine(`export interface ${inputName(nameOverride ?? messageType.name)} {`);
237253
formatter.indent();
238254
for (const field of messageType.fieldsArray) {
239255
const repeatedString = field.repeated ? '[]' : '';
240-
const type: string = getFieldTypePermissive(field);
256+
const type: string = getFieldTypePermissive(field, options);
241257
if (options.includeComments) {
242258
formatComment(formatter, field.comment);
243259
}
@@ -255,6 +271,7 @@ function generatePermissiveMessageInterface(formatter: TextFormatter, messageTyp
255271
}
256272

257273
function getTypeNameRestricted(fieldType: string, resolvedType: Protobuf.Type | Protobuf.Enum | null, repeated: boolean, map: boolean, options: GeneratorOptions): string {
274+
const {outputName} = useNameFmter(options);
258275
switch (fieldType) {
259276
case 'double':
260277
case 'float':
@@ -302,9 +319,9 @@ function getTypeNameRestricted(fieldType: string, resolvedType: Protobuf.Type |
302319
/* null is only used to represent absent message values if the defaults
303320
* option is set, and only for non-repeated, non-map fields. */
304321
if (options.defaults && !repeated && !map) {
305-
return `${typeInterfaceName}__Output | null`;
322+
return `${outputName(typeInterfaceName)} | null`;
306323
} else {
307-
return `${typeInterfaceName}__Output`;
324+
return `${outputName(typeInterfaceName)}`;
308325
}
309326
} else {
310327
if (options.enums == String) {
@@ -327,20 +344,21 @@ function getFieldTypeRestricted(field: Protobuf.FieldBase, options: GeneratorOpt
327344
}
328345
329346
function generateRestrictedMessageInterface(formatter: TextFormatter, messageType: Protobuf.Type, options: GeneratorOptions, nameOverride?: string) {
347+
const {outputName} = useNameFmter(options);
330348
if (options.includeComments) {
331349
formatComment(formatter, messageType.comment);
332350
}
333351
if (messageType.fullName === '.google.protobuf.Any' && options.json) {
334352
/* This describes the behavior of the Protobuf.js Any wrapper toObject
335353
* replacement function */
336354
let optionalString = options.defaults ? '' : '?';
337-
formatter.writeLine('export type Any__Output = AnyExtension | {');
355+
formatter.writeLine(`export type ${outputName('Any')} = AnyExtension | {`);
338356
formatter.writeLine(` type_url${optionalString}: string;`);
339357
formatter.writeLine(` value${optionalString}: ${getTypeNameRestricted('bytes', null, false, false, options)};`);
340358
formatter.writeLine('}');
341359
return;
342360
}
343-
formatter.writeLine(`export interface ${nameOverride ?? messageType.name}__Output {`);
361+
formatter.writeLine(`export interface ${outputName(nameOverride ?? messageType.name)} {`);
344362
formatter.indent();
345363
for (const field of messageType.fieldsArray) {
346364
let fieldGuaranteed: boolean;
@@ -389,7 +407,7 @@ function generateMessageInterfaces(formatter: TextFormatter, messageType: Protob
389407
continue;
390408
}
391409
seenDeps.add(dependency.fullName);
392-
formatter.writeLine(getImportLine(dependency, messageType));
410+
formatter.writeLine(getImportLine(dependency, messageType, options));
393411
}
394412
if (field.type.indexOf('64') >= 0) {
395413
usesLong = true;
@@ -404,7 +422,7 @@ function generateMessageInterfaces(formatter: TextFormatter, messageType: Protob
404422
continue;
405423
}
406424
seenDeps.add(dependency.fullName);
407-
formatter.writeLine(getImportLine(dependency, messageType));
425+
formatter.writeLine(getImportLine(dependency, messageType, options));
408426
}
409427
if (field.type.indexOf('64') >= 0) {
410428
usesLong = true;
@@ -487,6 +505,7 @@ const CLIENT_RESERVED_METHOD_NAMES = new Set([
487505
]);
488506

489507
function generateServiceClientInterface(formatter: TextFormatter, serviceType: Protobuf.Service, options: GeneratorOptions) {
508+
const {outputName, inputName} = useNameFmter(options);
490509
if (options.includeComments) {
491510
formatComment(formatter, serviceType.comment);
492511
}
@@ -501,8 +520,8 @@ function generateServiceClientInterface(formatter: TextFormatter, serviceType: P
501520
if (options.includeComments) {
502521
formatComment(formatter, method.comment);
503522
}
504-
const requestType = getTypeInterfaceName(method.resolvedRequestType!);
505-
const responseType = getTypeInterfaceName(method.resolvedResponseType!) + '__Output';
523+
const requestType = inputName(getTypeInterfaceName(method.resolvedRequestType!));
524+
const responseType = outputName(getTypeInterfaceName(method.resolvedResponseType!));
506525
const callbackType = `grpc.requestCallback<${responseType}>`;
507526
if (method.requestStream) {
508527
if (method.responseStream) {
@@ -541,6 +560,7 @@ function generateServiceClientInterface(formatter: TextFormatter, serviceType: P
541560
}
542561

543562
function generateServiceHandlerInterface(formatter: TextFormatter, serviceType: Protobuf.Service, options: GeneratorOptions) {
563+
const {inputName, outputName} = useNameFmter(options);
544564
if (options.includeComments) {
545565
formatComment(formatter, serviceType.comment);
546566
}
@@ -551,8 +571,8 @@ function generateServiceHandlerInterface(formatter: TextFormatter, serviceType:
551571
if (options.includeComments) {
552572
formatComment(formatter, method.comment);
553573
}
554-
const requestType = getTypeInterfaceName(method.resolvedRequestType!) + '__Output';
555-
const responseType = getTypeInterfaceName(method.resolvedResponseType!);
574+
const requestType = outputName(getTypeInterfaceName(method.resolvedRequestType!));
575+
const responseType = inputName(getTypeInterfaceName(method.resolvedResponseType!));
556576
if (method.requestStream) {
557577
if (method.responseStream) {
558578
// Bidi streaming
@@ -576,14 +596,15 @@ function generateServiceHandlerInterface(formatter: TextFormatter, serviceType:
576596
formatter.writeLine('}');
577597
}
578598

579-
function generateServiceDefinitionInterface(formatter: TextFormatter, serviceType: Protobuf.Service) {
599+
function generateServiceDefinitionInterface(formatter: TextFormatter, serviceType: Protobuf.Service, options: GeneratorOptions) {
600+
const {inputName, outputName} = useNameFmter(options);
580601
formatter.writeLine(`export interface ${serviceType.name}Definition extends grpc.ServiceDefinition {`);
581602
formatter.indent();
582603
for (const methodName of Object.keys(serviceType.methods).sort()) {
583604
const method = serviceType.methods[methodName];
584605
const requestType = getTypeInterfaceName(method.resolvedRequestType!);
585606
const responseType = getTypeInterfaceName(method.resolvedResponseType!);
586-
formatter.writeLine(`${methodName}: MethodDefinition<${requestType}, ${responseType}, ${requestType}__Output, ${responseType}__Output>`);
607+
formatter.writeLine(`${methodName}: MethodDefinition<${inputName(requestType)}, ${inputName(responseType)}, ${outputName(requestType)}, ${outputName(responseType)}>`);
587608
}
588609
formatter.unindent();
589610
formatter.writeLine('}')
@@ -601,7 +622,7 @@ function generateServiceInterfaces(formatter: TextFormatter, serviceType: Protob
601622
dependencies.add(method.resolvedResponseType!);
602623
}
603624
for (const dep of Array.from(dependencies.values()).sort(compareName)) {
604-
formatter.writeLine(getImportLine(dep, serviceType));
625+
formatter.writeLine(getImportLine(dep, serviceType, options));
605626
}
606627
formatter.writeLine('');
607628

@@ -611,7 +632,7 @@ function generateServiceInterfaces(formatter: TextFormatter, serviceType: Protob
611632
generateServiceHandlerInterface(formatter, serviceType, options);
612633
formatter.writeLine('');
613634

614-
generateServiceDefinitionInterface(formatter, serviceType);
635+
generateServiceDefinitionInterface(formatter, serviceType, options);
615636
}
616637

617638
function containsDefinition(definitionType: typeof Protobuf.Type | typeof Protobuf.Enum, namespace: Protobuf.NamespaceBase): boolean {
@@ -645,7 +666,7 @@ function generateDefinitionImports(formatter: TextFormatter, namespace: Protobuf
645666
function generateServiceImports(formatter: TextFormatter, namespace: Protobuf.NamespaceBase, options: GeneratorOptions) {
646667
for (const nested of namespace.nestedArray.sort(compareName)) {
647668
if (nested instanceof Protobuf.Service) {
648-
formatter.writeLine(getImportLine(nested));
669+
formatter.writeLine(getImportLine(nested, undefined, options));
649670
} else if (isNamespaceBase(nested) && !(nested instanceof Protobuf.Type) && !(nested instanceof Protobuf.Enum)) {
650671
generateServiceImports(formatter, nested, options);
651672
}
@@ -776,7 +797,7 @@ async function runScript() {
776797
.normalize(['includeDirs', 'outDir'])
777798
.array('includeDirs')
778799
.boolean(['keepCase', 'defaults', 'arrays', 'objects', 'oneofs', 'json', 'verbose', 'includeComments'])
779-
.string(['longs', 'enums', 'bytes'])
800+
.string(['longs', 'enums', 'bytes', 'inputTemplate', 'outputTemplate'])
780801
.default('keepCase', false)
781802
.default('defaults', false)
782803
.default('arrays', false)
@@ -787,6 +808,8 @@ async function runScript() {
787808
.default('longs', 'Long')
788809
.default('enums', 'number')
789810
.default('bytes', 'Buffer')
811+
.default('inputTemplate', `${templateStr}`)
812+
.default('outputTemplate', `${templateStr}__Output`)
790813
.coerce('longs', value => {
791814
switch (value) {
792815
case 'String': return String;
@@ -805,7 +828,8 @@ async function runScript() {
805828
case 'String': return String;
806829
default: return undefined;
807830
}
808-
}).alias({
831+
})
832+
.alias({
809833
includeDirs: 'I',
810834
outDir: 'O',
811835
verbose: 'v'
@@ -822,7 +846,9 @@ async function runScript() {
822846
includeComments: 'Generate doc comments from comments in the original files',
823847
includeDirs: 'Directories to search for included files',
824848
outDir: 'Directory in which to output files',
825-
grpcLib: 'The gRPC implementation library that these types will be used with'
849+
grpcLib: 'The gRPC implementation library that these types will be used with',
850+
inputTemplate: 'Template for mapping input or "permissive" type names',
851+
outputTemplate: 'Template for mapping output or "restricted" type names',
826852
}).demandOption(['outDir', 'grpcLib'])
827853
.demand(1)
828854
.usage('$0 [options] filenames...')

packages/proto-loader/golden-generated/google/api/CustomHttpPattern.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
/**
55
* A custom pattern is used for defining custom HTTP verb.
66
*/
7-
export interface CustomHttpPattern {
7+
export interface ICustomHttpPattern {
88
/**
99
* The name of this custom HTTP verb.
1010
*/
@@ -18,7 +18,7 @@ export interface CustomHttpPattern {
1818
/**
1919
* A custom pattern is used for defining custom HTTP verb.
2020
*/
21-
export interface CustomHttpPattern__Output {
21+
export interface OCustomHttpPattern {
2222
/**
2323
* The name of this custom HTTP verb.
2424
*/

packages/proto-loader/golden-generated/google/api/Http.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
// Original file: deps/googleapis/google/api/http.proto
22

3-
import type { HttpRule as _google_api_HttpRule, HttpRule__Output as _google_api_HttpRule__Output } from '../../google/api/HttpRule';
3+
import type { IHttpRule as I_google_api_HttpRule, OHttpRule as O_google_api_HttpRule } from '../../google/api/HttpRule';
44

55
/**
66
* Defines the HTTP configuration for an API service. It contains a list of
77
* [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method
88
* to one or more HTTP REST API methods.
99
*/
10-
export interface Http {
10+
export interface IHttp {
1111
/**
1212
* A list of HTTP configuration rules that apply to individual API methods.
1313
*
1414
* **NOTE:** All service configuration rules follow "last one wins" order.
1515
*/
16-
'rules'?: (_google_api_HttpRule)[];
16+
'rules'?: (I_google_api_HttpRule)[];
1717
/**
1818
* When set to true, URL path parameters will be fully URI-decoded except in
1919
* cases of single segment matches in reserved expansion, where "%2F" will be
@@ -30,13 +30,13 @@ export interface Http {
3030
* [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method
3131
* to one or more HTTP REST API methods.
3232
*/
33-
export interface Http__Output {
33+
export interface OHttp {
3434
/**
3535
* A list of HTTP configuration rules that apply to individual API methods.
3636
*
3737
* **NOTE:** All service configuration rules follow "last one wins" order.
3838
*/
39-
'rules': (_google_api_HttpRule__Output)[];
39+
'rules': (O_google_api_HttpRule)[];
4040
/**
4141
* When set to true, URL path parameters will be fully URI-decoded except in
4242
* cases of single segment matches in reserved expansion, where "%2F" will be

0 commit comments

Comments
 (0)