From 783915f0571816d607c8ac370485257e27330f88 Mon Sep 17 00:00:00 2001 From: Katalina Okano Date: Sun, 11 Oct 2020 18:35:22 -0400 Subject: [PATCH] chore(ts-protoc-gen): prettier --- src/ts-protoc-gen/CodePrinter.ts | 44 +- src/ts-protoc-gen/ExportMap.ts | 143 ++-- src/ts-protoc-gen/Printer.ts | 42 +- src/ts-protoc-gen/WellKnown.ts | 28 +- src/ts-protoc-gen/index.ts | 97 +-- src/ts-protoc-gen/parameters.ts | 10 +- src/ts-protoc-gen/service/common.ts | 263 ++++--- src/ts-protoc-gen/service/grpcnode.ts | 221 +++--- src/ts-protoc-gen/service/grpcweb.ts | 877 +++++++++++++--------- src/ts-protoc-gen/ts/FieldTypes.ts | 99 +-- src/ts-protoc-gen/ts/enum.ts | 28 +- src/ts-protoc-gen/ts/extensions.ts | 36 +- src/ts-protoc-gen/ts/fileDescriptorTSD.ts | 81 +- src/ts-protoc-gen/ts/message.ts | 570 ++++++++------ src/ts-protoc-gen/ts/oneof.ts | 35 +- src/ts-protoc-gen/util.ts | 288 +++---- 16 files changed, 1660 insertions(+), 1202 deletions(-) diff --git a/src/ts-protoc-gen/CodePrinter.ts b/src/ts-protoc-gen/CodePrinter.ts index 7892e2a..aa3402e 100644 --- a/src/ts-protoc-gen/CodePrinter.ts +++ b/src/ts-protoc-gen/CodePrinter.ts @@ -1,26 +1,26 @@ -import {Printer} from "./Printer"; -import {generateIndent} from "./util"; +import { Printer } from './Printer'; +import { generateIndent } from './util'; export class CodePrinter { - private indentation: string; - constructor(private depth: number, private printer: Printer) { - this.indentation = generateIndent(1); - } - indent() { - this.depth++; - return this; - } - dedent() { - this.depth--; - return this; - } - printLn(line: string) { - this.printer.printLn(new Array(this.depth + 1).join(this.indentation) + line); - return this; - } + private indentation: string; + constructor(private depth: number, private printer: Printer) { + this.indentation = generateIndent(1); + } + indent() { + this.depth++; + return this; + } + dedent() { + this.depth--; + return this; + } + printLn(line: string) { + this.printer.printLn(new Array(this.depth + 1).join(this.indentation) + line); + return this; + } - printEmptyLn() { - this.printer.printEmptyLn(); - return this; - } + printEmptyLn() { + this.printer.printEmptyLn(); + return this; + } } diff --git a/src/ts-protoc-gen/ExportMap.ts b/src/ts-protoc-gen/ExportMap.ts index c8410b3..c54a14e 100644 --- a/src/ts-protoc-gen/ExportMap.ts +++ b/src/ts-protoc-gen/ExportMap.ts @@ -1,84 +1,101 @@ import { - FileDescriptorProto, - DescriptorProto, - MessageOptions, - EnumOptions, - FieldDescriptorProto -} from "google-protobuf/google/protobuf/descriptor_pb"; + FileDescriptorProto, + DescriptorProto, + MessageOptions, + EnumOptions, + FieldDescriptorProto, +} from 'google-protobuf/google/protobuf/descriptor_pb'; import Type = FieldDescriptorProto.Type; export type ExportMessageEntry = { - pkg: string, - fileName: string, - messageOptions: MessageOptions, - mapFieldOptions?: { - key: [Type, string], - value: [Type, string], - } + pkg: string; + fileName: string; + messageOptions: MessageOptions; + mapFieldOptions?: { + key: [Type, string]; + value: [Type, string]; + }; }; export type ExportEnumEntry = { - pkg: string, - fileName: string, - enumOptions: EnumOptions, + pkg: string; + fileName: string; + enumOptions: EnumOptions; }; export class ExportMap { - messageMap: {[key: string]: ExportMessageEntry} = {}; - enumMap: {[key: string]: ExportEnumEntry} = {}; + messageMap: { [key: string]: ExportMessageEntry } = {}; + enumMap: { [key: string]: ExportEnumEntry } = {}; - exportNested(scope: string, fileDescriptor: FileDescriptorProto, message: DescriptorProto) { - const messageEntry: ExportMessageEntry = { - pkg: fileDescriptor.getPackage(), - fileName: fileDescriptor.getName(), - messageOptions: message.getOptions(), - mapFieldOptions: message.getOptions() && message.getOptions().getMapEntry() ? { - key: [message.getFieldList()[0].getType(), message.getFieldList()[0].getTypeName().slice(1)], - value: [message.getFieldList()[1].getType(), message.getFieldList()[1].getTypeName().slice(1)], - } : undefined, - }; + exportNested( + scope: string, + fileDescriptor: FileDescriptorProto, + message: DescriptorProto + ) { + const messageEntry: ExportMessageEntry = { + pkg: fileDescriptor.getPackage(), + fileName: fileDescriptor.getName(), + messageOptions: message.getOptions(), + mapFieldOptions: + message.getOptions() && message.getOptions().getMapEntry() + ? { + key: [ + message.getFieldList()[0].getType(), + message.getFieldList()[0].getTypeName().slice(1), + ], + value: [ + message.getFieldList()[1].getType(), + message.getFieldList()[1].getTypeName().slice(1), + ], + } + : undefined, + }; - const packagePrefix = scope ? scope + "." : ""; + const packagePrefix = scope ? scope + '.' : ''; - const entryName = `${packagePrefix}${message.getName()}`; - this.messageMap[entryName] = messageEntry; + const entryName = `${packagePrefix}${message.getName()}`; + this.messageMap[entryName] = messageEntry; - message.getNestedTypeList().forEach(nested => { - this.exportNested(`${packagePrefix}${message.getName()}`, fileDescriptor, nested); - }); + message.getNestedTypeList().forEach((nested) => { + this.exportNested( + `${packagePrefix}${message.getName()}`, + fileDescriptor, + nested + ); + }); - message.getEnumTypeList().forEach(enumType => { - const identifier = `${packagePrefix}${message.getName()}.${enumType.getName()}`; - this.enumMap[identifier] = { - pkg: fileDescriptor.getPackage(), - fileName: fileDescriptor.getName(), - enumOptions: enumType.getOptions(), - }; - }); - } + message.getEnumTypeList().forEach((enumType) => { + const identifier = `${packagePrefix}${message.getName()}.${enumType.getName()}`; + this.enumMap[identifier] = { + pkg: fileDescriptor.getPackage(), + fileName: fileDescriptor.getName(), + enumOptions: enumType.getOptions(), + }; + }); + } - addFileDescriptor(fileDescriptor: FileDescriptorProto) { - const scope = fileDescriptor.getPackage(); - fileDescriptor.getMessageTypeList().forEach(messageType => { - this.exportNested(scope, fileDescriptor, messageType); - }); + addFileDescriptor(fileDescriptor: FileDescriptorProto) { + const scope = fileDescriptor.getPackage(); + fileDescriptor.getMessageTypeList().forEach((messageType) => { + this.exportNested(scope, fileDescriptor, messageType); + }); - fileDescriptor.getEnumTypeList().forEach(enumType => { - const packagePrefix = scope ? scope + "." : ""; - this.enumMap[packagePrefix + enumType.getName()] = { - pkg: fileDescriptor.getPackage(), - fileName: fileDescriptor.getName(), - enumOptions: enumType.getOptions(), - }; - }); - } + fileDescriptor.getEnumTypeList().forEach((enumType) => { + const packagePrefix = scope ? scope + '.' : ''; + this.enumMap[packagePrefix + enumType.getName()] = { + pkg: fileDescriptor.getPackage(), + fileName: fileDescriptor.getName(), + enumOptions: enumType.getOptions(), + }; + }); + } - getMessage(str: string): ExportMessageEntry | undefined { - return this.messageMap[str]; - } + getMessage(str: string): ExportMessageEntry | undefined { + return this.messageMap[str]; + } - getEnum(str: string): ExportEnumEntry | undefined { - return this.enumMap[str]; - } + getEnum(str: string): ExportEnumEntry | undefined { + return this.enumMap[str]; + } } diff --git a/src/ts-protoc-gen/Printer.ts b/src/ts-protoc-gen/Printer.ts index 4a31ef1..ca1ddb8 100644 --- a/src/ts-protoc-gen/Printer.ts +++ b/src/ts-protoc-gen/Printer.ts @@ -1,30 +1,30 @@ -import {generateIndent} from "./util"; +import { generateIndent } from './util'; export class Printer { - indentStr: string; - output: string = ""; + indentStr: string; + output: string = ''; - constructor(indentLevel: number) { - this.indentStr = generateIndent(indentLevel); - } + constructor(indentLevel: number) { + this.indentStr = generateIndent(indentLevel); + } - printLn(str: string) { - this.output += this.indentStr + str + "\n"; - } + printLn(str: string) { + this.output += this.indentStr + str + '\n'; + } - print(str: string) { - this.output += str; - } + print(str: string) { + this.output += str; + } - printEmptyLn() { - this.output += "\n"; - } + printEmptyLn() { + this.output += '\n'; + } - printIndentedLn(str: string) { - this.output += this.indentStr + " " + str + "\n"; - } + printIndentedLn(str: string) { + this.output += this.indentStr + ' ' + str + '\n'; + } - getOutput(): string { - return this.output; - } + getOutput(): string { + return this.output; + } } diff --git a/src/ts-protoc-gen/WellKnown.ts b/src/ts-protoc-gen/WellKnown.ts index 51556da..70da371 100644 --- a/src/ts-protoc-gen/WellKnown.ts +++ b/src/ts-protoc-gen/WellKnown.ts @@ -1,14 +1,16 @@ -export const WellKnownTypesMap: {[key: string]: string} = { - "google/protobuf/compiler/plugin.proto": "google-protobuf/google/protobuf/compiler/plugin_pb", - "google/protobuf/any.proto": "google-protobuf/google/protobuf/any_pb", - "google/protobuf/api.proto": "google-protobuf/google/protobuf/api_pb", - "google/protobuf/descriptor.proto": "google-protobuf/google/protobuf/descriptor_pb", - "google/protobuf/duration.proto": "google-protobuf/google/protobuf/duration_pb", - "google/protobuf/empty.proto": "google-protobuf/google/protobuf/empty_pb", - "google/protobuf/field_mask.proto": "google-protobuf/google/protobuf/field_mask_pb", - "google/protobuf/source_context.proto": "google-protobuf/google/protobuf/source_context_pb", - "google/protobuf/struct.proto": "google-protobuf/google/protobuf/struct_pb", - "google/protobuf/timestamp.proto": "google-protobuf/google/protobuf/timestamp_pb", - "google/protobuf/type.proto": "google-protobuf/google/protobuf/type_pb", - "google/protobuf/wrappers.proto": "google-protobuf/google/protobuf/wrappers_pb" +export const WellKnownTypesMap: { [key: string]: string } = { + 'google/protobuf/compiler/plugin.proto': + 'google-protobuf/google/protobuf/compiler/plugin_pb', + 'google/protobuf/any.proto': 'google-protobuf/google/protobuf/any_pb', + 'google/protobuf/api.proto': 'google-protobuf/google/protobuf/api_pb', + 'google/protobuf/descriptor.proto': 'google-protobuf/google/protobuf/descriptor_pb', + 'google/protobuf/duration.proto': 'google-protobuf/google/protobuf/duration_pb', + 'google/protobuf/empty.proto': 'google-protobuf/google/protobuf/empty_pb', + 'google/protobuf/field_mask.proto': 'google-protobuf/google/protobuf/field_mask_pb', + 'google/protobuf/source_context.proto': + 'google-protobuf/google/protobuf/source_context_pb', + 'google/protobuf/struct.proto': 'google-protobuf/google/protobuf/struct_pb', + 'google/protobuf/timestamp.proto': 'google-protobuf/google/protobuf/timestamp_pb', + 'google/protobuf/type.proto': 'google-protobuf/google/protobuf/type_pb', + 'google/protobuf/wrappers.proto': 'google-protobuf/google/protobuf/wrappers_pb', }; diff --git a/src/ts-protoc-gen/index.ts b/src/ts-protoc-gen/index.ts index 33e69ac..e63c388 100644 --- a/src/ts-protoc-gen/index.ts +++ b/src/ts-protoc-gen/index.ts @@ -1,11 +1,14 @@ -import {printFileDescriptorTSD} from "./ts/fileDescriptorTSD"; -import {ExportMap} from "./ExportMap"; -import {replaceProtoSuffix, withAllStdIn, getParameterEnums} from "./util"; -import {CodeGeneratorRequest, CodeGeneratorResponse} from "google-protobuf/google/protobuf/compiler/plugin_pb"; -import {FileDescriptorProto} from "google-protobuf/google/protobuf/descriptor_pb"; -import {generateGrpcWebService} from "./service/grpcweb"; -import {generateGrpcNodeService} from "./service/grpcnode"; -import {ServiceParameter} from "./parameters"; +import { printFileDescriptorTSD } from './ts/fileDescriptorTSD'; +import { ExportMap } from './ExportMap'; +import { replaceProtoSuffix, withAllStdIn, getParameterEnums } from './util'; +import { + CodeGeneratorRequest, + CodeGeneratorResponse, +} from 'google-protobuf/google/protobuf/compiler/plugin_pb'; +import { FileDescriptorProto } from 'google-protobuf/google/protobuf/descriptor_pb'; +import { generateGrpcWebService } from './service/grpcweb'; +import { generateGrpcNodeService } from './service/grpcnode'; +import { ServiceParameter } from './parameters'; /** * This is the ProtoC compiler plugin. @@ -17,45 +20,55 @@ import {ServiceParameter} from "./parameters"; * */ withAllStdIn((inputBuff: Buffer) => { - try { - const typedInputBuff = new Uint8Array(inputBuff.length); - typedInputBuff.set(inputBuff); + try { + const typedInputBuff = new Uint8Array(inputBuff.length); + typedInputBuff.set(inputBuff); - const codeGenRequest = CodeGeneratorRequest.deserializeBinary(typedInputBuff); - const codeGenResponse = new CodeGeneratorResponse(); - const exportMap = new ExportMap(); - const fileNameToDescriptor: {[key: string]: FileDescriptorProto} = {}; + const codeGenRequest = CodeGeneratorRequest.deserializeBinary(typedInputBuff); + const codeGenResponse = new CodeGeneratorResponse(); + const exportMap = new ExportMap(); + const fileNameToDescriptor: { [key: string]: FileDescriptorProto } = {}; - const parameter = codeGenRequest.getParameter(); - const {service, mode} = getParameterEnums(parameter); + const parameter = codeGenRequest.getParameter(); + const { service, mode } = getParameterEnums(parameter); - const generateGrpcWebServices = service === ServiceParameter.GrpcWeb; - const generateGrpcNodeServices = service === ServiceParameter.GrpcNode; + const generateGrpcWebServices = service === ServiceParameter.GrpcWeb; + const generateGrpcNodeServices = service === ServiceParameter.GrpcNode; - codeGenRequest.getProtoFileList().forEach(protoFileDescriptor => { - fileNameToDescriptor[protoFileDescriptor.getName()] = protoFileDescriptor; - exportMap.addFileDescriptor(protoFileDescriptor); - }); + codeGenRequest.getProtoFileList().forEach((protoFileDescriptor) => { + fileNameToDescriptor[protoFileDescriptor.getName()] = protoFileDescriptor; + exportMap.addFileDescriptor(protoFileDescriptor); + }); - codeGenRequest.getFileToGenerateList().forEach(fileName => { - const outputFileName = replaceProtoSuffix(fileName); - const thisFile = new CodeGeneratorResponse.File(); - thisFile.setName(outputFileName + ".d.ts"); - thisFile.setContent(printFileDescriptorTSD(fileNameToDescriptor[fileName], exportMap)); - codeGenResponse.addFile(thisFile); + codeGenRequest.getFileToGenerateList().forEach((fileName) => { + const outputFileName = replaceProtoSuffix(fileName); + const thisFile = new CodeGeneratorResponse.File(); + thisFile.setName(outputFileName + '.d.ts'); + thisFile.setContent( + printFileDescriptorTSD(fileNameToDescriptor[fileName], exportMap) + ); + codeGenResponse.addFile(thisFile); - if (generateGrpcWebServices) { - generateGrpcWebService(outputFileName, fileNameToDescriptor[fileName], exportMap) - .forEach(file => codeGenResponse.addFile(file)); - } else if (generateGrpcNodeServices) { - const file = generateGrpcNodeService(outputFileName, fileNameToDescriptor[fileName], exportMap, mode); - codeGenResponse.addFile(file); - } - }); + if (generateGrpcWebServices) { + generateGrpcWebService( + outputFileName, + fileNameToDescriptor[fileName], + exportMap + ).forEach((file) => codeGenResponse.addFile(file)); + } else if (generateGrpcNodeServices) { + const file = generateGrpcNodeService( + outputFileName, + fileNameToDescriptor[fileName], + exportMap, + mode + ); + codeGenResponse.addFile(file); + } + }); - process.stdout.write(Buffer.from(codeGenResponse.serializeBinary())); - } catch (err) { - console.error("protoc-gen-ts error: " + err.stack + "\n"); - process.exit(1); - } + process.stdout.write(Buffer.from(codeGenResponse.serializeBinary())); + } catch (err) { + console.error('protoc-gen-ts error: ' + err.stack + '\n'); + process.exit(1); + } }); diff --git a/src/ts-protoc-gen/parameters.ts b/src/ts-protoc-gen/parameters.ts index c5d8e7d..8125e95 100644 --- a/src/ts-protoc-gen/parameters.ts +++ b/src/ts-protoc-gen/parameters.ts @@ -1,10 +1,10 @@ export enum ServiceParameter { - None, - GrpcWeb, - GrpcNode + None, + GrpcWeb, + GrpcNode, } export enum ModeParameter { - None, - GrpcJs + None, + GrpcJs, } diff --git a/src/ts-protoc-gen/service/common.ts b/src/ts-protoc-gen/service/common.ts index 9e24596..7d4c75f 100644 --- a/src/ts-protoc-gen/service/common.ts +++ b/src/ts-protoc-gen/service/common.ts @@ -1,142 +1,183 @@ -import {CodeGeneratorResponse} from "google-protobuf/google/protobuf/compiler/plugin_pb"; -import {FileDescriptorProto, MethodDescriptorProto, ServiceDescriptorProto} from "google-protobuf/google/protobuf/descriptor_pb"; -import {ExportMap} from "../ExportMap"; -import {WellKnownTypesMap} from "../WellKnown"; -import {getFieldType, MESSAGE_TYPE} from "../ts/FieldTypes"; -import {filePathToPseudoNamespace, replaceProtoSuffix, getPathToRoot, normaliseFieldObjectName} from "../util"; +import { CodeGeneratorResponse } from 'google-protobuf/google/protobuf/compiler/plugin_pb'; +import { + FileDescriptorProto, + MethodDescriptorProto, + ServiceDescriptorProto, +} from 'google-protobuf/google/protobuf/descriptor_pb'; +import { ExportMap } from '../ExportMap'; +import { WellKnownTypesMap } from '../WellKnown'; +import { getFieldType, MESSAGE_TYPE } from '../ts/FieldTypes'; +import { + filePathToPseudoNamespace, + replaceProtoSuffix, + getPathToRoot, + normaliseFieldObjectName, +} from '../util'; export function createFile(output: string, filename: string): CodeGeneratorResponse.File { - const file = new CodeGeneratorResponse.File(); - file.setName(filename); - file.setContent(output); - return file; + const file = new CodeGeneratorResponse.File(); + file.setName(filename); + file.setContent(output); + return file; } type CallingTypes = { - requestType: string - responseType: string + requestType: string; + responseType: string; }; -function getCallingTypes(method: MethodDescriptorProto, exportMap: ExportMap): CallingTypes { - return { - requestType: getFieldType(MESSAGE_TYPE, method.getInputType().slice(1), "", exportMap), - responseType: getFieldType(MESSAGE_TYPE, method.getOutputType().slice(1), "", exportMap), - }; +function getCallingTypes( + method: MethodDescriptorProto, + exportMap: ExportMap +): CallingTypes { + return { + requestType: getFieldType( + MESSAGE_TYPE, + method.getInputType().slice(1), + '', + exportMap + ), + responseType: getFieldType( + MESSAGE_TYPE, + method.getOutputType().slice(1), + '', + exportMap + ), + }; } -function isUsed(fileDescriptor: FileDescriptorProto, pseudoNamespace: string, exportMap: ExportMap) { - return fileDescriptor.getServiceList().some(service => { - return service.getMethodList().some(method => { - const callingTypes = getCallingTypes(method, exportMap); - const namespacePackage = pseudoNamespace + "."; - return ( - callingTypes.requestType.indexOf(namespacePackage) === 0 || - callingTypes.responseType.indexOf(namespacePackage) === 0 - ); +function isUsed( + fileDescriptor: FileDescriptorProto, + pseudoNamespace: string, + exportMap: ExportMap +) { + return fileDescriptor.getServiceList().some((service) => { + return service.getMethodList().some((method) => { + const callingTypes = getCallingTypes(method, exportMap); + const namespacePackage = pseudoNamespace + '.'; + return ( + callingTypes.requestType.indexOf(namespacePackage) === 0 || + callingTypes.responseType.indexOf(namespacePackage) === 0 + ); + }); }); - }); } export type ImportDescriptor = { - readonly namespace: string - readonly path: string + readonly namespace: string; + readonly path: string; }; export type RPCMethodDescriptor = { - readonly nameAsPascalCase: string, - readonly nameAsCamelCase: string, - readonly functionName: string, - readonly serviceName: string, - readonly requestStream: boolean - readonly responseStream: boolean - readonly requestType: string - readonly responseType: string + readonly nameAsPascalCase: string; + readonly nameAsCamelCase: string; + readonly functionName: string; + readonly serviceName: string; + readonly requestStream: boolean; + readonly responseStream: boolean; + readonly requestType: string; + readonly responseType: string; }; export class RPCDescriptor { - private readonly grpcService: GrpcServiceDescriptor; - private readonly protoService: ServiceDescriptorProto; - private readonly exportMap: ExportMap; + private readonly grpcService: GrpcServiceDescriptor; + private readonly protoService: ServiceDescriptorProto; + private readonly exportMap: ExportMap; - constructor(grpcService: GrpcServiceDescriptor, protoService: ServiceDescriptorProto, exportMap: ExportMap) { - this.grpcService = grpcService; - this.protoService = protoService; - this.exportMap = exportMap; - } - get name(): string { - return this.protoService.getName(); - } + constructor( + grpcService: GrpcServiceDescriptor, + protoService: ServiceDescriptorProto, + exportMap: ExportMap + ) { + this.grpcService = grpcService; + this.protoService = protoService; + this.exportMap = exportMap; + } + get name(): string { + return this.protoService.getName(); + } - get qualifiedName(): string { - return (this.grpcService.packageName ? `${this.grpcService.packageName}.` : "") + this.name; - } + get qualifiedName(): string { + return ( + (this.grpcService.packageName ? `${this.grpcService.packageName}.` : '') + + this.name + ); + } - get methods(): RPCMethodDescriptor[] { - return this.protoService.getMethodList() - .map(method => { - const callingTypes = getCallingTypes(method, this.exportMap); - const nameAsCamelCase = method.getName()[0].toLowerCase() + method.getName().substr(1); - return { - nameAsPascalCase: method.getName(), - nameAsCamelCase, - functionName: normaliseFieldObjectName(nameAsCamelCase), - serviceName: this.name, - requestStream: method.getClientStreaming(), - responseStream: method.getServerStreaming(), - requestType: callingTypes.requestType, - responseType: callingTypes.responseType, - }; - }); - } + get methods(): RPCMethodDescriptor[] { + return this.protoService.getMethodList().map((method) => { + const callingTypes = getCallingTypes(method, this.exportMap); + const nameAsCamelCase = + method.getName()[0].toLowerCase() + method.getName().substr(1); + return { + nameAsPascalCase: method.getName(), + nameAsCamelCase, + functionName: normaliseFieldObjectName(nameAsCamelCase), + serviceName: this.name, + requestStream: method.getClientStreaming(), + responseStream: method.getServerStreaming(), + requestType: callingTypes.requestType, + responseType: callingTypes.responseType, + }; + }); + } } export class GrpcServiceDescriptor { - private readonly fileDescriptor: FileDescriptorProto; - private readonly exportMap: ExportMap; - private readonly pathToRoot: string; + private readonly fileDescriptor: FileDescriptorProto; + private readonly exportMap: ExportMap; + private readonly pathToRoot: string; - constructor(fileDescriptor: FileDescriptorProto, exportMap: ExportMap) { - this.fileDescriptor = fileDescriptor; - this.exportMap = exportMap; - this.pathToRoot = getPathToRoot(fileDescriptor.getName()); - } + constructor(fileDescriptor: FileDescriptorProto, exportMap: ExportMap) { + this.fileDescriptor = fileDescriptor; + this.exportMap = exportMap; + this.pathToRoot = getPathToRoot(fileDescriptor.getName()); + } - get filename(): string { - return this.fileDescriptor.getName(); - } + get filename(): string { + return this.fileDescriptor.getName(); + } - get packageName(): string { - return this.fileDescriptor.getPackage(); - } + get packageName(): string { + return this.fileDescriptor.getPackage(); + } - get imports(): ImportDescriptor[] { - const dependencies = this.fileDescriptor.getDependencyList() - .filter(dependency => isUsed(this.fileDescriptor, filePathToPseudoNamespace(dependency), this.exportMap)) - .map(dependency => { - const namespace = filePathToPseudoNamespace(dependency); - if (dependency in WellKnownTypesMap) { - return { - namespace, - path: WellKnownTypesMap[dependency], - }; - } else { - return { - namespace, - path: `${this.pathToRoot}${replaceProtoSuffix(replaceProtoSuffix(dependency))}` - }; - } - }); - const hostProto = { - namespace: filePathToPseudoNamespace(this.filename), - path: `${this.pathToRoot}${replaceProtoSuffix(this.filename)}`, - }; - return [ hostProto ].concat(dependencies); - } + get imports(): ImportDescriptor[] { + const dependencies = this.fileDescriptor + .getDependencyList() + .filter((dependency) => + isUsed( + this.fileDescriptor, + filePathToPseudoNamespace(dependency), + this.exportMap + ) + ) + .map((dependency) => { + const namespace = filePathToPseudoNamespace(dependency); + if (dependency in WellKnownTypesMap) { + return { + namespace, + path: WellKnownTypesMap[dependency], + }; + } else { + return { + namespace, + path: `${this.pathToRoot}${replaceProtoSuffix( + replaceProtoSuffix(dependency) + )}`, + }; + } + }); + const hostProto = { + namespace: filePathToPseudoNamespace(this.filename), + path: `${this.pathToRoot}${replaceProtoSuffix(this.filename)}`, + }; + return [hostProto].concat(dependencies); + } - get services(): RPCDescriptor[] { - return this.fileDescriptor.getServiceList() - .map(service => { - return new RPCDescriptor(this, service, this.exportMap); - }); - } + get services(): RPCDescriptor[] { + return this.fileDescriptor.getServiceList().map((service) => { + return new RPCDescriptor(this, service, this.exportMap); + }); + } } diff --git a/src/ts-protoc-gen/service/grpcnode.ts b/src/ts-protoc-gen/service/grpcnode.ts index 07653ce..245961d 100644 --- a/src/ts-protoc-gen/service/grpcnode.ts +++ b/src/ts-protoc-gen/service/grpcnode.ts @@ -1,127 +1,162 @@ -import {ExportMap} from "../ExportMap"; -import {Printer} from "../Printer"; -import {FileDescriptorProto} from "google-protobuf/google/protobuf/descriptor_pb"; -import {CodeGeneratorResponse} from "google-protobuf/google/protobuf/compiler/plugin_pb"; -import {createFile, RPCDescriptor, GrpcServiceDescriptor, RPCMethodDescriptor} from "./common"; -import { ModeParameter } from "../parameters"; +import { ExportMap } from '../ExportMap'; +import { Printer } from '../Printer'; +import { FileDescriptorProto } from 'google-protobuf/google/protobuf/descriptor_pb'; +import { CodeGeneratorResponse } from 'google-protobuf/google/protobuf/compiler/plugin_pb'; +import { + createFile, + RPCDescriptor, + GrpcServiceDescriptor, + RPCMethodDescriptor, +} from './common'; +import { ModeParameter } from '../parameters'; -export function generateGrpcNodeService(filename: string, descriptor: FileDescriptorProto, exportMap: ExportMap, modeParameter: ModeParameter): CodeGeneratorResponse.File { - const definitionFilename = filename.replace(/_pb$/, "_grpc_pb.d.ts"); - return createFile(generateTypeScriptDefinition(descriptor, exportMap, modeParameter), definitionFilename); +export function generateGrpcNodeService( + filename: string, + descriptor: FileDescriptorProto, + exportMap: ExportMap, + modeParameter: ModeParameter +): CodeGeneratorResponse.File { + const definitionFilename = filename.replace(/_pb$/, '_grpc_pb.d.ts'); + return createFile( + generateTypeScriptDefinition(descriptor, exportMap, modeParameter), + definitionFilename + ); } -function generateTypeScriptDefinition(fileDescriptor: FileDescriptorProto, exportMap: ExportMap, modeParameter: ModeParameter): string { - const serviceDescriptor = new GrpcServiceDescriptor(fileDescriptor, exportMap); - const printer = new Printer(0); +function generateTypeScriptDefinition( + fileDescriptor: FileDescriptorProto, + exportMap: ExportMap, + modeParameter: ModeParameter +): string { + const serviceDescriptor = new GrpcServiceDescriptor(fileDescriptor, exportMap); + const printer = new Printer(0); - const hasServices = serviceDescriptor.services.length > 0; + const hasServices = serviceDescriptor.services.length > 0; - // Header. - if (hasServices) { - printer.printLn("// GENERATED CODE -- DO NOT EDIT!"); + // Header. + if (hasServices) { + printer.printLn('// GENERATED CODE -- DO NOT EDIT!'); + printer.printEmptyLn(); + } else { + printer.printLn('// GENERATED CODE -- NO SERVICES IN PROTO'); + return printer.getOutput(); + } + + printer.printLn(`// package: ${serviceDescriptor.packageName}`); + printer.printLn(`// file: ${serviceDescriptor.filename}`); printer.printEmptyLn(); - } else { - printer.printLn("// GENERATED CODE -- NO SERVICES IN PROTO"); + + // Import statements. + serviceDescriptor.imports.forEach((importDescriptor) => { + printer.printLn( + `import * as ${importDescriptor.namespace} from "${importDescriptor.path}";` + ); + }); + const importPackage = + modeParameter === ModeParameter.GrpcJs ? '@grpc/grpc-js' : 'grpc'; + printer.printLn(`import * as grpc from "${importPackage}";`); + + // Services. + serviceDescriptor.services.forEach((service) => { + printer.printEmptyLn(); + printService(printer, service); + printer.printEmptyLn(); + printClient(printer, service); + }); + return printer.getOutput(); - } - - printer.printLn(`// package: ${serviceDescriptor.packageName}`); - printer.printLn(`// file: ${serviceDescriptor.filename}`); - printer.printEmptyLn(); - - // Import statements. - serviceDescriptor.imports - .forEach(importDescriptor => { - printer.printLn(`import * as ${importDescriptor.namespace} from "${importDescriptor.path}";`); - }); - const importPackage = modeParameter === ModeParameter.GrpcJs ? "@grpc/grpc-js" : "grpc"; - printer.printLn(`import * as grpc from "${importPackage}";`); - - // Services. - serviceDescriptor.services - .forEach(service => { - printer.printEmptyLn(); - printService(printer, service); - printer.printEmptyLn(); - printClient(printer, service); - }); - - return printer.getOutput(); } function printService(printer: Printer, service: RPCDescriptor) { - const serviceName = `${service.name}Service`; - printer.printLn(`interface I${serviceName} extends grpc.ServiceDefinition {`); - service.methods - .forEach(method => { - const methodType = `grpc.MethodDefinition<${method.requestType}, ${method.responseType}>`; - printer.printIndentedLn(`${method.nameAsCamelCase}: ${methodType};`); + const serviceName = `${service.name}Service`; + printer.printLn( + `interface I${serviceName} extends grpc.ServiceDefinition {` + ); + service.methods.forEach((method) => { + const methodType = `grpc.MethodDefinition<${method.requestType}, ${method.responseType}>`; + printer.printIndentedLn(`${method.nameAsCamelCase}: ${methodType};`); }); - printer.printLn("}"); - printer.printEmptyLn(); - printer.printLn(`export const ${serviceName}: I${serviceName};`); + printer.printLn('}'); + printer.printEmptyLn(); + printer.printLn(`export const ${serviceName}: I${serviceName};`); } function printClient(printer: Printer, service: RPCDescriptor) { - printer.printLn(`export class ${service.name}Client extends grpc.Client {`); - printer.printIndentedLn("constructor(address: string, credentials: grpc.ChannelCredentials, options?: object);"); - service.methods - .forEach(method => { - if (!method.requestStream && !method.responseStream) { - printUnaryRequestMethod(printer, method); - } else if (!method.requestStream) { - printServerStreamRequestMethod(printer, method); - } else if (!method.responseStream) { - printClientStreamRequestMethod(printer, method); - } else { - printBidiStreamRequest(printer, method); - } + printer.printLn(`export class ${service.name}Client extends grpc.Client {`); + printer.printIndentedLn( + 'constructor(address: string, credentials: grpc.ChannelCredentials, options?: object);' + ); + service.methods.forEach((method) => { + if (!method.requestStream && !method.responseStream) { + printUnaryRequestMethod(printer, method); + } else if (!method.requestStream) { + printServerStreamRequestMethod(printer, method); + } else if (!method.responseStream) { + printClientStreamRequestMethod(printer, method); + } else { + printBidiStreamRequest(printer, method); + } }); - printer.printLn("}"); + printer.printLn('}'); } -const metadata = "metadata: grpc.Metadata | null"; -const options = "options: grpc.CallOptions | null"; -const metadataOrOptions = "metadataOrOptions: grpc.Metadata | grpc.CallOptions | null"; +const metadata = 'metadata: grpc.Metadata | null'; +const options = 'options: grpc.CallOptions | null'; +const metadataOrOptions = 'metadataOrOptions: grpc.Metadata | grpc.CallOptions | null'; -const optionalMetadata = "metadata?: grpc.Metadata | null"; -const optionalOptions = "options?: grpc.CallOptions | null"; -const optionalMetadataOrOptions = "metadataOrOptions?: grpc.Metadata | grpc.CallOptions | null"; +const optionalMetadata = 'metadata?: grpc.Metadata | null'; +const optionalOptions = 'options?: grpc.CallOptions | null'; +const optionalMetadataOrOptions = + 'metadataOrOptions?: grpc.Metadata | grpc.CallOptions | null'; function printUnaryRequestMethod(printer: Printer, method: RPCMethodDescriptor) { - const name = method.nameAsCamelCase; - const argument = `argument: ${method.requestType}`; - const callback = `callback: grpc.requestCallback<${method.responseType}>`; - const returnType = "grpc.ClientUnaryCall"; + const name = method.nameAsCamelCase; + const argument = `argument: ${method.requestType}`; + const callback = `callback: grpc.requestCallback<${method.responseType}>`; + const returnType = 'grpc.ClientUnaryCall'; - printer.printIndentedLn(`${name}(${argument}, ${callback}): ${returnType};`); - printer.printIndentedLn(`${name}(${argument}, ${metadataOrOptions}, ${callback}): ${returnType};`); - printer.printIndentedLn(`${name}(${argument}, ${metadata}, ${options}, ${callback}): ${returnType};`); + printer.printIndentedLn(`${name}(${argument}, ${callback}): ${returnType};`); + printer.printIndentedLn( + `${name}(${argument}, ${metadataOrOptions}, ${callback}): ${returnType};` + ); + printer.printIndentedLn( + `${name}(${argument}, ${metadata}, ${options}, ${callback}): ${returnType};` + ); } function printServerStreamRequestMethod(printer: Printer, method: RPCMethodDescriptor) { - const name = method.nameAsCamelCase; - const argument = `argument: ${method.requestType}`; - const returnType = `grpc.ClientReadableStream<${method.responseType}>`; + const name = method.nameAsCamelCase; + const argument = `argument: ${method.requestType}`; + const returnType = `grpc.ClientReadableStream<${method.responseType}>`; - printer.printIndentedLn(`${name}(${argument}, ${optionalMetadataOrOptions}): ${returnType};`); - printer.printIndentedLn(`${name}(${argument}, ${optionalMetadata}, ${optionalOptions}): ${returnType};`); + printer.printIndentedLn( + `${name}(${argument}, ${optionalMetadataOrOptions}): ${returnType};` + ); + printer.printIndentedLn( + `${name}(${argument}, ${optionalMetadata}, ${optionalOptions}): ${returnType};` + ); } function printClientStreamRequestMethod(printer: Printer, method: RPCMethodDescriptor) { - const name = method.nameAsCamelCase; - const callback = `callback: grpc.requestCallback<${method.responseType}>`; - const returnType = `grpc.ClientWritableStream<${method.requestType}>`; + const name = method.nameAsCamelCase; + const callback = `callback: grpc.requestCallback<${method.responseType}>`; + const returnType = `grpc.ClientWritableStream<${method.requestType}>`; - printer.printIndentedLn(`${name}(${callback}): grpc.ClientWritableStream<${method.requestType}>;`); - printer.printIndentedLn(`${name}(${metadataOrOptions}, ${callback}): ${returnType};`); - printer.printIndentedLn(`${name}(${metadata}, ${options}, ${callback}): ${returnType};`); + printer.printIndentedLn( + `${name}(${callback}): grpc.ClientWritableStream<${method.requestType}>;` + ); + printer.printIndentedLn(`${name}(${metadataOrOptions}, ${callback}): ${returnType};`); + printer.printIndentedLn( + `${name}(${metadata}, ${options}, ${callback}): ${returnType};` + ); } function printBidiStreamRequest(printer: Printer, method: RPCMethodDescriptor) { - const name = method.nameAsCamelCase; - const returnType = `grpc.ClientDuplexStream<${method.requestType}, ${method.responseType}>`; + const name = method.nameAsCamelCase; + const returnType = `grpc.ClientDuplexStream<${method.requestType}, ${method.responseType}>`; - printer.printIndentedLn(`${name}(${optionalMetadataOrOptions}): ${returnType};`); - printer.printIndentedLn(`${name}(${optionalMetadata}, ${optionalOptions}): ${returnType};`); + printer.printIndentedLn(`${name}(${optionalMetadataOrOptions}): ${returnType};`); + printer.printIndentedLn( + `${name}(${optionalMetadata}, ${optionalOptions}): ${returnType};` + ); } diff --git a/src/ts-protoc-gen/service/grpcweb.ts b/src/ts-protoc-gen/service/grpcweb.ts index 1a9d8d5..87b62fc 100644 --- a/src/ts-protoc-gen/service/grpcweb.ts +++ b/src/ts-protoc-gen/service/grpcweb.ts @@ -1,397 +1,570 @@ -import {ExportMap} from "../ExportMap"; -import {Printer} from "../Printer"; -import {CodePrinter} from "../CodePrinter"; -import {FileDescriptorProto} from "google-protobuf/google/protobuf/descriptor_pb"; -import {CodeGeneratorResponse} from "google-protobuf/google/protobuf/compiler/plugin_pb"; -import {createFile, RPCMethodDescriptor, RPCDescriptor, GrpcServiceDescriptor} from "./common"; +import { ExportMap } from '../ExportMap'; +import { Printer } from '../Printer'; +import { CodePrinter } from '../CodePrinter'; +import { FileDescriptorProto } from 'google-protobuf/google/protobuf/descriptor_pb'; +import { CodeGeneratorResponse } from 'google-protobuf/google/protobuf/compiler/plugin_pb'; +import { + createFile, + RPCMethodDescriptor, + RPCDescriptor, + GrpcServiceDescriptor, +} from './common'; -export function generateGrpcWebService(filename: string, descriptor: FileDescriptorProto, exportMap: ExportMap): CodeGeneratorResponse.File[] { - return [ - createFile(generateTypeScriptDefinition(descriptor, exportMap), `${filename}_service.d.ts`), - createFile(generateJavaScript(descriptor, exportMap), `${filename}_service.js`), - ]; +export function generateGrpcWebService( + filename: string, + descriptor: FileDescriptorProto, + exportMap: ExportMap +): CodeGeneratorResponse.File[] { + return [ + createFile( + generateTypeScriptDefinition(descriptor, exportMap), + `${filename}_service.d.ts` + ), + createFile(generateJavaScript(descriptor, exportMap), `${filename}_service.js`), + ]; } -function generateTypeScriptDefinition(fileDescriptor: FileDescriptorProto, exportMap: ExportMap): string { - const serviceDescriptor = new GrpcServiceDescriptor(fileDescriptor, exportMap); - const printer = new Printer(0); +function generateTypeScriptDefinition( + fileDescriptor: FileDescriptorProto, + exportMap: ExportMap +): string { + const serviceDescriptor = new GrpcServiceDescriptor(fileDescriptor, exportMap); + const printer = new Printer(0); - // Header. - printer.printLn(`// package: ${serviceDescriptor.packageName}`); - printer.printLn(`// file: ${serviceDescriptor.filename}`); - printer.printEmptyLn(); + // Header. + printer.printLn(`// package: ${serviceDescriptor.packageName}`); + printer.printLn(`// file: ${serviceDescriptor.filename}`); + printer.printEmptyLn(); - if (serviceDescriptor.services.length === 0) { - return printer.getOutput(); - } + if (serviceDescriptor.services.length === 0) { + return printer.getOutput(); + } - // Import statements. - serviceDescriptor.imports - .forEach(importDescriptor => { - printer.printLn(`import * as ${importDescriptor.namespace} from "${importDescriptor.path}";`); + // Import statements. + serviceDescriptor.imports.forEach((importDescriptor) => { + printer.printLn( + `import * as ${importDescriptor.namespace} from "${importDescriptor.path}";` + ); }); - printer.printLn(`import {grpc} from "@improbable-eng/grpc-web";`); - printer.printEmptyLn(); + printer.printLn(`import {grpc} from "@improbable-eng/grpc-web";`); + printer.printEmptyLn(); - // Services. - serviceDescriptor.services - .forEach(service => { - - // Method Type Definitions - service.methods.forEach(method => { - printer.printLn(`type ${method.serviceName}${method.nameAsPascalCase} = {`); - printer.printIndentedLn(`readonly methodName: string;`); - printer.printIndentedLn(`readonly service: typeof ${method.serviceName};`); - printer.printIndentedLn(`readonly requestStream: ${method.requestStream};`); - printer.printIndentedLn(`readonly responseStream: ${method.responseStream};`); - printer.printIndentedLn(`readonly requestType: typeof ${method.requestType};`); - printer.printIndentedLn(`readonly responseType: typeof ${method.responseType};`); - printer.printLn(`};`); - printer.printEmptyLn(); - }); - - printer.printLn(`export class ${service.name} {`); - printer.printIndentedLn(`static readonly serviceName: string;`); - service.methods.forEach(method => { - printer.printIndentedLn(`static readonly ${method.nameAsPascalCase}: ${method.serviceName}${method.nameAsPascalCase};`); - }); - printer.printLn(`}`); - printer.printEmptyLn(); - }); - - - - printer.printLn(`export type ServiceError = { message: string, code: number; metadata: grpc.Metadata }`); - printer.printLn(`export type Status = { details: string, code: number; metadata: grpc.Metadata }`); - printer.printEmptyLn(); - printer.printLn("interface UnaryResponse {"); - printer.printIndentedLn("cancel(): void;"); - printer.printLn("}"); - printer.printLn(`interface ResponseStream {`); - printer.printIndentedLn(`cancel(): void;`); - printer.printIndentedLn(`on(type: 'data', handler: (message: T) => void): ResponseStream;`); - printer.printIndentedLn(`on(type: 'end', handler: (status?: Status) => void): ResponseStream;`); - printer.printIndentedLn(`on(type: 'status', handler: (status: Status) => void): ResponseStream;`); - printer.printLn(`}`); - printer.printLn(`interface RequestStream {`); - printer.printIndentedLn(`write(message: T): RequestStream;`); - printer.printIndentedLn(`end(): void;`); - printer.printIndentedLn(`cancel(): void;`); - printer.printIndentedLn(`on(type: 'end', handler: (status?: Status) => void): RequestStream;`); - printer.printIndentedLn(`on(type: 'status', handler: (status: Status) => void): RequestStream;`); - printer.printLn(`}`); - printer.printLn(`interface BidirectionalStream {`); - printer.printIndentedLn(`write(message: ReqT): BidirectionalStream;`); - printer.printIndentedLn(`end(): void;`); - printer.printIndentedLn(`cancel(): void;`); - printer.printIndentedLn(`on(type: 'data', handler: (message: ResT) => void): BidirectionalStream;`); - printer.printIndentedLn(`on(type: 'end', handler: (status?: Status) => void): BidirectionalStream;`); - printer.printIndentedLn(`on(type: 'status', handler: (status: Status) => void): BidirectionalStream;`); - printer.printLn(`}`); - printer.printEmptyLn(); - - // Add a client stub that talks with the @improbable-eng/grpc-web library - serviceDescriptor.services - .forEach(service => { - printServiceStubTypes(printer, service); - printer.printEmptyLn(); - }); - - return printer.getOutput(); -} - -function generateJavaScript(fileDescriptor: FileDescriptorProto, exportMap: ExportMap): string { - const serviceDescriptor = new GrpcServiceDescriptor(fileDescriptor, exportMap); - const printer = new Printer(0); - - // Header. - printer.printLn(`// package: ${serviceDescriptor.packageName}`); - printer.printLn(`// file: ${serviceDescriptor.filename}`); - printer.printEmptyLn(); - - if (serviceDescriptor.services.length === 0) { - return printer.getOutput(); - } - - // Import Statements - serviceDescriptor.imports - .forEach(importDescriptor => { - printer.printLn(`var ${importDescriptor.namespace} = require("${importDescriptor.path}");`); - }); - printer.printLn(`var grpc = require("@improbable-eng/grpc-web").grpc;`); - printer.printEmptyLn(); - - // Services. - serviceDescriptor.services - .forEach(service => { - printer.printLn(`var ${service.name} = (function () {`); - printer.printIndentedLn(`function ${service.name}() {}`); - printer.printIndentedLn(`${service.name}.serviceName = "${service.qualifiedName}";`); - printer.printIndentedLn(`return ${service.name};`); - printer.printLn(`}());`); - printer.printEmptyLn(); - - service.methods - .forEach(method => { - printer.printLn(`${method.serviceName}.${method.nameAsPascalCase} = {`); - printer.printIndentedLn(`methodName: "${method.nameAsPascalCase}",`); - printer.printIndentedLn(`service: ${method.serviceName},`); - printer.printIndentedLn(`requestStream: ${method.requestStream},`); - printer.printIndentedLn(`responseStream: ${method.responseStream},`); - printer.printIndentedLn(`requestType: ${method.requestType},`); - printer.printIndentedLn(`responseType: ${method.responseType}`); - printer.printLn(`};`); - printer.printEmptyLn(); + // Services. + serviceDescriptor.services.forEach((service) => { + // Method Type Definitions + service.methods.forEach((method) => { + printer.printLn(`type ${method.serviceName}${method.nameAsPascalCase} = {`); + printer.printIndentedLn(`readonly methodName: string;`); + printer.printIndentedLn(`readonly service: typeof ${method.serviceName};`); + printer.printIndentedLn(`readonly requestStream: ${method.requestStream};`); + printer.printIndentedLn(`readonly responseStream: ${method.responseStream};`); + printer.printIndentedLn( + `readonly requestType: typeof ${method.requestType};` + ); + printer.printIndentedLn( + `readonly responseType: typeof ${method.responseType};` + ); + printer.printLn(`};`); + printer.printEmptyLn(); }); - printer.printLn(`exports.${service.name} = ${service.name};`); - printer.printEmptyLn(); - // Add a client stub that talks with the @improbable-eng/grpc-web library - printServiceStub(printer, service); - - printer.printEmptyLn(); + printer.printLn(`export class ${service.name} {`); + printer.printIndentedLn(`static readonly serviceName: string;`); + service.methods.forEach((method) => { + printer.printIndentedLn( + `static readonly ${method.nameAsPascalCase}: ${method.serviceName}${method.nameAsPascalCase};` + ); + }); + printer.printLn(`}`); + printer.printEmptyLn(); }); - return printer.getOutput(); + printer.printLn( + `export type ServiceError = { message: string, code: number; metadata: grpc.Metadata }` + ); + printer.printLn( + `export type Status = { details: string, code: number; metadata: grpc.Metadata }` + ); + printer.printEmptyLn(); + printer.printLn('interface UnaryResponse {'); + printer.printIndentedLn('cancel(): void;'); + printer.printLn('}'); + printer.printLn(`interface ResponseStream {`); + printer.printIndentedLn(`cancel(): void;`); + printer.printIndentedLn( + `on(type: 'data', handler: (message: T) => void): ResponseStream;` + ); + printer.printIndentedLn( + `on(type: 'end', handler: (status?: Status) => void): ResponseStream;` + ); + printer.printIndentedLn( + `on(type: 'status', handler: (status: Status) => void): ResponseStream;` + ); + printer.printLn(`}`); + printer.printLn(`interface RequestStream {`); + printer.printIndentedLn(`write(message: T): RequestStream;`); + printer.printIndentedLn(`end(): void;`); + printer.printIndentedLn(`cancel(): void;`); + printer.printIndentedLn( + `on(type: 'end', handler: (status?: Status) => void): RequestStream;` + ); + printer.printIndentedLn( + `on(type: 'status', handler: (status: Status) => void): RequestStream;` + ); + printer.printLn(`}`); + printer.printLn(`interface BidirectionalStream {`); + printer.printIndentedLn(`write(message: ReqT): BidirectionalStream;`); + printer.printIndentedLn(`end(): void;`); + printer.printIndentedLn(`cancel(): void;`); + printer.printIndentedLn( + `on(type: 'data', handler: (message: ResT) => void): BidirectionalStream;` + ); + printer.printIndentedLn( + `on(type: 'end', handler: (status?: Status) => void): BidirectionalStream;` + ); + printer.printIndentedLn( + `on(type: 'status', handler: (status: Status) => void): BidirectionalStream;` + ); + printer.printLn(`}`); + printer.printEmptyLn(); + + // Add a client stub that talks with the @improbable-eng/grpc-web library + serviceDescriptor.services.forEach((service) => { + printServiceStubTypes(printer, service); + printer.printEmptyLn(); + }); + + return printer.getOutput(); +} + +function generateJavaScript( + fileDescriptor: FileDescriptorProto, + exportMap: ExportMap +): string { + const serviceDescriptor = new GrpcServiceDescriptor(fileDescriptor, exportMap); + const printer = new Printer(0); + + // Header. + printer.printLn(`// package: ${serviceDescriptor.packageName}`); + printer.printLn(`// file: ${serviceDescriptor.filename}`); + printer.printEmptyLn(); + + if (serviceDescriptor.services.length === 0) { + return printer.getOutput(); + } + + // Import Statements + serviceDescriptor.imports.forEach((importDescriptor) => { + printer.printLn( + `var ${importDescriptor.namespace} = require("${importDescriptor.path}");` + ); + }); + printer.printLn(`var grpc = require("@improbable-eng/grpc-web").grpc;`); + printer.printEmptyLn(); + + // Services. + serviceDescriptor.services.forEach((service) => { + printer.printLn(`var ${service.name} = (function () {`); + printer.printIndentedLn(`function ${service.name}() {}`); + printer.printIndentedLn( + `${service.name}.serviceName = "${service.qualifiedName}";` + ); + printer.printIndentedLn(`return ${service.name};`); + printer.printLn(`}());`); + printer.printEmptyLn(); + + service.methods.forEach((method) => { + printer.printLn(`${method.serviceName}.${method.nameAsPascalCase} = {`); + printer.printIndentedLn(`methodName: "${method.nameAsPascalCase}",`); + printer.printIndentedLn(`service: ${method.serviceName},`); + printer.printIndentedLn(`requestStream: ${method.requestStream},`); + printer.printIndentedLn(`responseStream: ${method.responseStream},`); + printer.printIndentedLn(`requestType: ${method.requestType},`); + printer.printIndentedLn(`responseType: ${method.responseType}`); + printer.printLn(`};`); + printer.printEmptyLn(); + }); + printer.printLn(`exports.${service.name} = ${service.name};`); + printer.printEmptyLn(); + + // Add a client stub that talks with the @improbable-eng/grpc-web library + printServiceStub(printer, service); + + printer.printEmptyLn(); + }); + + return printer.getOutput(); } function printServiceStub(methodPrinter: Printer, service: RPCDescriptor) { - const printer = new CodePrinter(0, methodPrinter); + const printer = new CodePrinter(0, methodPrinter); - printer - .printLn(`function ${service.name}Client(serviceHost, options) {`) - .indent().printLn(`this.serviceHost = serviceHost;`) - .printLn(`this.options = options || {};`) - .dedent().printLn(`}`) - .printEmptyLn(); + printer + .printLn(`function ${service.name}Client(serviceHost, options) {`) + .indent() + .printLn(`this.serviceHost = serviceHost;`) + .printLn(`this.options = options || {};`) + .dedent() + .printLn(`}`) + .printEmptyLn(); - service.methods.forEach((method: RPCMethodDescriptor) => { - if (method.requestStream && method.responseStream) { - printBidirectionalStubMethod(printer, method); - } else if (method.requestStream) { - printClientStreamStubMethod(printer, method); - } else if (method.responseStream) { - printServerStreamStubMethod(printer, method); - } else { - printUnaryStubMethod(printer, method); - } - printer.printEmptyLn(); - }); - printer.printLn(`exports.${service.name}Client = ${service.name}Client;`); + service.methods.forEach((method: RPCMethodDescriptor) => { + if (method.requestStream && method.responseStream) { + printBidirectionalStubMethod(printer, method); + } else if (method.requestStream) { + printClientStreamStubMethod(printer, method); + } else if (method.responseStream) { + printServerStreamStubMethod(printer, method); + } else { + printUnaryStubMethod(printer, method); + } + printer.printEmptyLn(); + }); + printer.printLn(`exports.${service.name}Client = ${service.name}Client;`); } function printUnaryStubMethod(printer: CodePrinter, method: RPCMethodDescriptor) { - printer - .printLn(`${method.serviceName}Client.prototype.${method.nameAsCamelCase} = function ${method.functionName}(requestMessage, metadata, callback) {`) - .indent().printLn(`if (arguments.length === 2) {`) - .indent().printLn(`callback = arguments[1];`) - .dedent().printLn("}") - .printLn(`var client = grpc.unary(${method.serviceName}.${method.nameAsPascalCase}, {`) - .indent().printLn(`request: requestMessage,`) - .printLn(`host: this.serviceHost,`) - .printLn(`metadata: metadata,`) - .printLn(`transport: this.options.transport,`) - .printLn(`debug: this.options.debug,`) - .printLn(`onEnd: function (response) {`) - .indent().printLn(`if (callback) {`) - .indent().printLn(`if (response.status !== grpc.Code.OK) {`) - .indent().printLn(`var err = new Error(response.statusMessage);`) - .printLn(`err.code = response.status;`) - .printLn(`err.metadata = response.trailers;`) - .printLn(`callback(err, null);`) - .dedent().printLn(`} else {`) - .indent().printLn(`callback(null, response.message);`) - .dedent().printLn(`}`) - .dedent().printLn(`}`) - .dedent().printLn(`}`) - .dedent().printLn(`});`) - .printLn(`return {`) - .indent().printLn(`cancel: function () {`) - .indent().printLn(`callback = null;`) - .printLn(`client.close();`) - .dedent().printLn(`}`) - .dedent().printLn(`};`) - .dedent().printLn(`};`); + printer + .printLn( + `${method.serviceName}Client.prototype.${method.nameAsCamelCase} = function ${method.functionName}(requestMessage, metadata, callback) {` + ) + .indent() + .printLn(`if (arguments.length === 2) {`) + .indent() + .printLn(`callback = arguments[1];`) + .dedent() + .printLn('}') + .printLn( + `var client = grpc.unary(${method.serviceName}.${method.nameAsPascalCase}, {` + ) + .indent() + .printLn(`request: requestMessage,`) + .printLn(`host: this.serviceHost,`) + .printLn(`metadata: metadata,`) + .printLn(`transport: this.options.transport,`) + .printLn(`debug: this.options.debug,`) + .printLn(`onEnd: function (response) {`) + .indent() + .printLn(`if (callback) {`) + .indent() + .printLn(`if (response.status !== grpc.Code.OK) {`) + .indent() + .printLn(`var err = new Error(response.statusMessage);`) + .printLn(`err.code = response.status;`) + .printLn(`err.metadata = response.trailers;`) + .printLn(`callback(err, null);`) + .dedent() + .printLn(`} else {`) + .indent() + .printLn(`callback(null, response.message);`) + .dedent() + .printLn(`}`) + .dedent() + .printLn(`}`) + .dedent() + .printLn(`}`) + .dedent() + .printLn(`});`) + .printLn(`return {`) + .indent() + .printLn(`cancel: function () {`) + .indent() + .printLn(`callback = null;`) + .printLn(`client.close();`) + .dedent() + .printLn(`}`) + .dedent() + .printLn(`};`) + .dedent() + .printLn(`};`); } function printServerStreamStubMethod(printer: CodePrinter, method: RPCMethodDescriptor) { - printer - .printLn(`${method.serviceName}Client.prototype.${method.nameAsCamelCase} = function ${method.functionName}(requestMessage, metadata) {`) - .indent().printLn(`var listeners = {`) - .indent().printLn(`data: [],`) - .printLn(`end: [],`) - .printLn(`status: []`) - .dedent().printLn(`};`) - .printLn(`var client = grpc.invoke(${method.serviceName}.${method.nameAsPascalCase}, {`) - .indent().printLn(`request: requestMessage,`) - .printLn(`host: this.serviceHost,`) - .printLn(`metadata: metadata,`) - .printLn(`transport: this.options.transport,`) - .printLn(`debug: this.options.debug,`) - .printLn(`onMessage: function (responseMessage) {`) - .indent().printLn(`listeners.data.forEach(function (handler) {`) - .indent().printLn(`handler(responseMessage);`) - .dedent().printLn(`});`) - .dedent().printLn(`},`) - .printLn(`onEnd: function (status, statusMessage, trailers) {`) - .indent().printLn(`listeners.status.forEach(function (handler) {`) - .indent().printLn(`handler({ code: status, details: statusMessage, metadata: trailers });`) - .dedent().printLn(`});`) - .printLn(`listeners.end.forEach(function (handler) {`) - .indent().printLn(`handler({ code: status, details: statusMessage, metadata: trailers });`) - .dedent().printLn(`});`) - .printLn(`listeners = null;`) - .dedent().printLn(`}`) - .dedent().printLn(`});`) - .printLn(`return {`) - .indent().printLn(`on: function (type, handler) {`) - .indent().printLn(`listeners[type].push(handler);`) - .printLn(`return this;`) - .dedent().printLn(`},`) - .printLn(`cancel: function () {`) - .indent().printLn(`listeners = null;`) - .printLn(`client.close();`) - .dedent().printLn(`}`) - .dedent().printLn(`};`) - .dedent().printLn(`};`); + printer + .printLn( + `${method.serviceName}Client.prototype.${method.nameAsCamelCase} = function ${method.functionName}(requestMessage, metadata) {` + ) + .indent() + .printLn(`var listeners = {`) + .indent() + .printLn(`data: [],`) + .printLn(`end: [],`) + .printLn(`status: []`) + .dedent() + .printLn(`};`) + .printLn( + `var client = grpc.invoke(${method.serviceName}.${method.nameAsPascalCase}, {` + ) + .indent() + .printLn(`request: requestMessage,`) + .printLn(`host: this.serviceHost,`) + .printLn(`metadata: metadata,`) + .printLn(`transport: this.options.transport,`) + .printLn(`debug: this.options.debug,`) + .printLn(`onMessage: function (responseMessage) {`) + .indent() + .printLn(`listeners.data.forEach(function (handler) {`) + .indent() + .printLn(`handler(responseMessage);`) + .dedent() + .printLn(`});`) + .dedent() + .printLn(`},`) + .printLn(`onEnd: function (status, statusMessage, trailers) {`) + .indent() + .printLn(`listeners.status.forEach(function (handler) {`) + .indent() + .printLn(`handler({ code: status, details: statusMessage, metadata: trailers });`) + .dedent() + .printLn(`});`) + .printLn(`listeners.end.forEach(function (handler) {`) + .indent() + .printLn(`handler({ code: status, details: statusMessage, metadata: trailers });`) + .dedent() + .printLn(`});`) + .printLn(`listeners = null;`) + .dedent() + .printLn(`}`) + .dedent() + .printLn(`});`) + .printLn(`return {`) + .indent() + .printLn(`on: function (type, handler) {`) + .indent() + .printLn(`listeners[type].push(handler);`) + .printLn(`return this;`) + .dedent() + .printLn(`},`) + .printLn(`cancel: function () {`) + .indent() + .printLn(`listeners = null;`) + .printLn(`client.close();`) + .dedent() + .printLn(`}`) + .dedent() + .printLn(`};`) + .dedent() + .printLn(`};`); } function printClientStreamStubMethod(printer: CodePrinter, method: RPCMethodDescriptor) { - printer - .printLn(`${method.serviceName}Client.prototype.${method.nameAsCamelCase} = function ${method.functionName}(metadata) {`) - .indent().printLn(`var listeners = {`) - .indent().printLn(`end: [],`) - .printLn(`status: []`) - .dedent().printLn(`};`) - .printLn(`var client = grpc.client(${method.serviceName}.${method.nameAsPascalCase}, {`) - .indent().printLn(`host: this.serviceHost,`) - .printLn(`metadata: metadata,`) - .printLn(`transport: this.options.transport`) - .dedent().printLn(`});`) - .printLn(`client.onEnd(function (status, statusMessage, trailers) {`) - .indent().printLn(`listeners.status.forEach(function (handler) {`) - .indent().printLn(`handler({ code: status, details: statusMessage, metadata: trailers });`) - .dedent().printLn(`});`) - .printLn(`listeners.end.forEach(function (handler) {`) - .indent().printLn(`handler({ code: status, details: statusMessage, metadata: trailers });`) - .dedent().printLn(`});`) - .printLn(`listeners = null;`) - .dedent().printLn(`});`) - .printLn(`return {`) - .indent().printLn(`on: function (type, handler) {`) - .indent().printLn(`listeners[type].push(handler);`) - .printLn(`return this;`) - .dedent().printLn(`},`) - .printLn(`write: function (requestMessage) {`) - .indent().printLn(`if (!client.started) {`) - .indent().printLn(`client.start(metadata);`) - .dedent().printLn(`}`) - .printLn(`client.send(requestMessage);`) - .printLn(`return this;`) - .dedent().printLn(`},`) - .printLn(`end: function () {`) - .indent().printLn(`client.finishSend();`) - .dedent().printLn(`},`) - .printLn(`cancel: function () {`) - .indent().printLn(`listeners = null;`) - .printLn(`client.close();`) - .dedent().printLn(`}`) - .dedent().printLn(`};`) - .dedent().printLn(`};`); + printer + .printLn( + `${method.serviceName}Client.prototype.${method.nameAsCamelCase} = function ${method.functionName}(metadata) {` + ) + .indent() + .printLn(`var listeners = {`) + .indent() + .printLn(`end: [],`) + .printLn(`status: []`) + .dedent() + .printLn(`};`) + .printLn( + `var client = grpc.client(${method.serviceName}.${method.nameAsPascalCase}, {` + ) + .indent() + .printLn(`host: this.serviceHost,`) + .printLn(`metadata: metadata,`) + .printLn(`transport: this.options.transport`) + .dedent() + .printLn(`});`) + .printLn(`client.onEnd(function (status, statusMessage, trailers) {`) + .indent() + .printLn(`listeners.status.forEach(function (handler) {`) + .indent() + .printLn(`handler({ code: status, details: statusMessage, metadata: trailers });`) + .dedent() + .printLn(`});`) + .printLn(`listeners.end.forEach(function (handler) {`) + .indent() + .printLn(`handler({ code: status, details: statusMessage, metadata: trailers });`) + .dedent() + .printLn(`});`) + .printLn(`listeners = null;`) + .dedent() + .printLn(`});`) + .printLn(`return {`) + .indent() + .printLn(`on: function (type, handler) {`) + .indent() + .printLn(`listeners[type].push(handler);`) + .printLn(`return this;`) + .dedent() + .printLn(`},`) + .printLn(`write: function (requestMessage) {`) + .indent() + .printLn(`if (!client.started) {`) + .indent() + .printLn(`client.start(metadata);`) + .dedent() + .printLn(`}`) + .printLn(`client.send(requestMessage);`) + .printLn(`return this;`) + .dedent() + .printLn(`},`) + .printLn(`end: function () {`) + .indent() + .printLn(`client.finishSend();`) + .dedent() + .printLn(`},`) + .printLn(`cancel: function () {`) + .indent() + .printLn(`listeners = null;`) + .printLn(`client.close();`) + .dedent() + .printLn(`}`) + .dedent() + .printLn(`};`) + .dedent() + .printLn(`};`); } function printBidirectionalStubMethod(printer: CodePrinter, method: RPCMethodDescriptor) { - printer - .printLn(`${method.serviceName}Client.prototype.${method.nameAsCamelCase} = function ${method.functionName}(metadata) {`) - .indent().printLn(`var listeners = {`) - .indent().printLn(`data: [],`) - .printLn(`end: [],`) - .printLn(`status: []`) - .dedent().printLn(`};`) - .printLn(`var client = grpc.client(${method.serviceName}.${method.nameAsPascalCase}, {`) - .indent().printLn(`host: this.serviceHost,`) - .printLn(`metadata: metadata,`) - .printLn(`transport: this.options.transport`) - .dedent().printLn(`});`) - .printLn(`client.onEnd(function (status, statusMessage, trailers) {`) - .indent().printLn(`listeners.status.forEach(function (handler) {`) - .indent().printLn(`handler({ code: status, details: statusMessage, metadata: trailers });`) - .dedent().printLn(`});`) - .printLn(`listeners.end.forEach(function (handler) {`) - .indent().printLn(`handler({ code: status, details: statusMessage, metadata: trailers });`) - .dedent().printLn(`});`) - .printLn(`listeners = null;`) - .dedent().printLn(`});`) - .printLn(`client.onMessage(function (message) {`) - .indent().printLn(`listeners.data.forEach(function (handler) {`) - .indent().printLn(`handler(message);`) - .dedent().printLn(`})`) - .dedent().printLn(`});`) - .printLn(`client.start(metadata);`) - .printLn(`return {`) - .indent().printLn(`on: function (type, handler) {`) - .indent().printLn(`listeners[type].push(handler);`) - .printLn(`return this;`) - .dedent().printLn(`},`) - .printLn(`write: function (requestMessage) {`) - .indent().printLn(`client.send(requestMessage);`) - .printLn(`return this;`) - .dedent().printLn(`},`) - .printLn(`end: function () {`) - .indent().printLn(`client.finishSend();`) - .dedent().printLn(`},`) - .printLn(`cancel: function () {`) - .indent().printLn(`listeners = null;`) - .printLn(`client.close();`) - .dedent().printLn(`}`) - .dedent().printLn(`};`) - .dedent().printLn(`};`); + printer + .printLn( + `${method.serviceName}Client.prototype.${method.nameAsCamelCase} = function ${method.functionName}(metadata) {` + ) + .indent() + .printLn(`var listeners = {`) + .indent() + .printLn(`data: [],`) + .printLn(`end: [],`) + .printLn(`status: []`) + .dedent() + .printLn(`};`) + .printLn( + `var client = grpc.client(${method.serviceName}.${method.nameAsPascalCase}, {` + ) + .indent() + .printLn(`host: this.serviceHost,`) + .printLn(`metadata: metadata,`) + .printLn(`transport: this.options.transport`) + .dedent() + .printLn(`});`) + .printLn(`client.onEnd(function (status, statusMessage, trailers) {`) + .indent() + .printLn(`listeners.status.forEach(function (handler) {`) + .indent() + .printLn(`handler({ code: status, details: statusMessage, metadata: trailers });`) + .dedent() + .printLn(`});`) + .printLn(`listeners.end.forEach(function (handler) {`) + .indent() + .printLn(`handler({ code: status, details: statusMessage, metadata: trailers });`) + .dedent() + .printLn(`});`) + .printLn(`listeners = null;`) + .dedent() + .printLn(`});`) + .printLn(`client.onMessage(function (message) {`) + .indent() + .printLn(`listeners.data.forEach(function (handler) {`) + .indent() + .printLn(`handler(message);`) + .dedent() + .printLn(`})`) + .dedent() + .printLn(`});`) + .printLn(`client.start(metadata);`) + .printLn(`return {`) + .indent() + .printLn(`on: function (type, handler) {`) + .indent() + .printLn(`listeners[type].push(handler);`) + .printLn(`return this;`) + .dedent() + .printLn(`},`) + .printLn(`write: function (requestMessage) {`) + .indent() + .printLn(`client.send(requestMessage);`) + .printLn(`return this;`) + .dedent() + .printLn(`},`) + .printLn(`end: function () {`) + .indent() + .printLn(`client.finishSend();`) + .dedent() + .printLn(`},`) + .printLn(`cancel: function () {`) + .indent() + .printLn(`listeners = null;`) + .printLn(`client.close();`) + .dedent() + .printLn(`}`) + .dedent() + .printLn(`};`) + .dedent() + .printLn(`};`); } function printServiceStubTypes(methodPrinter: Printer, service: RPCDescriptor) { - const printer = new CodePrinter(0, methodPrinter); + const printer = new CodePrinter(0, methodPrinter); - printer - .printLn(`export class ${service.name}Client {`) - .indent().printLn(`readonly serviceHost: string;`) + printer + .printLn(`export class ${service.name}Client {`) + .indent() + .printLn(`readonly serviceHost: string;`) .printEmptyLn() - .printLn(`constructor(serviceHost: string, options?: grpc.RpcOptions);`); + .printLn(`constructor(serviceHost: string, options?: grpc.RpcOptions);`); - service.methods.forEach((method: RPCMethodDescriptor) => { - if (method.requestStream && method.responseStream) { - printBidirectionalStubMethodTypes(printer, method); - } else if (method.requestStream) { - printClientStreamStubMethodTypes(printer, method); - } else if (method.responseStream) { - printServerStreamStubMethodTypes(printer, method); - } else { - printUnaryStubMethodTypes(printer, method); - } - }); - printer.dedent().printLn("}"); + service.methods.forEach((method: RPCMethodDescriptor) => { + if (method.requestStream && method.responseStream) { + printBidirectionalStubMethodTypes(printer, method); + } else if (method.requestStream) { + printClientStreamStubMethodTypes(printer, method); + } else if (method.responseStream) { + printServerStreamStubMethodTypes(printer, method); + } else { + printUnaryStubMethodTypes(printer, method); + } + }); + printer.dedent().printLn('}'); } function printUnaryStubMethodTypes(printer: CodePrinter, method: RPCMethodDescriptor) { - printer - .printLn(`${method.nameAsCamelCase}(`) - .indent().printLn(`requestMessage: ${method.requestType},`) - .printLn(`metadata: grpc.Metadata,`) - .printLn(`callback: (error: ServiceError|null, responseMessage: ${method.responseType}|null) => void`) - .dedent().printLn(`): UnaryResponse;`) - .printLn(`${method.nameAsCamelCase}(`) - .indent().printLn(`requestMessage: ${method.requestType},`) - .printLn(`callback: (error: ServiceError|null, responseMessage: ${method.responseType}|null) => void`) - .dedent().printLn(`): UnaryResponse;`); + printer + .printLn(`${method.nameAsCamelCase}(`) + .indent() + .printLn(`requestMessage: ${method.requestType},`) + .printLn(`metadata: grpc.Metadata,`) + .printLn( + `callback: (error: ServiceError|null, responseMessage: ${method.responseType}|null) => void` + ) + .dedent() + .printLn(`): UnaryResponse;`) + .printLn(`${method.nameAsCamelCase}(`) + .indent() + .printLn(`requestMessage: ${method.requestType},`) + .printLn( + `callback: (error: ServiceError|null, responseMessage: ${method.responseType}|null) => void` + ) + .dedent() + .printLn(`): UnaryResponse;`); } -function printServerStreamStubMethodTypes(printer: CodePrinter, method: RPCMethodDescriptor) { - printer.printLn(`${method.nameAsCamelCase}(requestMessage: ${method.requestType}, metadata?: grpc.Metadata): ResponseStream<${method.responseType}>;`); +function printServerStreamStubMethodTypes( + printer: CodePrinter, + method: RPCMethodDescriptor +) { + printer.printLn( + `${method.nameAsCamelCase}(requestMessage: ${method.requestType}, metadata?: grpc.Metadata): ResponseStream<${method.responseType}>;` + ); } -function printClientStreamStubMethodTypes(printer: CodePrinter, method: RPCMethodDescriptor) { - printer.printLn(`${method.nameAsCamelCase}(metadata?: grpc.Metadata): RequestStream<${method.requestType}>;`); +function printClientStreamStubMethodTypes( + printer: CodePrinter, + method: RPCMethodDescriptor +) { + printer.printLn( + `${method.nameAsCamelCase}(metadata?: grpc.Metadata): RequestStream<${method.requestType}>;` + ); } -function printBidirectionalStubMethodTypes(printer: CodePrinter, method: RPCMethodDescriptor) { - printer.printLn(`${method.nameAsCamelCase}(metadata?: grpc.Metadata): BidirectionalStream<${method.requestType}, ${method.responseType}>;`); +function printBidirectionalStubMethodTypes( + printer: CodePrinter, + method: RPCMethodDescriptor +) { + printer.printLn( + `${method.nameAsCamelCase}(metadata?: grpc.Metadata): BidirectionalStream<${method.requestType}, ${method.responseType}>;` + ); } diff --git a/src/ts-protoc-gen/ts/FieldTypes.ts b/src/ts-protoc-gen/ts/FieldTypes.ts index 7ccd739..897cd24 100644 --- a/src/ts-protoc-gen/ts/FieldTypes.ts +++ b/src/ts-protoc-gen/ts/FieldTypes.ts @@ -1,59 +1,64 @@ -import {filePathToPseudoNamespace, withinNamespaceFromExportEntry} from "../util"; -import {ExportMap} from "../ExportMap"; -import {FieldDescriptorProto} from "google-protobuf/google/protobuf/descriptor_pb"; +import { filePathToPseudoNamespace, withinNamespaceFromExportEntry } from '../util'; +import { ExportMap } from '../ExportMap'; +import { FieldDescriptorProto } from 'google-protobuf/google/protobuf/descriptor_pb'; export const MESSAGE_TYPE = 11; export const BYTES_TYPE = 12; export const ENUM_TYPE = 14; -const TypeNumToTypeString: {[key: number]: string} = {}; -TypeNumToTypeString[1] = "number"; // TYPE_DOUBLE -TypeNumToTypeString[2] = "number"; // TYPE_FLOAT -TypeNumToTypeString[3] = "number"; // TYPE_INT64 -TypeNumToTypeString[4] = "number"; // TYPE_UINT64 -TypeNumToTypeString[5] = "number"; // TYPE_INT32 -TypeNumToTypeString[6] = "number"; // TYPE_FIXED64 -TypeNumToTypeString[7] = "number"; // TYPE_FIXED32 -TypeNumToTypeString[8] = "boolean"; // TYPE_BOOL -TypeNumToTypeString[9] = "string"; // TYPE_STRING -TypeNumToTypeString[10] = "Object"; // TYPE_GROUP -TypeNumToTypeString[MESSAGE_TYPE] = "Object"; // TYPE_MESSAGE - Length-delimited aggregate. -TypeNumToTypeString[BYTES_TYPE] = "Uint8Array"; // TYPE_BYTES -TypeNumToTypeString[13] = "number"; // TYPE_UINT32 -TypeNumToTypeString[ENUM_TYPE] = "number"; // TYPE_ENUM -TypeNumToTypeString[15] = "number"; // TYPE_SFIXED32 -TypeNumToTypeString[16] = "number"; // TYPE_SFIXED64 -TypeNumToTypeString[17] = "number"; // TYPE_SINT32 - Uses ZigZag encoding. -TypeNumToTypeString[18] = "number"; // TYPE_SINT64 - Uses ZigZag encoding. +const TypeNumToTypeString: { [key: number]: string } = {}; +TypeNumToTypeString[1] = 'number'; // TYPE_DOUBLE +TypeNumToTypeString[2] = 'number'; // TYPE_FLOAT +TypeNumToTypeString[3] = 'number'; // TYPE_INT64 +TypeNumToTypeString[4] = 'number'; // TYPE_UINT64 +TypeNumToTypeString[5] = 'number'; // TYPE_INT32 +TypeNumToTypeString[6] = 'number'; // TYPE_FIXED64 +TypeNumToTypeString[7] = 'number'; // TYPE_FIXED32 +TypeNumToTypeString[8] = 'boolean'; // TYPE_BOOL +TypeNumToTypeString[9] = 'string'; // TYPE_STRING +TypeNumToTypeString[10] = 'Object'; // TYPE_GROUP +TypeNumToTypeString[MESSAGE_TYPE] = 'Object'; // TYPE_MESSAGE - Length-delimited aggregate. +TypeNumToTypeString[BYTES_TYPE] = 'Uint8Array'; // TYPE_BYTES +TypeNumToTypeString[13] = 'number'; // TYPE_UINT32 +TypeNumToTypeString[ENUM_TYPE] = 'number'; // TYPE_ENUM +TypeNumToTypeString[15] = 'number'; // TYPE_SFIXED32 +TypeNumToTypeString[16] = 'number'; // TYPE_SFIXED64 +TypeNumToTypeString[17] = 'number'; // TYPE_SINT32 - Uses ZigZag encoding. +TypeNumToTypeString[18] = 'number'; // TYPE_SINT64 - Uses ZigZag encoding. export function getTypeName(fieldTypeNum: number): string { - return TypeNumToTypeString[fieldTypeNum]; + return TypeNumToTypeString[fieldTypeNum]; } -export function getFieldType(type: FieldDescriptorProto.Type, typeName: string, currentFileName: string, exportMap: ExportMap): string { - if (type === MESSAGE_TYPE) { - const fromExport = exportMap.getMessage(typeName); - if (!fromExport) { - throw new Error("Could not getFieldType for message: " + typeName); - } - const withinNamespace = withinNamespaceFromExportEntry(typeName, fromExport); - if (fromExport.fileName === currentFileName) { - return withinNamespace; +export function getFieldType( + type: FieldDescriptorProto.Type, + typeName: string, + currentFileName: string, + exportMap: ExportMap +): string { + if (type === MESSAGE_TYPE) { + const fromExport = exportMap.getMessage(typeName); + if (!fromExport) { + throw new Error('Could not getFieldType for message: ' + typeName); + } + const withinNamespace = withinNamespaceFromExportEntry(typeName, fromExport); + if (fromExport.fileName === currentFileName) { + return withinNamespace; + } else { + return filePathToPseudoNamespace(fromExport.fileName) + '.' + withinNamespace; + } + } else if (type === ENUM_TYPE) { + const fromExport = exportMap.getEnum(typeName); + if (!fromExport) { + throw new Error('Could not getFieldType for enum: ' + typeName); + } + const withinNamespace = withinNamespaceFromExportEntry(typeName, fromExport); + if (fromExport.fileName === currentFileName) { + return `${withinNamespace}Map`; + } else { + return filePathToPseudoNamespace(fromExport.fileName) + '.' + withinNamespace; + } } else { - return filePathToPseudoNamespace(fromExport.fileName) + "." + withinNamespace; + return TypeNumToTypeString[type]; } - } else if (type === ENUM_TYPE) { - const fromExport = exportMap.getEnum(typeName); - if (!fromExport) { - throw new Error("Could not getFieldType for enum: " + typeName); - } - const withinNamespace = withinNamespaceFromExportEntry(typeName, fromExport); - if (fromExport.fileName === currentFileName) { - return `${withinNamespace}Map`; - } else { - return filePathToPseudoNamespace(fromExport.fileName) + "." + withinNamespace; - } - } else { - return TypeNumToTypeString[type]; - } } diff --git a/src/ts-protoc-gen/ts/enum.ts b/src/ts-protoc-gen/ts/enum.ts index 97b6fd1..18a827f 100644 --- a/src/ts-protoc-gen/ts/enum.ts +++ b/src/ts-protoc-gen/ts/enum.ts @@ -1,16 +1,18 @@ -import {EnumDescriptorProto} from "google-protobuf/google/protobuf/descriptor_pb"; -import {Printer} from "../Printer"; +import { EnumDescriptorProto } from 'google-protobuf/google/protobuf/descriptor_pb'; +import { Printer } from '../Printer'; export function printEnum(enumDescriptor: EnumDescriptorProto, indentLevel: number) { - const printer = new Printer(indentLevel); - const enumInterfaceName = `${enumDescriptor.getName()}Map`; - printer.printEmptyLn(); - printer.printLn(`export interface ${enumInterfaceName} {`); - enumDescriptor.getValueList().forEach(value => { - printer.printIndentedLn(`${value.getName().toUpperCase()}: ${value.getNumber()};`); - }); - printer.printLn(`}`); - printer.printEmptyLn(); - printer.printLn(`export const ${enumDescriptor.getName()}: ${enumInterfaceName};`); - return printer.getOutput(); + const printer = new Printer(indentLevel); + const enumInterfaceName = `${enumDescriptor.getName()}Map`; + printer.printEmptyLn(); + printer.printLn(`export interface ${enumInterfaceName} {`); + enumDescriptor.getValueList().forEach((value) => { + printer.printIndentedLn( + `${value.getName().toUpperCase()}: ${value.getNumber()};` + ); + }); + printer.printLn(`}`); + printer.printEmptyLn(); + printer.printLn(`export const ${enumDescriptor.getName()}: ${enumInterfaceName};`); + return printer.getOutput(); } diff --git a/src/ts-protoc-gen/ts/extensions.ts b/src/ts-protoc-gen/ts/extensions.ts index fbe2bb2..bd5a02c 100644 --- a/src/ts-protoc-gen/ts/extensions.ts +++ b/src/ts-protoc-gen/ts/extensions.ts @@ -1,14 +1,26 @@ -import {Printer} from "../Printer"; -import {ExportMap} from "../ExportMap"; -import {FieldDescriptorProto} from "google-protobuf/google/protobuf/descriptor_pb"; -import {snakeToCamel} from "../util"; -import {getFieldType} from "./FieldTypes"; +import { Printer } from '../Printer'; +import { ExportMap } from '../ExportMap'; +import { FieldDescriptorProto } from 'google-protobuf/google/protobuf/descriptor_pb'; +import { snakeToCamel } from '../util'; +import { getFieldType } from './FieldTypes'; -export function printExtension(fileName: string, exportMap: ExportMap, extension: FieldDescriptorProto, indentLevel: number): string { - const printer = new Printer(indentLevel + 1); - printer.printEmptyLn(); - const extensionName = snakeToCamel(extension.getName()); - const fieldType = getFieldType(extension.getType(), extension.getTypeName().slice(1), fileName, exportMap); - printer.printLn(`export const ${extensionName}: jspb.ExtensionFieldInfo<${fieldType}>;`); - return printer.output; +export function printExtension( + fileName: string, + exportMap: ExportMap, + extension: FieldDescriptorProto, + indentLevel: number +): string { + const printer = new Printer(indentLevel + 1); + printer.printEmptyLn(); + const extensionName = snakeToCamel(extension.getName()); + const fieldType = getFieldType( + extension.getType(), + extension.getTypeName().slice(1), + fileName, + exportMap + ); + printer.printLn( + `export const ${extensionName}: jspb.ExtensionFieldInfo<${fieldType}>;` + ); + return printer.output; } diff --git a/src/ts-protoc-gen/ts/fileDescriptorTSD.ts b/src/ts-protoc-gen/ts/fileDescriptorTSD.ts index b824a7e..0e7e11a 100644 --- a/src/ts-protoc-gen/ts/fileDescriptorTSD.ts +++ b/src/ts-protoc-gen/ts/fileDescriptorTSD.ts @@ -1,49 +1,56 @@ -import {filePathToPseudoNamespace, replaceProtoSuffix, getPathToRoot} from "../util"; -import {ExportMap} from "../ExportMap"; -import {Printer} from "../Printer"; -import {FileDescriptorProto} from "google-protobuf/google/protobuf/descriptor_pb"; -import {WellKnownTypesMap} from "../WellKnown"; -import {printMessage} from "./message"; -import {printEnum} from "./enum"; -import {printExtension} from "./extensions"; +import { filePathToPseudoNamespace, replaceProtoSuffix, getPathToRoot } from '../util'; +import { ExportMap } from '../ExportMap'; +import { Printer } from '../Printer'; +import { FileDescriptorProto } from 'google-protobuf/google/protobuf/descriptor_pb'; +import { WellKnownTypesMap } from '../WellKnown'; +import { printMessage } from './message'; +import { printEnum } from './enum'; +import { printExtension } from './extensions'; -export function printFileDescriptorTSD(fileDescriptor: FileDescriptorProto, exportMap: ExportMap) { - const fileName = fileDescriptor.getName(); - const packageName = fileDescriptor.getPackage(); +export function printFileDescriptorTSD( + fileDescriptor: FileDescriptorProto, + exportMap: ExportMap +) { + const fileName = fileDescriptor.getName(); + const packageName = fileDescriptor.getPackage(); - const printer = new Printer(0); + const printer = new Printer(0); - printer.printLn(`// package: ${packageName}`); - printer.printLn(`// file: ${fileDescriptor.getName()}`); + printer.printLn(`// package: ${packageName}`); + printer.printLn(`// file: ${fileDescriptor.getName()}`); - const upToRoot = getPathToRoot(fileName); + const upToRoot = getPathToRoot(fileName); - printer.printEmptyLn(); - printer.printLn(`import * as jspb from "google-protobuf";`); + printer.printEmptyLn(); + printer.printLn(`import * as jspb from "google-protobuf";`); - fileDescriptor.getDependencyList().forEach((dependency: string) => { - const pseudoNamespace = filePathToPseudoNamespace(dependency); - if (dependency in WellKnownTypesMap) { - printer.printLn(`import * as ${pseudoNamespace} from "${WellKnownTypesMap[dependency]}";`); - } else { - const filePath = replaceProtoSuffix(dependency); - printer.printLn(`import * as ${pseudoNamespace} from "${upToRoot}${filePath}";`); - } - }); + fileDescriptor.getDependencyList().forEach((dependency: string) => { + const pseudoNamespace = filePathToPseudoNamespace(dependency); + if (dependency in WellKnownTypesMap) { + printer.printLn( + `import * as ${pseudoNamespace} from "${WellKnownTypesMap[dependency]}";` + ); + } else { + const filePath = replaceProtoSuffix(dependency); + printer.printLn( + `import * as ${pseudoNamespace} from "${upToRoot}${filePath}";` + ); + } + }); - fileDescriptor.getMessageTypeList().forEach(enumType => { - printer.print(printMessage(fileName, exportMap, enumType, 0, fileDescriptor)); - }); + fileDescriptor.getMessageTypeList().forEach((enumType) => { + printer.print(printMessage(fileName, exportMap, enumType, 0, fileDescriptor)); + }); - fileDescriptor.getExtensionList().forEach(extension => { - printer.print(printExtension(fileName, exportMap, extension, 0)); - }); + fileDescriptor.getExtensionList().forEach((extension) => { + printer.print(printExtension(fileName, exportMap, extension, 0)); + }); - fileDescriptor.getEnumTypeList().forEach(enumType => { - printer.print(printEnum(enumType, 0)); - }); + fileDescriptor.getEnumTypeList().forEach((enumType) => { + printer.print(printEnum(enumType, 0)); + }); - printer.printEmptyLn(); + printer.printEmptyLn(); - return printer.getOutput(); + return printer.getOutput(); } diff --git a/src/ts-protoc-gen/ts/message.ts b/src/ts-protoc-gen/ts/message.ts index d4fa11a..1930de6 100644 --- a/src/ts-protoc-gen/ts/message.ts +++ b/src/ts-protoc-gen/ts/message.ts @@ -1,235 +1,361 @@ import { - filePathToPseudoNamespace, snakeToCamel, uppercaseFirst, oneOfName, isProto2, - withinNamespaceFromExportEntry, normaliseFieldObjectName, stripPrefix -} from "../util"; -import {ExportMap} from "../ExportMap"; + filePathToPseudoNamespace, + snakeToCamel, + uppercaseFirst, + oneOfName, + isProto2, + withinNamespaceFromExportEntry, + normaliseFieldObjectName, + stripPrefix, +} from '../util'; +import { ExportMap } from '../ExportMap'; import { - FieldDescriptorProto, FileDescriptorProto, DescriptorProto, - FieldOptions -} from "google-protobuf/google/protobuf/descriptor_pb"; -import {MESSAGE_TYPE, BYTES_TYPE, ENUM_TYPE, getFieldType, getTypeName} from "./FieldTypes"; -import {Printer} from "../Printer"; -import {printEnum} from "./enum"; -import {printOneOfDecl} from "./oneof"; -import {printExtension} from "./extensions"; + FieldDescriptorProto, + FileDescriptorProto, + DescriptorProto, + FieldOptions, +} from 'google-protobuf/google/protobuf/descriptor_pb'; +import { + MESSAGE_TYPE, + BYTES_TYPE, + ENUM_TYPE, + getFieldType, + getTypeName, +} from './FieldTypes'; +import { Printer } from '../Printer'; +import { printEnum } from './enum'; +import { printOneOfDecl } from './oneof'; +import { printExtension } from './extensions'; import JSType = FieldOptions.JSType; -function hasFieldPresence(field: FieldDescriptorProto, fileDescriptor: FileDescriptorProto): boolean { - if (field.getLabel() === FieldDescriptorProto.Label.LABEL_REPEATED) { - return false; - } +function hasFieldPresence( + field: FieldDescriptorProto, + fileDescriptor: FileDescriptorProto +): boolean { + if (field.getLabel() === FieldDescriptorProto.Label.LABEL_REPEATED) { + return false; + } - if (field.hasOneofIndex()) { - return true; - } - - if (field.getType() === MESSAGE_TYPE) { - return true; - } - - if (isProto2(fileDescriptor)) { - return true; - } - - return false; -} - -export function printMessage(fileName: string, exportMap: ExportMap, messageDescriptor: DescriptorProto, indentLevel: number, fileDescriptor: FileDescriptorProto) { - const messageName = messageDescriptor.getName(); - const messageOptions = messageDescriptor.getOptions(); - if (messageOptions !== undefined && messageOptions.getMapEntry()) { - // this message type is the entry tuple for a map - don't output it - return ""; - } - - const objectTypeName = `AsObject`; - const toObjectType = new Printer(indentLevel + 1); - toObjectType.printLn(`export type ${objectTypeName} = {`); - - const printer = new Printer(indentLevel); - printer.printEmptyLn(); - printer.printLn(`export class ${messageName} extends jspb.Message {`); - - const oneOfGroups: Array> = []; - - messageDescriptor.getFieldList().forEach(field => { if (field.hasOneofIndex()) { - const oneOfIndex = field.getOneofIndex(); - let existing = oneOfGroups[oneOfIndex]; - if (existing === undefined) { - existing = []; - oneOfGroups[oneOfIndex] = existing; - } - existing.push(field); - } - const snakeCaseName = stripPrefix(field.getName().toLowerCase(), "_"); - const camelCaseName = snakeToCamel(snakeCaseName); - const withUppercase = uppercaseFirst(camelCaseName); - const type = field.getType(); - - let exportType; - const fullTypeName = field.getTypeName().slice(1); - if (type === MESSAGE_TYPE) { - const fieldMessageType = exportMap.getMessage(fullTypeName); - if (fieldMessageType === undefined) { - throw new Error("No message export for: " + fullTypeName); - } - if (fieldMessageType.messageOptions !== undefined && fieldMessageType.messageOptions.getMapEntry()) { - // This field is a map - const keyTuple = fieldMessageType.mapFieldOptions!.key; - const keyType = keyTuple[0]; - const keyTypeName = getFieldType(keyType, keyTuple[1], fileName, exportMap); - const valueTuple = fieldMessageType.mapFieldOptions!.value; - const valueType = valueTuple[0]; - let valueTypeName = getFieldType(valueType, valueTuple[1], fileName, exportMap); - if (valueType === BYTES_TYPE) { - valueTypeName = "Uint8Array | string"; - } - if (valueType === ENUM_TYPE) { - valueTypeName = `${valueTypeName}[keyof ${valueTypeName}]`; - } - printer.printIndentedLn(`get${withUppercase}Map(): jspb.Map<${keyTypeName}, ${valueTypeName}>;`); - printer.printIndentedLn(`clear${withUppercase}Map(): void;`); - toObjectType.printIndentedLn(`${camelCaseName}Map: Array<[${keyTypeName}${keyType === MESSAGE_TYPE ? ".AsObject" : ""}, ${valueTypeName}${valueType === MESSAGE_TYPE ? ".AsObject" : ""}]>,`); - return; - } - const withinNamespace = withinNamespaceFromExportEntry(fullTypeName, fieldMessageType); - if (fieldMessageType.fileName === fileName) { - exportType = withinNamespace; - } else { - exportType = filePathToPseudoNamespace(fieldMessageType.fileName) + "." + withinNamespace; - } - } else if (type === ENUM_TYPE) { - const fieldEnumType = exportMap.getEnum(fullTypeName); - if (fieldEnumType === undefined) { - throw new Error("No enum export for: " + fullTypeName); - } - const withinNamespace = withinNamespaceFromExportEntry(fullTypeName, fieldEnumType); - if (fieldEnumType.fileName === fileName) { - exportType = withinNamespace; - } else { - exportType = filePathToPseudoNamespace(fieldEnumType.fileName) + "." + withinNamespace; - } - exportType = `${exportType}Map[keyof ${exportType}Map]`; - } else { - if (field.getOptions() && field.getOptions().hasJstype()) { - switch (field.getOptions().getJstype()) { - case JSType.JS_NUMBER: - exportType = "number"; - break; - case JSType.JS_STRING: - exportType = "string"; - break; - default: - exportType = getTypeName(type); - } - } else { - exportType = getTypeName(type); - } + return true; } - let hasClearMethod = false; - function printClearIfNotPresent() { - if (!hasClearMethod) { - hasClearMethod = true; - printer.printIndentedLn(`clear${withUppercase}${field.getLabel() === FieldDescriptorProto.Label.LABEL_REPEATED ? "List" : ""}(): void;`); - } + if (field.getType() === MESSAGE_TYPE) { + return true; } - if (hasFieldPresence(field, fileDescriptor)) { - printer.printIndentedLn(`has${withUppercase}(): boolean;`); - printClearIfNotPresent(); + if (isProto2(fileDescriptor)) { + return true; } - function printRepeatedAddMethod(valueType: string) { - const optionalValue = field.getType() === MESSAGE_TYPE; - printer.printIndentedLn(`add${withUppercase}(value${optionalValue ? "?" : ""}: ${valueType}, index?: number): ${valueType};`); - } - - if (field.getLabel() === FieldDescriptorProto.Label.LABEL_REPEATED) {// is repeated - printClearIfNotPresent(); - if (type === BYTES_TYPE) { - toObjectType.printIndentedLn(`${camelCaseName}List: Array,`); - printer.printIndentedLn(`get${withUppercase}List(): Array;`); - printer.printIndentedLn(`get${withUppercase}List_asU8(): Array;`); - printer.printIndentedLn(`get${withUppercase}List_asB64(): Array;`); - printer.printIndentedLn(`set${withUppercase}List(value: Array): void;`); - printRepeatedAddMethod("Uint8Array | string"); - } else { - toObjectType.printIndentedLn(`${camelCaseName}List: Array<${exportType}${type === MESSAGE_TYPE ? ".AsObject" : ""}>,`); - printer.printIndentedLn(`get${withUppercase}List(): Array<${exportType}>;`); - printer.printIndentedLn(`set${withUppercase}List(value: Array<${exportType}>): void;`); - printRepeatedAddMethod(exportType); - } - } else { - if (type === BYTES_TYPE) { - toObjectType.printIndentedLn(`${camelCaseName}: Uint8Array | string,`); - printer.printIndentedLn(`get${withUppercase}(): Uint8Array | string;`); - printer.printIndentedLn(`get${withUppercase}_asU8(): Uint8Array;`); - printer.printIndentedLn(`get${withUppercase}_asB64(): string;`); - printer.printIndentedLn(`set${withUppercase}(value: Uint8Array | string): void;`); - } else { - let fieldObjectType = exportType; - let canBeUndefined = false; - if (type === MESSAGE_TYPE) { - fieldObjectType += ".AsObject"; - if (!isProto2(fileDescriptor) || (field.getLabel() === FieldDescriptorProto.Label.LABEL_OPTIONAL)) { - canBeUndefined = true; - } - } else { - if (isProto2(fileDescriptor)) { - canBeUndefined = true; - } - } - const fieldObjectName = normaliseFieldObjectName(camelCaseName); - toObjectType.printIndentedLn(`${fieldObjectName}${canBeUndefined ? "?" : ""}: ${fieldObjectType},`); - printer.printIndentedLn(`get${withUppercase}(): ${exportType}${canBeUndefined ? " | undefined" : ""};`); - printer.printIndentedLn(`set${withUppercase}(value${type === MESSAGE_TYPE ? "?" : ""}: ${exportType}): void;`); - } - } - printer.printEmptyLn(); - }); - - toObjectType.printLn(`}`); - - messageDescriptor.getOneofDeclList().forEach(oneOfDecl => { - printer.printIndentedLn(`get${oneOfName(oneOfDecl.getName())}Case(): ${messageName}.${oneOfName(oneOfDecl.getName())}Case;`); - }); - - printer.printIndentedLn(`serializeBinary(): Uint8Array;`); - printer.printIndentedLn(`toObject(includeInstance?: boolean): ${messageName}.${objectTypeName};`); - printer.printIndentedLn(`static toObject(includeInstance: boolean, msg: ${messageName}): ${messageName}.${objectTypeName};`); - printer.printIndentedLn(`static extensions: {[key: number]: jspb.ExtensionFieldInfo};`); - printer.printIndentedLn(`static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo};`); - printer.printIndentedLn(`static serializeBinaryToWriter(message: ${messageName}, writer: jspb.BinaryWriter): void;`); - printer.printIndentedLn(`static deserializeBinary(bytes: Uint8Array): ${messageName};`); - printer.printIndentedLn(`static deserializeBinaryFromReader(message: ${messageName}, reader: jspb.BinaryReader): ${messageName};`); - - printer.printLn(`}`); - printer.printEmptyLn(); - - printer.printLn(`export namespace ${messageName} {`); - - printer.print(toObjectType.getOutput()); - - messageDescriptor.getNestedTypeList().forEach(nested => { - const msgOutput = printMessage(fileName, exportMap, nested, indentLevel + 1, fileDescriptor); - if (msgOutput !== "") { - // If the message class is a Map entry then it isn't output, so don't print the namespace block - printer.print(msgOutput); - } - }); - messageDescriptor.getEnumTypeList().forEach(enumType => { - printer.print(`${printEnum(enumType, indentLevel + 1)}`); - }); - messageDescriptor.getOneofDeclList().forEach((oneOfDecl, index) => { - printer.print(`${printOneOfDecl(oneOfDecl, oneOfGroups[index] || [], indentLevel + 1)}`); - }); - messageDescriptor.getExtensionList().forEach(extension => { - printer.print(printExtension(fileName, exportMap, extension, indentLevel + 1)); - }); - - printer.printLn(`}`); - - return printer.getOutput(); + return false; +} + +export function printMessage( + fileName: string, + exportMap: ExportMap, + messageDescriptor: DescriptorProto, + indentLevel: number, + fileDescriptor: FileDescriptorProto +) { + const messageName = messageDescriptor.getName(); + const messageOptions = messageDescriptor.getOptions(); + if (messageOptions !== undefined && messageOptions.getMapEntry()) { + // this message type is the entry tuple for a map - don't output it + return ''; + } + + const objectTypeName = `AsObject`; + const toObjectType = new Printer(indentLevel + 1); + toObjectType.printLn(`export type ${objectTypeName} = {`); + + const printer = new Printer(indentLevel); + printer.printEmptyLn(); + printer.printLn(`export class ${messageName} extends jspb.Message {`); + + const oneOfGroups: Array> = []; + + messageDescriptor.getFieldList().forEach((field) => { + if (field.hasOneofIndex()) { + const oneOfIndex = field.getOneofIndex(); + let existing = oneOfGroups[oneOfIndex]; + if (existing === undefined) { + existing = []; + oneOfGroups[oneOfIndex] = existing; + } + existing.push(field); + } + const snakeCaseName = stripPrefix(field.getName().toLowerCase(), '_'); + const camelCaseName = snakeToCamel(snakeCaseName); + const withUppercase = uppercaseFirst(camelCaseName); + const type = field.getType(); + + let exportType; + const fullTypeName = field.getTypeName().slice(1); + if (type === MESSAGE_TYPE) { + const fieldMessageType = exportMap.getMessage(fullTypeName); + if (fieldMessageType === undefined) { + throw new Error('No message export for: ' + fullTypeName); + } + if ( + fieldMessageType.messageOptions !== undefined && + fieldMessageType.messageOptions.getMapEntry() + ) { + // This field is a map + const keyTuple = fieldMessageType.mapFieldOptions!.key; + const keyType = keyTuple[0]; + const keyTypeName = getFieldType( + keyType, + keyTuple[1], + fileName, + exportMap + ); + const valueTuple = fieldMessageType.mapFieldOptions!.value; + const valueType = valueTuple[0]; + let valueTypeName = getFieldType( + valueType, + valueTuple[1], + fileName, + exportMap + ); + if (valueType === BYTES_TYPE) { + valueTypeName = 'Uint8Array | string'; + } + if (valueType === ENUM_TYPE) { + valueTypeName = `${valueTypeName}[keyof ${valueTypeName}]`; + } + printer.printIndentedLn( + `get${withUppercase}Map(): jspb.Map<${keyTypeName}, ${valueTypeName}>;` + ); + printer.printIndentedLn(`clear${withUppercase}Map(): void;`); + toObjectType.printIndentedLn( + `${camelCaseName}Map: Array<[${keyTypeName}${ + keyType === MESSAGE_TYPE ? '.AsObject' : '' + }, ${valueTypeName}${ + valueType === MESSAGE_TYPE ? '.AsObject' : '' + }]>,` + ); + return; + } + const withinNamespace = withinNamespaceFromExportEntry( + fullTypeName, + fieldMessageType + ); + if (fieldMessageType.fileName === fileName) { + exportType = withinNamespace; + } else { + exportType = + filePathToPseudoNamespace(fieldMessageType.fileName) + + '.' + + withinNamespace; + } + } else if (type === ENUM_TYPE) { + const fieldEnumType = exportMap.getEnum(fullTypeName); + if (fieldEnumType === undefined) { + throw new Error('No enum export for: ' + fullTypeName); + } + const withinNamespace = withinNamespaceFromExportEntry( + fullTypeName, + fieldEnumType + ); + if (fieldEnumType.fileName === fileName) { + exportType = withinNamespace; + } else { + exportType = + filePathToPseudoNamespace(fieldEnumType.fileName) + + '.' + + withinNamespace; + } + exportType = `${exportType}Map[keyof ${exportType}Map]`; + } else { + if (field.getOptions() && field.getOptions().hasJstype()) { + switch (field.getOptions().getJstype()) { + case JSType.JS_NUMBER: + exportType = 'number'; + break; + case JSType.JS_STRING: + exportType = 'string'; + break; + default: + exportType = getTypeName(type); + } + } else { + exportType = getTypeName(type); + } + } + + let hasClearMethod = false; + function printClearIfNotPresent() { + if (!hasClearMethod) { + hasClearMethod = true; + printer.printIndentedLn( + `clear${withUppercase}${ + field.getLabel() === FieldDescriptorProto.Label.LABEL_REPEATED + ? 'List' + : '' + }(): void;` + ); + } + } + + if (hasFieldPresence(field, fileDescriptor)) { + printer.printIndentedLn(`has${withUppercase}(): boolean;`); + printClearIfNotPresent(); + } + + function printRepeatedAddMethod(valueType: string) { + const optionalValue = field.getType() === MESSAGE_TYPE; + printer.printIndentedLn( + `add${withUppercase}(value${ + optionalValue ? '?' : '' + }: ${valueType}, index?: number): ${valueType};` + ); + } + + if (field.getLabel() === FieldDescriptorProto.Label.LABEL_REPEATED) { + // is repeated + printClearIfNotPresent(); + if (type === BYTES_TYPE) { + toObjectType.printIndentedLn( + `${camelCaseName}List: Array,` + ); + printer.printIndentedLn( + `get${withUppercase}List(): Array;` + ); + printer.printIndentedLn( + `get${withUppercase}List_asU8(): Array;` + ); + printer.printIndentedLn( + `get${withUppercase}List_asB64(): Array;` + ); + printer.printIndentedLn( + `set${withUppercase}List(value: Array): void;` + ); + printRepeatedAddMethod('Uint8Array | string'); + } else { + toObjectType.printIndentedLn( + `${camelCaseName}List: Array<${exportType}${ + type === MESSAGE_TYPE ? '.AsObject' : '' + }>,` + ); + printer.printIndentedLn( + `get${withUppercase}List(): Array<${exportType}>;` + ); + printer.printIndentedLn( + `set${withUppercase}List(value: Array<${exportType}>): void;` + ); + printRepeatedAddMethod(exportType); + } + } else { + if (type === BYTES_TYPE) { + toObjectType.printIndentedLn(`${camelCaseName}: Uint8Array | string,`); + printer.printIndentedLn(`get${withUppercase}(): Uint8Array | string;`); + printer.printIndentedLn(`get${withUppercase}_asU8(): Uint8Array;`); + printer.printIndentedLn(`get${withUppercase}_asB64(): string;`); + printer.printIndentedLn( + `set${withUppercase}(value: Uint8Array | string): void;` + ); + } else { + let fieldObjectType = exportType; + let canBeUndefined = false; + if (type === MESSAGE_TYPE) { + fieldObjectType += '.AsObject'; + if ( + !isProto2(fileDescriptor) || + field.getLabel() === FieldDescriptorProto.Label.LABEL_OPTIONAL + ) { + canBeUndefined = true; + } + } else { + if (isProto2(fileDescriptor)) { + canBeUndefined = true; + } + } + const fieldObjectName = normaliseFieldObjectName(camelCaseName); + toObjectType.printIndentedLn( + `${fieldObjectName}${canBeUndefined ? '?' : ''}: ${fieldObjectType},` + ); + printer.printIndentedLn( + `get${withUppercase}(): ${exportType}${ + canBeUndefined ? ' | undefined' : '' + };` + ); + printer.printIndentedLn( + `set${withUppercase}(value${ + type === MESSAGE_TYPE ? '?' : '' + }: ${exportType}): void;` + ); + } + } + printer.printEmptyLn(); + }); + + toObjectType.printLn(`}`); + + messageDescriptor.getOneofDeclList().forEach((oneOfDecl) => { + printer.printIndentedLn( + `get${oneOfName(oneOfDecl.getName())}Case(): ${messageName}.${oneOfName( + oneOfDecl.getName() + )}Case;` + ); + }); + + printer.printIndentedLn(`serializeBinary(): Uint8Array;`); + printer.printIndentedLn( + `toObject(includeInstance?: boolean): ${messageName}.${objectTypeName};` + ); + printer.printIndentedLn( + `static toObject(includeInstance: boolean, msg: ${messageName}): ${messageName}.${objectTypeName};` + ); + printer.printIndentedLn( + `static extensions: {[key: number]: jspb.ExtensionFieldInfo};` + ); + printer.printIndentedLn( + `static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo};` + ); + printer.printIndentedLn( + `static serializeBinaryToWriter(message: ${messageName}, writer: jspb.BinaryWriter): void;` + ); + printer.printIndentedLn( + `static deserializeBinary(bytes: Uint8Array): ${messageName};` + ); + printer.printIndentedLn( + `static deserializeBinaryFromReader(message: ${messageName}, reader: jspb.BinaryReader): ${messageName};` + ); + + printer.printLn(`}`); + printer.printEmptyLn(); + + printer.printLn(`export namespace ${messageName} {`); + + printer.print(toObjectType.getOutput()); + + messageDescriptor.getNestedTypeList().forEach((nested) => { + const msgOutput = printMessage( + fileName, + exportMap, + nested, + indentLevel + 1, + fileDescriptor + ); + if (msgOutput !== '') { + // If the message class is a Map entry then it isn't output, so don't print the namespace block + printer.print(msgOutput); + } + }); + messageDescriptor.getEnumTypeList().forEach((enumType) => { + printer.print(`${printEnum(enumType, indentLevel + 1)}`); + }); + messageDescriptor.getOneofDeclList().forEach((oneOfDecl, index) => { + printer.print( + `${printOneOfDecl(oneOfDecl, oneOfGroups[index] || [], indentLevel + 1)}` + ); + }); + messageDescriptor.getExtensionList().forEach((extension) => { + printer.print(printExtension(fileName, exportMap, extension, indentLevel + 1)); + }); + + printer.printLn(`}`); + + return printer.getOutput(); } diff --git a/src/ts-protoc-gen/ts/oneof.ts b/src/ts-protoc-gen/ts/oneof.ts index d9e36d7..5224523 100644 --- a/src/ts-protoc-gen/ts/oneof.ts +++ b/src/ts-protoc-gen/ts/oneof.ts @@ -1,16 +1,25 @@ -import {Printer} from "../Printer"; -import {OneofDescriptorProto, FieldDescriptorProto} from "google-protobuf/google/protobuf/descriptor_pb"; -import {oneOfName} from "../util"; +import { Printer } from '../Printer'; +import { + OneofDescriptorProto, + FieldDescriptorProto, +} from 'google-protobuf/google/protobuf/descriptor_pb'; +import { oneOfName } from '../util'; -export function printOneOfDecl(oneOfDecl: OneofDescriptorProto, oneOfFields: Array, indentLevel: number) { - const printer = new Printer(indentLevel); - printer.printEmptyLn(); - printer.printLn(`export enum ${oneOfName(oneOfDecl.getName())}Case {`); - printer.printIndentedLn(`${oneOfDecl.getName().toUpperCase()}_NOT_SET = 0,`); - oneOfFields.forEach(field => { - printer.printIndentedLn(`${field.getName().toUpperCase()} = ${field.getNumber()},`); - }); - printer.printLn("}"); +export function printOneOfDecl( + oneOfDecl: OneofDescriptorProto, + oneOfFields: Array, + indentLevel: number +) { + const printer = new Printer(indentLevel); + printer.printEmptyLn(); + printer.printLn(`export enum ${oneOfName(oneOfDecl.getName())}Case {`); + printer.printIndentedLn(`${oneOfDecl.getName().toUpperCase()}_NOT_SET = 0,`); + oneOfFields.forEach((field) => { + printer.printIndentedLn( + `${field.getName().toUpperCase()} = ${field.getNumber()},` + ); + }); + printer.printLn('}'); - return printer.output; + return printer.output; } diff --git a/src/ts-protoc-gen/util.ts b/src/ts-protoc-gen/util.ts index 7b41791..f1632c5 100644 --- a/src/ts-protoc-gen/util.ts +++ b/src/ts-protoc-gen/util.ts @@ -1,184 +1,200 @@ -import {parse} from "querystring"; -import {FileDescriptorProto} from "google-protobuf/google/protobuf/descriptor_pb"; -import {ExportEnumEntry, ExportMessageEntry} from "./ExportMap"; -import {ServiceParameter, ModeParameter} from "./parameters"; +import { parse } from 'querystring'; +import { FileDescriptorProto } from 'google-protobuf/google/protobuf/descriptor_pb'; +import { ExportEnumEntry, ExportMessageEntry } from './ExportMap'; +import { ServiceParameter, ModeParameter } from './parameters'; export function filePathToPseudoNamespace(filePath: string): string { - return filePath.replace(".proto", "").replace(/\//g, "_").replace(/\./g, "_").replace(/\-/g, "_") + "_pb"; + return ( + filePath + .replace('.proto', '') + .replace(/\//g, '_') + .replace(/\./g, '_') + .replace(/\-/g, '_') + '_pb' + ); } export function stripPrefix(str: string, prefix: string) { - if (str.substr(0, prefix.length) === prefix) { - return str.substr(prefix.length); - } - return str; + if (str.substr(0, prefix.length) === prefix) { + return str.substr(prefix.length); + } + return str; } export function snakeToCamel(str: string): string { - return str.replace(/(\_\w)/g, function(m) { - return m[1].toUpperCase(); - }); + return str.replace(/(\_\w)/g, function (m) { + return m[1].toUpperCase(); + }); } export function uppercaseFirst(str: string): string { - return str.charAt(0).toUpperCase() + str.slice(1); + return str.charAt(0).toUpperCase() + str.slice(1); } -const PROTO2_SYNTAX = "proto2"; +const PROTO2_SYNTAX = 'proto2'; export function isProto2(fileDescriptor: FileDescriptorProto): boolean { - // Empty syntax defaults to proto2 - return (fileDescriptor.getSyntax() === "" || fileDescriptor.getSyntax() === PROTO2_SYNTAX); + // Empty syntax defaults to proto2 + return ( + fileDescriptor.getSyntax() === '' || fileDescriptor.getSyntax() === PROTO2_SYNTAX + ); } export function oneOfName(name: string) { - return uppercaseFirst(snakeToCamel(name.toLowerCase())); + return uppercaseFirst(snakeToCamel(name.toLowerCase())); } export function generateIndent(indentLevel: number): string { - let indent = ""; - for (let i = 0; i < indentLevel; i++) { - indent += " "; - } - return indent; + let indent = ''; + for (let i = 0; i < indentLevel; i++) { + indent += ' '; + } + return indent; } export function getPathToRoot(fileName: string) { - const depth = fileName.split("/").length; - return depth === 1 ? "./" : new Array(depth).join("../"); + const depth = fileName.split('/').length; + return depth === 1 ? './' : new Array(depth).join('../'); } -export function withinNamespaceFromExportEntry(name: string, exportEntry: ExportMessageEntry | ExportEnumEntry) { - return exportEntry.pkg ? name.substring(exportEntry.pkg.length + 1) : name; +export function withinNamespaceFromExportEntry( + name: string, + exportEntry: ExportMessageEntry | ExportEnumEntry +) { + return exportEntry.pkg ? name.substring(exportEntry.pkg.length + 1) : name; } export function replaceProtoSuffix(protoFilePath: string): string { - const suffix = ".proto"; - const hasProtoSuffix = protoFilePath.slice(protoFilePath.length - suffix.length) === suffix; - return hasProtoSuffix - ? protoFilePath.slice(0, -suffix.length) + "_pb" - : protoFilePath; + const suffix = '.proto'; + const hasProtoSuffix = + protoFilePath.slice(protoFilePath.length - suffix.length) === suffix; + return hasProtoSuffix + ? protoFilePath.slice(0, -suffix.length) + '_pb' + : protoFilePath; } export function withAllStdIn(callback: (buffer: Buffer) => void): void { - const ret: Buffer[] = []; - let len = 0; + const ret: Buffer[] = []; + let len = 0; - const stdin = process.stdin; - stdin.on("readable", function () { - let chunk; + const stdin = process.stdin; + stdin.on('readable', function () { + let chunk; - while ((chunk = stdin.read())) { - if (!(chunk instanceof Buffer)) throw new Error("Did not receive buffer"); - ret.push(chunk); - len += chunk.length; - } - }); + while ((chunk = stdin.read())) { + if (!(chunk instanceof Buffer)) throw new Error('Did not receive buffer'); + ret.push(chunk); + len += chunk.length; + } + }); - stdin.on("end", function () { - callback(Buffer.concat(ret, len)); - }); + stdin.on('end', function () { + callback(Buffer.concat(ret, len)); + }); } // normaliseFieldObjectName modifies the field name that appears in the `asObject` representation // to match the logic found in `protobuf/compiler/js/js_generator.cc`. See: https://goo.gl/tX1dPQ export function normaliseFieldObjectName(name: string): string { - switch (name) { - case "abstract": - case "boolean": - case "break": - case "byte": - case "case": - case "catch": - case "char": - case "class": - case "const": - case "continue": - case "debugger": - case "default": - case "delete": - case "do": - case "double": - case "else": - case "enum": - case "export": - case "extends": - case "false": - case "final": - case "finally": - case "float": - case "for": - case "function": - case "goto": - case "if": - case "implements": - case "import": - case "in": - case "instanceof": - case "int": - case "interface": - case "long": - case "native": - case "new": - case "null": - case "package": - case "private": - case "protected": - case "public": - case "return": - case "short": - case "static": - case "super": - case "switch": - case "synchronized": - case "this": - case "throw": - case "throws": - case "transient": - case "try": - case "typeof": - case "var": - case "void": - case "volatile": - case "while": - case "with": - return `pb_${name}`; - } - return name; + switch (name) { + case 'abstract': + case 'boolean': + case 'break': + case 'byte': + case 'case': + case 'catch': + case 'char': + case 'class': + case 'const': + case 'continue': + case 'debugger': + case 'default': + case 'delete': + case 'do': + case 'double': + case 'else': + case 'enum': + case 'export': + case 'extends': + case 'false': + case 'final': + case 'finally': + case 'float': + case 'for': + case 'function': + case 'goto': + case 'if': + case 'implements': + case 'import': + case 'in': + case 'instanceof': + case 'int': + case 'interface': + case 'long': + case 'native': + case 'new': + case 'null': + case 'package': + case 'private': + case 'protected': + case 'public': + case 'return': + case 'short': + case 'static': + case 'super': + case 'switch': + case 'synchronized': + case 'this': + case 'throw': + case 'throws': + case 'transient': + case 'try': + case 'typeof': + case 'var': + case 'void': + case 'volatile': + case 'while': + case 'with': + return `pb_${name}`; + } + return name; } export function getServiceParameter(service?: string): ServiceParameter { - switch (service) { - case "true": - console.warn("protoc-gen-ts warning: The service=true parameter has been deprecated. Use service=grpc-web instead."); - return ServiceParameter.GrpcWeb; - case "grpc-web": - return ServiceParameter.GrpcWeb; - case "grpc-node": - return ServiceParameter.GrpcNode; - case undefined: - return ServiceParameter.None; - default: - throw new Error(`Unrecognised service parameter: ${service}`); - } + switch (service) { + case 'true': + console.warn( + 'protoc-gen-ts warning: The service=true parameter has been deprecated. Use service=grpc-web instead.' + ); + return ServiceParameter.GrpcWeb; + case 'grpc-web': + return ServiceParameter.GrpcWeb; + case 'grpc-node': + return ServiceParameter.GrpcNode; + case undefined: + return ServiceParameter.None; + default: + throw new Error(`Unrecognised service parameter: ${service}`); + } } export function getModeParameter(mode?: string): ModeParameter { - switch (mode) { - case "grpc-js": - return ModeParameter.GrpcJs; - case undefined: - return ModeParameter.None; - default: - throw new Error(`Unrecognised mode parameter: ${mode}`); - } + switch (mode) { + case 'grpc-js': + return ModeParameter.GrpcJs; + case undefined: + return ModeParameter.None; + default: + throw new Error(`Unrecognised mode parameter: ${mode}`); + } } -export function getParameterEnums(parameter: string): { - service: ServiceParameter, - mode: ModeParameter +export function getParameterEnums( + parameter: string +): { + service: ServiceParameter; + mode: ModeParameter; } { - const {service, mode} = parse(parameter, ","); - return { - service: getServiceParameter(service), - mode: getModeParameter(mode) - }; + const { service, mode } = parse(parameter, ','); + return { + service: getServiceParameter(service), + mode: getModeParameter(mode), + }; }