chore(ts-protoc-gen): prettier

This commit is contained in:
41666 2020-10-11 18:35:22 -04:00
parent 291ec9576f
commit 783915f057
16 changed files with 1660 additions and 1202 deletions

View file

@ -1,26 +1,26 @@
import {Printer} from "./Printer"; import { Printer } from './Printer';
import {generateIndent} from "./util"; import { generateIndent } from './util';
export class CodePrinter { export class CodePrinter {
private indentation: string; private indentation: string;
constructor(private depth: number, private printer: Printer) { constructor(private depth: number, private printer: Printer) {
this.indentation = generateIndent(1); this.indentation = generateIndent(1);
} }
indent() { indent() {
this.depth++; this.depth++;
return this; return this;
} }
dedent() { dedent() {
this.depth--; this.depth--;
return this; return this;
} }
printLn(line: string) { printLn(line: string) {
this.printer.printLn(new Array(this.depth + 1).join(this.indentation) + line); this.printer.printLn(new Array(this.depth + 1).join(this.indentation) + line);
return this; return this;
} }
printEmptyLn() { printEmptyLn() {
this.printer.printEmptyLn(); this.printer.printEmptyLn();
return this; return this;
} }
} }

View file

@ -1,84 +1,101 @@
import { import {
FileDescriptorProto, FileDescriptorProto,
DescriptorProto, DescriptorProto,
MessageOptions, MessageOptions,
EnumOptions, EnumOptions,
FieldDescriptorProto FieldDescriptorProto,
} from "google-protobuf/google/protobuf/descriptor_pb"; } from 'google-protobuf/google/protobuf/descriptor_pb';
import Type = FieldDescriptorProto.Type; import Type = FieldDescriptorProto.Type;
export type ExportMessageEntry = { export type ExportMessageEntry = {
pkg: string, pkg: string;
fileName: string, fileName: string;
messageOptions: MessageOptions, messageOptions: MessageOptions;
mapFieldOptions?: { mapFieldOptions?: {
key: [Type, string], key: [Type, string];
value: [Type, string], value: [Type, string];
} };
}; };
export type ExportEnumEntry = { export type ExportEnumEntry = {
pkg: string, pkg: string;
fileName: string, fileName: string;
enumOptions: EnumOptions, enumOptions: EnumOptions;
}; };
export class ExportMap { export class ExportMap {
messageMap: {[key: string]: ExportMessageEntry} = {}; messageMap: { [key: string]: ExportMessageEntry } = {};
enumMap: {[key: string]: ExportEnumEntry} = {}; enumMap: { [key: string]: ExportEnumEntry } = {};
exportNested(scope: string, fileDescriptor: FileDescriptorProto, message: DescriptorProto) { exportNested(
const messageEntry: ExportMessageEntry = { scope: string,
pkg: fileDescriptor.getPackage(), fileDescriptor: FileDescriptorProto,
fileName: fileDescriptor.getName(), message: DescriptorProto
messageOptions: message.getOptions(), ) {
mapFieldOptions: message.getOptions() && message.getOptions().getMapEntry() ? { const messageEntry: ExportMessageEntry = {
key: [message.getFieldList()[0].getType(), message.getFieldList()[0].getTypeName().slice(1)], pkg: fileDescriptor.getPackage(),
value: [message.getFieldList()[1].getType(), message.getFieldList()[1].getTypeName().slice(1)], fileName: fileDescriptor.getName(),
} : undefined, 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()}`; const entryName = `${packagePrefix}${message.getName()}`;
this.messageMap[entryName] = messageEntry; this.messageMap[entryName] = messageEntry;
message.getNestedTypeList().forEach(nested => { message.getNestedTypeList().forEach((nested) => {
this.exportNested(`${packagePrefix}${message.getName()}`, fileDescriptor, nested); this.exportNested(
}); `${packagePrefix}${message.getName()}`,
fileDescriptor,
nested
);
});
message.getEnumTypeList().forEach(enumType => { message.getEnumTypeList().forEach((enumType) => {
const identifier = `${packagePrefix}${message.getName()}.${enumType.getName()}`; const identifier = `${packagePrefix}${message.getName()}.${enumType.getName()}`;
this.enumMap[identifier] = { this.enumMap[identifier] = {
pkg: fileDescriptor.getPackage(), pkg: fileDescriptor.getPackage(),
fileName: fileDescriptor.getName(), fileName: fileDescriptor.getName(),
enumOptions: enumType.getOptions(), enumOptions: enumType.getOptions(),
}; };
}); });
} }
addFileDescriptor(fileDescriptor: FileDescriptorProto) { addFileDescriptor(fileDescriptor: FileDescriptorProto) {
const scope = fileDescriptor.getPackage(); const scope = fileDescriptor.getPackage();
fileDescriptor.getMessageTypeList().forEach(messageType => { fileDescriptor.getMessageTypeList().forEach((messageType) => {
this.exportNested(scope, fileDescriptor, messageType); this.exportNested(scope, fileDescriptor, messageType);
}); });
fileDescriptor.getEnumTypeList().forEach(enumType => { fileDescriptor.getEnumTypeList().forEach((enumType) => {
const packagePrefix = scope ? scope + "." : ""; const packagePrefix = scope ? scope + '.' : '';
this.enumMap[packagePrefix + enumType.getName()] = { this.enumMap[packagePrefix + enumType.getName()] = {
pkg: fileDescriptor.getPackage(), pkg: fileDescriptor.getPackage(),
fileName: fileDescriptor.getName(), fileName: fileDescriptor.getName(),
enumOptions: enumType.getOptions(), enumOptions: enumType.getOptions(),
}; };
}); });
} }
getMessage(str: string): ExportMessageEntry | undefined { getMessage(str: string): ExportMessageEntry | undefined {
return this.messageMap[str]; return this.messageMap[str];
} }
getEnum(str: string): ExportEnumEntry | undefined { getEnum(str: string): ExportEnumEntry | undefined {
return this.enumMap[str]; return this.enumMap[str];
} }
} }

View file

@ -1,30 +1,30 @@
import {generateIndent} from "./util"; import { generateIndent } from './util';
export class Printer { export class Printer {
indentStr: string; indentStr: string;
output: string = ""; output: string = '';
constructor(indentLevel: number) { constructor(indentLevel: number) {
this.indentStr = generateIndent(indentLevel); this.indentStr = generateIndent(indentLevel);
} }
printLn(str: string) { printLn(str: string) {
this.output += this.indentStr + str + "\n"; this.output += this.indentStr + str + '\n';
} }
print(str: string) { print(str: string) {
this.output += str; this.output += str;
} }
printEmptyLn() { printEmptyLn() {
this.output += "\n"; this.output += '\n';
} }
printIndentedLn(str: string) { printIndentedLn(str: string) {
this.output += this.indentStr + " " + str + "\n"; this.output += this.indentStr + ' ' + str + '\n';
} }
getOutput(): string { getOutput(): string {
return this.output; return this.output;
} }
} }

View file

@ -1,14 +1,16 @@
export const WellKnownTypesMap: {[key: string]: string} = { export const WellKnownTypesMap: { [key: string]: string } = {
"google/protobuf/compiler/plugin.proto": "google-protobuf/google/protobuf/compiler/plugin_pb", 'google/protobuf/compiler/plugin.proto':
"google/protobuf/any.proto": "google-protobuf/google/protobuf/any_pb", 'google-protobuf/google/protobuf/compiler/plugin_pb',
"google/protobuf/api.proto": "google-protobuf/google/protobuf/api_pb", 'google/protobuf/any.proto': 'google-protobuf/google/protobuf/any_pb',
"google/protobuf/descriptor.proto": "google-protobuf/google/protobuf/descriptor_pb", 'google/protobuf/api.proto': 'google-protobuf/google/protobuf/api_pb',
"google/protobuf/duration.proto": "google-protobuf/google/protobuf/duration_pb", 'google/protobuf/descriptor.proto': 'google-protobuf/google/protobuf/descriptor_pb',
"google/protobuf/empty.proto": "google-protobuf/google/protobuf/empty_pb", 'google/protobuf/duration.proto': 'google-protobuf/google/protobuf/duration_pb',
"google/protobuf/field_mask.proto": "google-protobuf/google/protobuf/field_mask_pb", 'google/protobuf/empty.proto': 'google-protobuf/google/protobuf/empty_pb',
"google/protobuf/source_context.proto": "google-protobuf/google/protobuf/source_context_pb", 'google/protobuf/field_mask.proto': 'google-protobuf/google/protobuf/field_mask_pb',
"google/protobuf/struct.proto": "google-protobuf/google/protobuf/struct_pb", 'google/protobuf/source_context.proto':
"google/protobuf/timestamp.proto": "google-protobuf/google/protobuf/timestamp_pb", 'google-protobuf/google/protobuf/source_context_pb',
"google/protobuf/type.proto": "google-protobuf/google/protobuf/type_pb", 'google/protobuf/struct.proto': 'google-protobuf/google/protobuf/struct_pb',
"google/protobuf/wrappers.proto": "google-protobuf/google/protobuf/wrappers_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',
}; };

View file

@ -1,11 +1,14 @@
import {printFileDescriptorTSD} from "./ts/fileDescriptorTSD"; import { printFileDescriptorTSD } from './ts/fileDescriptorTSD';
import {ExportMap} from "./ExportMap"; import { ExportMap } from './ExportMap';
import {replaceProtoSuffix, withAllStdIn, getParameterEnums} from "./util"; import { replaceProtoSuffix, withAllStdIn, getParameterEnums } from './util';
import {CodeGeneratorRequest, CodeGeneratorResponse} from "google-protobuf/google/protobuf/compiler/plugin_pb"; import {
import {FileDescriptorProto} from "google-protobuf/google/protobuf/descriptor_pb"; CodeGeneratorRequest,
import {generateGrpcWebService} from "./service/grpcweb"; CodeGeneratorResponse,
import {generateGrpcNodeService} from "./service/grpcnode"; } from 'google-protobuf/google/protobuf/compiler/plugin_pb';
import {ServiceParameter} from "./parameters"; 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. * This is the ProtoC compiler plugin.
@ -17,45 +20,55 @@ import {ServiceParameter} from "./parameters";
* *
*/ */
withAllStdIn((inputBuff: Buffer) => { withAllStdIn((inputBuff: Buffer) => {
try { try {
const typedInputBuff = new Uint8Array(inputBuff.length); const typedInputBuff = new Uint8Array(inputBuff.length);
typedInputBuff.set(inputBuff); typedInputBuff.set(inputBuff);
const codeGenRequest = CodeGeneratorRequest.deserializeBinary(typedInputBuff); const codeGenRequest = CodeGeneratorRequest.deserializeBinary(typedInputBuff);
const codeGenResponse = new CodeGeneratorResponse(); const codeGenResponse = new CodeGeneratorResponse();
const exportMap = new ExportMap(); const exportMap = new ExportMap();
const fileNameToDescriptor: {[key: string]: FileDescriptorProto} = {}; const fileNameToDescriptor: { [key: string]: FileDescriptorProto } = {};
const parameter = codeGenRequest.getParameter(); const parameter = codeGenRequest.getParameter();
const {service, mode} = getParameterEnums(parameter); const { service, mode } = getParameterEnums(parameter);
const generateGrpcWebServices = service === ServiceParameter.GrpcWeb; const generateGrpcWebServices = service === ServiceParameter.GrpcWeb;
const generateGrpcNodeServices = service === ServiceParameter.GrpcNode; const generateGrpcNodeServices = service === ServiceParameter.GrpcNode;
codeGenRequest.getProtoFileList().forEach(protoFileDescriptor => { codeGenRequest.getProtoFileList().forEach((protoFileDescriptor) => {
fileNameToDescriptor[protoFileDescriptor.getName()] = protoFileDescriptor; fileNameToDescriptor[protoFileDescriptor.getName()] = protoFileDescriptor;
exportMap.addFileDescriptor(protoFileDescriptor); exportMap.addFileDescriptor(protoFileDescriptor);
}); });
codeGenRequest.getFileToGenerateList().forEach(fileName => { codeGenRequest.getFileToGenerateList().forEach((fileName) => {
const outputFileName = replaceProtoSuffix(fileName); const outputFileName = replaceProtoSuffix(fileName);
const thisFile = new CodeGeneratorResponse.File(); const thisFile = new CodeGeneratorResponse.File();
thisFile.setName(outputFileName + ".d.ts"); thisFile.setName(outputFileName + '.d.ts');
thisFile.setContent(printFileDescriptorTSD(fileNameToDescriptor[fileName], exportMap)); thisFile.setContent(
codeGenResponse.addFile(thisFile); printFileDescriptorTSD(fileNameToDescriptor[fileName], exportMap)
);
codeGenResponse.addFile(thisFile);
if (generateGrpcWebServices) { if (generateGrpcWebServices) {
generateGrpcWebService(outputFileName, fileNameToDescriptor[fileName], exportMap) generateGrpcWebService(
.forEach(file => codeGenResponse.addFile(file)); outputFileName,
} else if (generateGrpcNodeServices) { fileNameToDescriptor[fileName],
const file = generateGrpcNodeService(outputFileName, fileNameToDescriptor[fileName], exportMap, mode); exportMap
codeGenResponse.addFile(file); ).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())); process.stdout.write(Buffer.from(codeGenResponse.serializeBinary()));
} catch (err) { } catch (err) {
console.error("protoc-gen-ts error: " + err.stack + "\n"); console.error('protoc-gen-ts error: ' + err.stack + '\n');
process.exit(1); process.exit(1);
} }
}); });

View file

@ -1,10 +1,10 @@
export enum ServiceParameter { export enum ServiceParameter {
None, None,
GrpcWeb, GrpcWeb,
GrpcNode GrpcNode,
} }
export enum ModeParameter { export enum ModeParameter {
None, None,
GrpcJs GrpcJs,
} }

View file

@ -1,142 +1,183 @@
import {CodeGeneratorResponse} from "google-protobuf/google/protobuf/compiler/plugin_pb"; import { CodeGeneratorResponse } from 'google-protobuf/google/protobuf/compiler/plugin_pb';
import {FileDescriptorProto, MethodDescriptorProto, ServiceDescriptorProto} from "google-protobuf/google/protobuf/descriptor_pb"; import {
import {ExportMap} from "../ExportMap"; FileDescriptorProto,
import {WellKnownTypesMap} from "../WellKnown"; MethodDescriptorProto,
import {getFieldType, MESSAGE_TYPE} from "../ts/FieldTypes"; ServiceDescriptorProto,
import {filePathToPseudoNamespace, replaceProtoSuffix, getPathToRoot, normaliseFieldObjectName} from "../util"; } 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 { export function createFile(output: string, filename: string): CodeGeneratorResponse.File {
const file = new CodeGeneratorResponse.File(); const file = new CodeGeneratorResponse.File();
file.setName(filename); file.setName(filename);
file.setContent(output); file.setContent(output);
return file; return file;
} }
type CallingTypes = { type CallingTypes = {
requestType: string requestType: string;
responseType: string responseType: string;
}; };
function getCallingTypes(method: MethodDescriptorProto, exportMap: ExportMap): CallingTypes { function getCallingTypes(
return { method: MethodDescriptorProto,
requestType: getFieldType(MESSAGE_TYPE, method.getInputType().slice(1), "", exportMap), exportMap: ExportMap
responseType: getFieldType(MESSAGE_TYPE, method.getOutputType().slice(1), "", 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) { function isUsed(
return fileDescriptor.getServiceList().some(service => { fileDescriptor: FileDescriptorProto,
return service.getMethodList().some(method => { pseudoNamespace: string,
const callingTypes = getCallingTypes(method, exportMap); exportMap: ExportMap
const namespacePackage = pseudoNamespace + "."; ) {
return ( return fileDescriptor.getServiceList().some((service) => {
callingTypes.requestType.indexOf(namespacePackage) === 0 || return service.getMethodList().some((method) => {
callingTypes.responseType.indexOf(namespacePackage) === 0 const callingTypes = getCallingTypes(method, exportMap);
); const namespacePackage = pseudoNamespace + '.';
return (
callingTypes.requestType.indexOf(namespacePackage) === 0 ||
callingTypes.responseType.indexOf(namespacePackage) === 0
);
});
}); });
});
} }
export type ImportDescriptor = { export type ImportDescriptor = {
readonly namespace: string readonly namespace: string;
readonly path: string readonly path: string;
}; };
export type RPCMethodDescriptor = { export type RPCMethodDescriptor = {
readonly nameAsPascalCase: string, readonly nameAsPascalCase: string;
readonly nameAsCamelCase: string, readonly nameAsCamelCase: string;
readonly functionName: string, readonly functionName: string;
readonly serviceName: string, readonly serviceName: string;
readonly requestStream: boolean readonly requestStream: boolean;
readonly responseStream: boolean readonly responseStream: boolean;
readonly requestType: string readonly requestType: string;
readonly responseType: string readonly responseType: string;
}; };
export class RPCDescriptor { export class RPCDescriptor {
private readonly grpcService: GrpcServiceDescriptor; private readonly grpcService: GrpcServiceDescriptor;
private readonly protoService: ServiceDescriptorProto; private readonly protoService: ServiceDescriptorProto;
private readonly exportMap: ExportMap; private readonly exportMap: ExportMap;
constructor(grpcService: GrpcServiceDescriptor, protoService: ServiceDescriptorProto, exportMap: ExportMap) { constructor(
this.grpcService = grpcService; grpcService: GrpcServiceDescriptor,
this.protoService = protoService; protoService: ServiceDescriptorProto,
this.exportMap = exportMap; exportMap: ExportMap
} ) {
get name(): string { this.grpcService = grpcService;
return this.protoService.getName(); this.protoService = protoService;
} this.exportMap = exportMap;
}
get name(): string {
return this.protoService.getName();
}
get qualifiedName(): string { get qualifiedName(): string {
return (this.grpcService.packageName ? `${this.grpcService.packageName}.` : "") + this.name; return (
} (this.grpcService.packageName ? `${this.grpcService.packageName}.` : '') +
this.name
);
}
get methods(): RPCMethodDescriptor[] { get methods(): RPCMethodDescriptor[] {
return this.protoService.getMethodList() return this.protoService.getMethodList().map((method) => {
.map(method => { const callingTypes = getCallingTypes(method, this.exportMap);
const callingTypes = getCallingTypes(method, this.exportMap); const nameAsCamelCase =
const nameAsCamelCase = method.getName()[0].toLowerCase() + method.getName().substr(1); method.getName()[0].toLowerCase() + method.getName().substr(1);
return { return {
nameAsPascalCase: method.getName(), nameAsPascalCase: method.getName(),
nameAsCamelCase, nameAsCamelCase,
functionName: normaliseFieldObjectName(nameAsCamelCase), functionName: normaliseFieldObjectName(nameAsCamelCase),
serviceName: this.name, serviceName: this.name,
requestStream: method.getClientStreaming(), requestStream: method.getClientStreaming(),
responseStream: method.getServerStreaming(), responseStream: method.getServerStreaming(),
requestType: callingTypes.requestType, requestType: callingTypes.requestType,
responseType: callingTypes.responseType, responseType: callingTypes.responseType,
}; };
}); });
} }
} }
export class GrpcServiceDescriptor { export class GrpcServiceDescriptor {
private readonly fileDescriptor: FileDescriptorProto; private readonly fileDescriptor: FileDescriptorProto;
private readonly exportMap: ExportMap; private readonly exportMap: ExportMap;
private readonly pathToRoot: string; private readonly pathToRoot: string;
constructor(fileDescriptor: FileDescriptorProto, exportMap: ExportMap) { constructor(fileDescriptor: FileDescriptorProto, exportMap: ExportMap) {
this.fileDescriptor = fileDescriptor; this.fileDescriptor = fileDescriptor;
this.exportMap = exportMap; this.exportMap = exportMap;
this.pathToRoot = getPathToRoot(fileDescriptor.getName()); this.pathToRoot = getPathToRoot(fileDescriptor.getName());
} }
get filename(): string { get filename(): string {
return this.fileDescriptor.getName(); return this.fileDescriptor.getName();
} }
get packageName(): string { get packageName(): string {
return this.fileDescriptor.getPackage(); return this.fileDescriptor.getPackage();
} }
get imports(): ImportDescriptor[] { get imports(): ImportDescriptor[] {
const dependencies = this.fileDescriptor.getDependencyList() const dependencies = this.fileDescriptor
.filter(dependency => isUsed(this.fileDescriptor, filePathToPseudoNamespace(dependency), this.exportMap)) .getDependencyList()
.map(dependency => { .filter((dependency) =>
const namespace = filePathToPseudoNamespace(dependency); isUsed(
if (dependency in WellKnownTypesMap) { this.fileDescriptor,
return { filePathToPseudoNamespace(dependency),
namespace, this.exportMap
path: WellKnownTypesMap[dependency], )
}; )
} else { .map((dependency) => {
return { const namespace = filePathToPseudoNamespace(dependency);
namespace, if (dependency in WellKnownTypesMap) {
path: `${this.pathToRoot}${replaceProtoSuffix(replaceProtoSuffix(dependency))}` return {
}; namespace,
} path: WellKnownTypesMap[dependency],
}); };
const hostProto = { } else {
namespace: filePathToPseudoNamespace(this.filename), return {
path: `${this.pathToRoot}${replaceProtoSuffix(this.filename)}`, namespace,
}; path: `${this.pathToRoot}${replaceProtoSuffix(
return [ hostProto ].concat(dependencies); replaceProtoSuffix(dependency)
} )}`,
};
}
});
const hostProto = {
namespace: filePathToPseudoNamespace(this.filename),
path: `${this.pathToRoot}${replaceProtoSuffix(this.filename)}`,
};
return [hostProto].concat(dependencies);
}
get services(): RPCDescriptor[] { get services(): RPCDescriptor[] {
return this.fileDescriptor.getServiceList() return this.fileDescriptor.getServiceList().map((service) => {
.map(service => { return new RPCDescriptor(this, service, this.exportMap);
return new RPCDescriptor(this, service, this.exportMap); });
}); }
}
} }

View file

@ -1,127 +1,162 @@
import {ExportMap} from "../ExportMap"; import { ExportMap } from '../ExportMap';
import {Printer} from "../Printer"; import { Printer } from '../Printer';
import {FileDescriptorProto} from "google-protobuf/google/protobuf/descriptor_pb"; import { FileDescriptorProto } from 'google-protobuf/google/protobuf/descriptor_pb';
import {CodeGeneratorResponse} from "google-protobuf/google/protobuf/compiler/plugin_pb"; import { CodeGeneratorResponse } from 'google-protobuf/google/protobuf/compiler/plugin_pb';
import {createFile, RPCDescriptor, GrpcServiceDescriptor, RPCMethodDescriptor} from "./common"; import {
import { ModeParameter } from "../parameters"; createFile,
RPCDescriptor,
GrpcServiceDescriptor,
RPCMethodDescriptor,
} from './common';
import { ModeParameter } from '../parameters';
export function generateGrpcNodeService(filename: string, descriptor: FileDescriptorProto, exportMap: ExportMap, modeParameter: ModeParameter): CodeGeneratorResponse.File { export function generateGrpcNodeService(
const definitionFilename = filename.replace(/_pb$/, "_grpc_pb.d.ts"); filename: string,
return createFile(generateTypeScriptDefinition(descriptor, exportMap, modeParameter), definitionFilename); 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 { function generateTypeScriptDefinition(
const serviceDescriptor = new GrpcServiceDescriptor(fileDescriptor, exportMap); fileDescriptor: FileDescriptorProto,
const printer = new Printer(0); 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. // Header.
if (hasServices) { if (hasServices) {
printer.printLn("// GENERATED CODE -- DO NOT EDIT!"); 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(); 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(); 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) { function printService(printer: Printer, service: RPCDescriptor) {
const serviceName = `${service.name}Service`; const serviceName = `${service.name}Service`;
printer.printLn(`interface I${serviceName} extends grpc.ServiceDefinition<grpc.UntypedServiceImplementation> {`); printer.printLn(
service.methods `interface I${serviceName} extends grpc.ServiceDefinition<grpc.UntypedServiceImplementation> {`
.forEach(method => { );
const methodType = `grpc.MethodDefinition<${method.requestType}, ${method.responseType}>`; service.methods.forEach((method) => {
printer.printIndentedLn(`${method.nameAsCamelCase}: ${methodType};`); const methodType = `grpc.MethodDefinition<${method.requestType}, ${method.responseType}>`;
printer.printIndentedLn(`${method.nameAsCamelCase}: ${methodType};`);
}); });
printer.printLn("}"); printer.printLn('}');
printer.printEmptyLn(); printer.printEmptyLn();
printer.printLn(`export const ${serviceName}: I${serviceName};`); printer.printLn(`export const ${serviceName}: I${serviceName};`);
} }
function printClient(printer: Printer, service: RPCDescriptor) { function printClient(printer: Printer, service: RPCDescriptor) {
printer.printLn(`export class ${service.name}Client extends grpc.Client {`); printer.printLn(`export class ${service.name}Client extends grpc.Client {`);
printer.printIndentedLn("constructor(address: string, credentials: grpc.ChannelCredentials, options?: object);"); printer.printIndentedLn(
service.methods 'constructor(address: string, credentials: grpc.ChannelCredentials, options?: object);'
.forEach(method => { );
if (!method.requestStream && !method.responseStream) { service.methods.forEach((method) => {
printUnaryRequestMethod(printer, method); if (!method.requestStream && !method.responseStream) {
} else if (!method.requestStream) { printUnaryRequestMethod(printer, method);
printServerStreamRequestMethod(printer, method); } else if (!method.requestStream) {
} else if (!method.responseStream) { printServerStreamRequestMethod(printer, method);
printClientStreamRequestMethod(printer, method); } else if (!method.responseStream) {
} else { printClientStreamRequestMethod(printer, method);
printBidiStreamRequest(printer, method); } else {
} printBidiStreamRequest(printer, method);
}
}); });
printer.printLn("}"); printer.printLn('}');
} }
const metadata = "metadata: grpc.Metadata | null"; const metadata = 'metadata: grpc.Metadata | null';
const options = "options: grpc.CallOptions | null"; const options = 'options: grpc.CallOptions | null';
const metadataOrOptions = "metadataOrOptions: grpc.Metadata | grpc.CallOptions | null"; const metadataOrOptions = 'metadataOrOptions: grpc.Metadata | grpc.CallOptions | null';
const optionalMetadata = "metadata?: grpc.Metadata | null"; const optionalMetadata = 'metadata?: grpc.Metadata | null';
const optionalOptions = "options?: grpc.CallOptions | null"; const optionalOptions = 'options?: grpc.CallOptions | null';
const optionalMetadataOrOptions = "metadataOrOptions?: grpc.Metadata | grpc.CallOptions | null"; const optionalMetadataOrOptions =
'metadataOrOptions?: grpc.Metadata | grpc.CallOptions | null';
function printUnaryRequestMethod(printer: Printer, method: RPCMethodDescriptor) { function printUnaryRequestMethod(printer: Printer, method: RPCMethodDescriptor) {
const name = method.nameAsCamelCase; const name = method.nameAsCamelCase;
const argument = `argument: ${method.requestType}`; const argument = `argument: ${method.requestType}`;
const callback = `callback: grpc.requestCallback<${method.responseType}>`; const callback = `callback: grpc.requestCallback<${method.responseType}>`;
const returnType = "grpc.ClientUnaryCall"; const returnType = 'grpc.ClientUnaryCall';
printer.printIndentedLn(`${name}(${argument}, ${callback}): ${returnType};`); printer.printIndentedLn(`${name}(${argument}, ${callback}): ${returnType};`);
printer.printIndentedLn(`${name}(${argument}, ${metadataOrOptions}, ${callback}): ${returnType};`); printer.printIndentedLn(
printer.printIndentedLn(`${name}(${argument}, ${metadata}, ${options}, ${callback}): ${returnType};`); `${name}(${argument}, ${metadataOrOptions}, ${callback}): ${returnType};`
);
printer.printIndentedLn(
`${name}(${argument}, ${metadata}, ${options}, ${callback}): ${returnType};`
);
} }
function printServerStreamRequestMethod(printer: Printer, method: RPCMethodDescriptor) { function printServerStreamRequestMethod(printer: Printer, method: RPCMethodDescriptor) {
const name = method.nameAsCamelCase; const name = method.nameAsCamelCase;
const argument = `argument: ${method.requestType}`; const argument = `argument: ${method.requestType}`;
const returnType = `grpc.ClientReadableStream<${method.responseType}>`; const returnType = `grpc.ClientReadableStream<${method.responseType}>`;
printer.printIndentedLn(`${name}(${argument}, ${optionalMetadataOrOptions}): ${returnType};`); printer.printIndentedLn(
printer.printIndentedLn(`${name}(${argument}, ${optionalMetadata}, ${optionalOptions}): ${returnType};`); `${name}(${argument}, ${optionalMetadataOrOptions}): ${returnType};`
);
printer.printIndentedLn(
`${name}(${argument}, ${optionalMetadata}, ${optionalOptions}): ${returnType};`
);
} }
function printClientStreamRequestMethod(printer: Printer, method: RPCMethodDescriptor) { function printClientStreamRequestMethod(printer: Printer, method: RPCMethodDescriptor) {
const name = method.nameAsCamelCase; const name = method.nameAsCamelCase;
const callback = `callback: grpc.requestCallback<${method.responseType}>`; const callback = `callback: grpc.requestCallback<${method.responseType}>`;
const returnType = `grpc.ClientWritableStream<${method.requestType}>`; const returnType = `grpc.ClientWritableStream<${method.requestType}>`;
printer.printIndentedLn(`${name}(${callback}): grpc.ClientWritableStream<${method.requestType}>;`); printer.printIndentedLn(
printer.printIndentedLn(`${name}(${metadataOrOptions}, ${callback}): ${returnType};`); `${name}(${callback}): grpc.ClientWritableStream<${method.requestType}>;`
printer.printIndentedLn(`${name}(${metadata}, ${options}, ${callback}): ${returnType};`); );
printer.printIndentedLn(`${name}(${metadataOrOptions}, ${callback}): ${returnType};`);
printer.printIndentedLn(
`${name}(${metadata}, ${options}, ${callback}): ${returnType};`
);
} }
function printBidiStreamRequest(printer: Printer, method: RPCMethodDescriptor) { function printBidiStreamRequest(printer: Printer, method: RPCMethodDescriptor) {
const name = method.nameAsCamelCase; const name = method.nameAsCamelCase;
const returnType = `grpc.ClientDuplexStream<${method.requestType}, ${method.responseType}>`; const returnType = `grpc.ClientDuplexStream<${method.requestType}, ${method.responseType}>`;
printer.printIndentedLn(`${name}(${optionalMetadataOrOptions}): ${returnType};`); printer.printIndentedLn(`${name}(${optionalMetadataOrOptions}): ${returnType};`);
printer.printIndentedLn(`${name}(${optionalMetadata}, ${optionalOptions}): ${returnType};`); printer.printIndentedLn(
`${name}(${optionalMetadata}, ${optionalOptions}): ${returnType};`
);
} }

View file

@ -1,397 +1,570 @@
import {ExportMap} from "../ExportMap"; import { ExportMap } from '../ExportMap';
import {Printer} from "../Printer"; import { Printer } from '../Printer';
import {CodePrinter} from "../CodePrinter"; import { CodePrinter } from '../CodePrinter';
import {FileDescriptorProto} from "google-protobuf/google/protobuf/descriptor_pb"; import { FileDescriptorProto } from 'google-protobuf/google/protobuf/descriptor_pb';
import {CodeGeneratorResponse} from "google-protobuf/google/protobuf/compiler/plugin_pb"; import { CodeGeneratorResponse } from 'google-protobuf/google/protobuf/compiler/plugin_pb';
import {createFile, RPCMethodDescriptor, RPCDescriptor, GrpcServiceDescriptor} from "./common"; import {
createFile,
RPCMethodDescriptor,
RPCDescriptor,
GrpcServiceDescriptor,
} from './common';
export function generateGrpcWebService(filename: string, descriptor: FileDescriptorProto, exportMap: ExportMap): CodeGeneratorResponse.File[] { export function generateGrpcWebService(
return [ filename: string,
createFile(generateTypeScriptDefinition(descriptor, exportMap), `${filename}_service.d.ts`), descriptor: FileDescriptorProto,
createFile(generateJavaScript(descriptor, exportMap), `${filename}_service.js`), 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 { function generateTypeScriptDefinition(
const serviceDescriptor = new GrpcServiceDescriptor(fileDescriptor, exportMap); fileDescriptor: FileDescriptorProto,
const printer = new Printer(0); exportMap: ExportMap
): string {
const serviceDescriptor = new GrpcServiceDescriptor(fileDescriptor, exportMap);
const printer = new Printer(0);
// Header. // Header.
printer.printLn(`// package: ${serviceDescriptor.packageName}`); printer.printLn(`// package: ${serviceDescriptor.packageName}`);
printer.printLn(`// file: ${serviceDescriptor.filename}`); printer.printLn(`// file: ${serviceDescriptor.filename}`);
printer.printEmptyLn(); printer.printEmptyLn();
if (serviceDescriptor.services.length === 0) { if (serviceDescriptor.services.length === 0) {
return printer.getOutput(); return printer.getOutput();
} }
// Import statements. // Import statements.
serviceDescriptor.imports serviceDescriptor.imports.forEach((importDescriptor) => {
.forEach(importDescriptor => { printer.printLn(
printer.printLn(`import * as ${importDescriptor.namespace} from "${importDescriptor.path}";`); `import * as ${importDescriptor.namespace} from "${importDescriptor.path}";`
);
}); });
printer.printLn(`import {grpc} from "@improbable-eng/grpc-web";`); printer.printLn(`import {grpc} from "@improbable-eng/grpc-web";`);
printer.printEmptyLn(); printer.printEmptyLn();
// Services. // Services.
serviceDescriptor.services serviceDescriptor.services.forEach((service) => {
.forEach(service => { // Method Type Definitions
service.methods.forEach((method) => {
// Method Type Definitions printer.printLn(`type ${method.serviceName}${method.nameAsPascalCase} = {`);
service.methods.forEach(method => { printer.printIndentedLn(`readonly methodName: string;`);
printer.printLn(`type ${method.serviceName}${method.nameAsPascalCase} = {`); printer.printIndentedLn(`readonly service: typeof ${method.serviceName};`);
printer.printIndentedLn(`readonly methodName: string;`); printer.printIndentedLn(`readonly requestStream: ${method.requestStream};`);
printer.printIndentedLn(`readonly service: typeof ${method.serviceName};`); printer.printIndentedLn(`readonly responseStream: ${method.responseStream};`);
printer.printIndentedLn(`readonly requestStream: ${method.requestStream};`); printer.printIndentedLn(
printer.printIndentedLn(`readonly responseStream: ${method.responseStream};`); `readonly requestType: typeof ${method.requestType};`
printer.printIndentedLn(`readonly requestType: typeof ${method.requestType};`); );
printer.printIndentedLn(`readonly responseType: typeof ${method.responseType};`); printer.printIndentedLn(
printer.printLn(`};`); `readonly responseType: typeof ${method.responseType};`
printer.printEmptyLn(); );
}); 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<T> {`);
printer.printIndentedLn(`cancel(): void;`);
printer.printIndentedLn(`on(type: 'data', handler: (message: T) => void): ResponseStream<T>;`);
printer.printIndentedLn(`on(type: 'end', handler: (status?: Status) => void): ResponseStream<T>;`);
printer.printIndentedLn(`on(type: 'status', handler: (status: Status) => void): ResponseStream<T>;`);
printer.printLn(`}`);
printer.printLn(`interface RequestStream<T> {`);
printer.printIndentedLn(`write(message: T): RequestStream<T>;`);
printer.printIndentedLn(`end(): void;`);
printer.printIndentedLn(`cancel(): void;`);
printer.printIndentedLn(`on(type: 'end', handler: (status?: Status) => void): RequestStream<T>;`);
printer.printIndentedLn(`on(type: 'status', handler: (status: Status) => void): RequestStream<T>;`);
printer.printLn(`}`);
printer.printLn(`interface BidirectionalStream<ReqT, ResT> {`);
printer.printIndentedLn(`write(message: ReqT): BidirectionalStream<ReqT, ResT>;`);
printer.printIndentedLn(`end(): void;`);
printer.printIndentedLn(`cancel(): void;`);
printer.printIndentedLn(`on(type: 'data', handler: (message: ResT) => void): BidirectionalStream<ReqT, ResT>;`);
printer.printIndentedLn(`on(type: 'end', handler: (status?: Status) => void): BidirectionalStream<ReqT, ResT>;`);
printer.printIndentedLn(`on(type: 'status', handler: (status: Status) => void): BidirectionalStream<ReqT, ResT>;`);
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 printer.printLn(`export class ${service.name} {`);
printServiceStub(printer, service); printer.printIndentedLn(`static readonly serviceName: string;`);
service.methods.forEach((method) => {
printer.printEmptyLn(); 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<T> {`);
printer.printIndentedLn(`cancel(): void;`);
printer.printIndentedLn(
`on(type: 'data', handler: (message: T) => void): ResponseStream<T>;`
);
printer.printIndentedLn(
`on(type: 'end', handler: (status?: Status) => void): ResponseStream<T>;`
);
printer.printIndentedLn(
`on(type: 'status', handler: (status: Status) => void): ResponseStream<T>;`
);
printer.printLn(`}`);
printer.printLn(`interface RequestStream<T> {`);
printer.printIndentedLn(`write(message: T): RequestStream<T>;`);
printer.printIndentedLn(`end(): void;`);
printer.printIndentedLn(`cancel(): void;`);
printer.printIndentedLn(
`on(type: 'end', handler: (status?: Status) => void): RequestStream<T>;`
);
printer.printIndentedLn(
`on(type: 'status', handler: (status: Status) => void): RequestStream<T>;`
);
printer.printLn(`}`);
printer.printLn(`interface BidirectionalStream<ReqT, ResT> {`);
printer.printIndentedLn(`write(message: ReqT): BidirectionalStream<ReqT, ResT>;`);
printer.printIndentedLn(`end(): void;`);
printer.printIndentedLn(`cancel(): void;`);
printer.printIndentedLn(
`on(type: 'data', handler: (message: ResT) => void): BidirectionalStream<ReqT, ResT>;`
);
printer.printIndentedLn(
`on(type: 'end', handler: (status?: Status) => void): BidirectionalStream<ReqT, ResT>;`
);
printer.printIndentedLn(
`on(type: 'status', handler: (status: Status) => void): BidirectionalStream<ReqT, ResT>;`
);
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) { function printServiceStub(methodPrinter: Printer, service: RPCDescriptor) {
const printer = new CodePrinter(0, methodPrinter); const printer = new CodePrinter(0, methodPrinter);
printer printer
.printLn(`function ${service.name}Client(serviceHost, options) {`) .printLn(`function ${service.name}Client(serviceHost, options) {`)
.indent().printLn(`this.serviceHost = serviceHost;`) .indent()
.printLn(`this.options = options || {};`) .printLn(`this.serviceHost = serviceHost;`)
.dedent().printLn(`}`) .printLn(`this.options = options || {};`)
.printEmptyLn(); .dedent()
.printLn(`}`)
.printEmptyLn();
service.methods.forEach((method: RPCMethodDescriptor) => { service.methods.forEach((method: RPCMethodDescriptor) => {
if (method.requestStream && method.responseStream) { if (method.requestStream && method.responseStream) {
printBidirectionalStubMethod(printer, method); printBidirectionalStubMethod(printer, method);
} else if (method.requestStream) { } else if (method.requestStream) {
printClientStreamStubMethod(printer, method); printClientStreamStubMethod(printer, method);
} else if (method.responseStream) { } else if (method.responseStream) {
printServerStreamStubMethod(printer, method); printServerStreamStubMethod(printer, method);
} else { } else {
printUnaryStubMethod(printer, method); printUnaryStubMethod(printer, method);
} }
printer.printEmptyLn(); printer.printEmptyLn();
}); });
printer.printLn(`exports.${service.name}Client = ${service.name}Client;`); printer.printLn(`exports.${service.name}Client = ${service.name}Client;`);
} }
function printUnaryStubMethod(printer: CodePrinter, method: RPCMethodDescriptor) { function printUnaryStubMethod(printer: CodePrinter, method: RPCMethodDescriptor) {
printer printer
.printLn(`${method.serviceName}Client.prototype.${method.nameAsCamelCase} = function ${method.functionName}(requestMessage, metadata, callback) {`) .printLn(
.indent().printLn(`if (arguments.length === 2) {`) `${method.serviceName}Client.prototype.${method.nameAsCamelCase} = function ${method.functionName}(requestMessage, metadata, callback) {`
.indent().printLn(`callback = arguments[1];`) )
.dedent().printLn("}") .indent()
.printLn(`var client = grpc.unary(${method.serviceName}.${method.nameAsPascalCase}, {`) .printLn(`if (arguments.length === 2) {`)
.indent().printLn(`request: requestMessage,`) .indent()
.printLn(`host: this.serviceHost,`) .printLn(`callback = arguments[1];`)
.printLn(`metadata: metadata,`) .dedent()
.printLn(`transport: this.options.transport,`) .printLn('}')
.printLn(`debug: this.options.debug,`) .printLn(
.printLn(`onEnd: function (response) {`) `var client = grpc.unary(${method.serviceName}.${method.nameAsPascalCase}, {`
.indent().printLn(`if (callback) {`) )
.indent().printLn(`if (response.status !== grpc.Code.OK) {`) .indent()
.indent().printLn(`var err = new Error(response.statusMessage);`) .printLn(`request: requestMessage,`)
.printLn(`err.code = response.status;`) .printLn(`host: this.serviceHost,`)
.printLn(`err.metadata = response.trailers;`) .printLn(`metadata: metadata,`)
.printLn(`callback(err, null);`) .printLn(`transport: this.options.transport,`)
.dedent().printLn(`} else {`) .printLn(`debug: this.options.debug,`)
.indent().printLn(`callback(null, response.message);`) .printLn(`onEnd: function (response) {`)
.dedent().printLn(`}`) .indent()
.dedent().printLn(`}`) .printLn(`if (callback) {`)
.dedent().printLn(`}`) .indent()
.dedent().printLn(`});`) .printLn(`if (response.status !== grpc.Code.OK) {`)
.printLn(`return {`) .indent()
.indent().printLn(`cancel: function () {`) .printLn(`var err = new Error(response.statusMessage);`)
.indent().printLn(`callback = null;`) .printLn(`err.code = response.status;`)
.printLn(`client.close();`) .printLn(`err.metadata = response.trailers;`)
.dedent().printLn(`}`) .printLn(`callback(err, null);`)
.dedent().printLn(`};`) .dedent()
.dedent().printLn(`};`); .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) { function printServerStreamStubMethod(printer: CodePrinter, method: RPCMethodDescriptor) {
printer printer
.printLn(`${method.serviceName}Client.prototype.${method.nameAsCamelCase} = function ${method.functionName}(requestMessage, metadata) {`) .printLn(
.indent().printLn(`var listeners = {`) `${method.serviceName}Client.prototype.${method.nameAsCamelCase} = function ${method.functionName}(requestMessage, metadata) {`
.indent().printLn(`data: [],`) )
.printLn(`end: [],`) .indent()
.printLn(`status: []`) .printLn(`var listeners = {`)
.dedent().printLn(`};`) .indent()
.printLn(`var client = grpc.invoke(${method.serviceName}.${method.nameAsPascalCase}, {`) .printLn(`data: [],`)
.indent().printLn(`request: requestMessage,`) .printLn(`end: [],`)
.printLn(`host: this.serviceHost,`) .printLn(`status: []`)
.printLn(`metadata: metadata,`) .dedent()
.printLn(`transport: this.options.transport,`) .printLn(`};`)
.printLn(`debug: this.options.debug,`) .printLn(
.printLn(`onMessage: function (responseMessage) {`) `var client = grpc.invoke(${method.serviceName}.${method.nameAsPascalCase}, {`
.indent().printLn(`listeners.data.forEach(function (handler) {`) )
.indent().printLn(`handler(responseMessage);`) .indent()
.dedent().printLn(`});`) .printLn(`request: requestMessage,`)
.dedent().printLn(`},`) .printLn(`host: this.serviceHost,`)
.printLn(`onEnd: function (status, statusMessage, trailers) {`) .printLn(`metadata: metadata,`)
.indent().printLn(`listeners.status.forEach(function (handler) {`) .printLn(`transport: this.options.transport,`)
.indent().printLn(`handler({ code: status, details: statusMessage, metadata: trailers });`) .printLn(`debug: this.options.debug,`)
.dedent().printLn(`});`) .printLn(`onMessage: function (responseMessage) {`)
.printLn(`listeners.end.forEach(function (handler) {`) .indent()
.indent().printLn(`handler({ code: status, details: statusMessage, metadata: trailers });`) .printLn(`listeners.data.forEach(function (handler) {`)
.dedent().printLn(`});`) .indent()
.printLn(`listeners = null;`) .printLn(`handler(responseMessage);`)
.dedent().printLn(`}`) .dedent()
.dedent().printLn(`});`) .printLn(`});`)
.printLn(`return {`) .dedent()
.indent().printLn(`on: function (type, handler) {`) .printLn(`},`)
.indent().printLn(`listeners[type].push(handler);`) .printLn(`onEnd: function (status, statusMessage, trailers) {`)
.printLn(`return this;`) .indent()
.dedent().printLn(`},`) .printLn(`listeners.status.forEach(function (handler) {`)
.printLn(`cancel: function () {`) .indent()
.indent().printLn(`listeners = null;`) .printLn(`handler({ code: status, details: statusMessage, metadata: trailers });`)
.printLn(`client.close();`) .dedent()
.dedent().printLn(`}`) .printLn(`});`)
.dedent().printLn(`};`) .printLn(`listeners.end.forEach(function (handler) {`)
.dedent().printLn(`};`); .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) { function printClientStreamStubMethod(printer: CodePrinter, method: RPCMethodDescriptor) {
printer printer
.printLn(`${method.serviceName}Client.prototype.${method.nameAsCamelCase} = function ${method.functionName}(metadata) {`) .printLn(
.indent().printLn(`var listeners = {`) `${method.serviceName}Client.prototype.${method.nameAsCamelCase} = function ${method.functionName}(metadata) {`
.indent().printLn(`end: [],`) )
.printLn(`status: []`) .indent()
.dedent().printLn(`};`) .printLn(`var listeners = {`)
.printLn(`var client = grpc.client(${method.serviceName}.${method.nameAsPascalCase}, {`) .indent()
.indent().printLn(`host: this.serviceHost,`) .printLn(`end: [],`)
.printLn(`metadata: metadata,`) .printLn(`status: []`)
.printLn(`transport: this.options.transport`) .dedent()
.dedent().printLn(`});`) .printLn(`};`)
.printLn(`client.onEnd(function (status, statusMessage, trailers) {`) .printLn(
.indent().printLn(`listeners.status.forEach(function (handler) {`) `var client = grpc.client(${method.serviceName}.${method.nameAsPascalCase}, {`
.indent().printLn(`handler({ code: status, details: statusMessage, metadata: trailers });`) )
.dedent().printLn(`});`) .indent()
.printLn(`listeners.end.forEach(function (handler) {`) .printLn(`host: this.serviceHost,`)
.indent().printLn(`handler({ code: status, details: statusMessage, metadata: trailers });`) .printLn(`metadata: metadata,`)
.dedent().printLn(`});`) .printLn(`transport: this.options.transport`)
.printLn(`listeners = null;`) .dedent()
.dedent().printLn(`});`) .printLn(`});`)
.printLn(`return {`) .printLn(`client.onEnd(function (status, statusMessage, trailers) {`)
.indent().printLn(`on: function (type, handler) {`) .indent()
.indent().printLn(`listeners[type].push(handler);`) .printLn(`listeners.status.forEach(function (handler) {`)
.printLn(`return this;`) .indent()
.dedent().printLn(`},`) .printLn(`handler({ code: status, details: statusMessage, metadata: trailers });`)
.printLn(`write: function (requestMessage) {`) .dedent()
.indent().printLn(`if (!client.started) {`) .printLn(`});`)
.indent().printLn(`client.start(metadata);`) .printLn(`listeners.end.forEach(function (handler) {`)
.dedent().printLn(`}`) .indent()
.printLn(`client.send(requestMessage);`) .printLn(`handler({ code: status, details: statusMessage, metadata: trailers });`)
.printLn(`return this;`) .dedent()
.dedent().printLn(`},`) .printLn(`});`)
.printLn(`end: function () {`) .printLn(`listeners = null;`)
.indent().printLn(`client.finishSend();`) .dedent()
.dedent().printLn(`},`) .printLn(`});`)
.printLn(`cancel: function () {`) .printLn(`return {`)
.indent().printLn(`listeners = null;`) .indent()
.printLn(`client.close();`) .printLn(`on: function (type, handler) {`)
.dedent().printLn(`}`) .indent()
.dedent().printLn(`};`) .printLn(`listeners[type].push(handler);`)
.dedent().printLn(`};`); .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) { function printBidirectionalStubMethod(printer: CodePrinter, method: RPCMethodDescriptor) {
printer printer
.printLn(`${method.serviceName}Client.prototype.${method.nameAsCamelCase} = function ${method.functionName}(metadata) {`) .printLn(
.indent().printLn(`var listeners = {`) `${method.serviceName}Client.prototype.${method.nameAsCamelCase} = function ${method.functionName}(metadata) {`
.indent().printLn(`data: [],`) )
.printLn(`end: [],`) .indent()
.printLn(`status: []`) .printLn(`var listeners = {`)
.dedent().printLn(`};`) .indent()
.printLn(`var client = grpc.client(${method.serviceName}.${method.nameAsPascalCase}, {`) .printLn(`data: [],`)
.indent().printLn(`host: this.serviceHost,`) .printLn(`end: [],`)
.printLn(`metadata: metadata,`) .printLn(`status: []`)
.printLn(`transport: this.options.transport`) .dedent()
.dedent().printLn(`});`) .printLn(`};`)
.printLn(`client.onEnd(function (status, statusMessage, trailers) {`) .printLn(
.indent().printLn(`listeners.status.forEach(function (handler) {`) `var client = grpc.client(${method.serviceName}.${method.nameAsPascalCase}, {`
.indent().printLn(`handler({ code: status, details: statusMessage, metadata: trailers });`) )
.dedent().printLn(`});`) .indent()
.printLn(`listeners.end.forEach(function (handler) {`) .printLn(`host: this.serviceHost,`)
.indent().printLn(`handler({ code: status, details: statusMessage, metadata: trailers });`) .printLn(`metadata: metadata,`)
.dedent().printLn(`});`) .printLn(`transport: this.options.transport`)
.printLn(`listeners = null;`) .dedent()
.dedent().printLn(`});`) .printLn(`});`)
.printLn(`client.onMessage(function (message) {`) .printLn(`client.onEnd(function (status, statusMessage, trailers) {`)
.indent().printLn(`listeners.data.forEach(function (handler) {`) .indent()
.indent().printLn(`handler(message);`) .printLn(`listeners.status.forEach(function (handler) {`)
.dedent().printLn(`})`) .indent()
.dedent().printLn(`});`) .printLn(`handler({ code: status, details: statusMessage, metadata: trailers });`)
.printLn(`client.start(metadata);`) .dedent()
.printLn(`return {`) .printLn(`});`)
.indent().printLn(`on: function (type, handler) {`) .printLn(`listeners.end.forEach(function (handler) {`)
.indent().printLn(`listeners[type].push(handler);`) .indent()
.printLn(`return this;`) .printLn(`handler({ code: status, details: statusMessage, metadata: trailers });`)
.dedent().printLn(`},`) .dedent()
.printLn(`write: function (requestMessage) {`) .printLn(`});`)
.indent().printLn(`client.send(requestMessage);`) .printLn(`listeners = null;`)
.printLn(`return this;`) .dedent()
.dedent().printLn(`},`) .printLn(`});`)
.printLn(`end: function () {`) .printLn(`client.onMessage(function (message) {`)
.indent().printLn(`client.finishSend();`) .indent()
.dedent().printLn(`},`) .printLn(`listeners.data.forEach(function (handler) {`)
.printLn(`cancel: function () {`) .indent()
.indent().printLn(`listeners = null;`) .printLn(`handler(message);`)
.printLn(`client.close();`) .dedent()
.dedent().printLn(`}`) .printLn(`})`)
.dedent().printLn(`};`) .dedent()
.dedent().printLn(`};`); .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) { function printServiceStubTypes(methodPrinter: Printer, service: RPCDescriptor) {
const printer = new CodePrinter(0, methodPrinter); const printer = new CodePrinter(0, methodPrinter);
printer printer
.printLn(`export class ${service.name}Client {`) .printLn(`export class ${service.name}Client {`)
.indent().printLn(`readonly serviceHost: string;`) .indent()
.printLn(`readonly serviceHost: string;`)
.printEmptyLn() .printEmptyLn()
.printLn(`constructor(serviceHost: string, options?: grpc.RpcOptions);`); .printLn(`constructor(serviceHost: string, options?: grpc.RpcOptions);`);
service.methods.forEach((method: RPCMethodDescriptor) => { service.methods.forEach((method: RPCMethodDescriptor) => {
if (method.requestStream && method.responseStream) { if (method.requestStream && method.responseStream) {
printBidirectionalStubMethodTypes(printer, method); printBidirectionalStubMethodTypes(printer, method);
} else if (method.requestStream) { } else if (method.requestStream) {
printClientStreamStubMethodTypes(printer, method); printClientStreamStubMethodTypes(printer, method);
} else if (method.responseStream) { } else if (method.responseStream) {
printServerStreamStubMethodTypes(printer, method); printServerStreamStubMethodTypes(printer, method);
} else { } else {
printUnaryStubMethodTypes(printer, method); printUnaryStubMethodTypes(printer, method);
} }
}); });
printer.dedent().printLn("}"); printer.dedent().printLn('}');
} }
function printUnaryStubMethodTypes(printer: CodePrinter, method: RPCMethodDescriptor) { function printUnaryStubMethodTypes(printer: CodePrinter, method: RPCMethodDescriptor) {
printer printer
.printLn(`${method.nameAsCamelCase}(`) .printLn(`${method.nameAsCamelCase}(`)
.indent().printLn(`requestMessage: ${method.requestType},`) .indent()
.printLn(`metadata: grpc.Metadata,`) .printLn(`requestMessage: ${method.requestType},`)
.printLn(`callback: (error: ServiceError|null, responseMessage: ${method.responseType}|null) => void`) .printLn(`metadata: grpc.Metadata,`)
.dedent().printLn(`): UnaryResponse;`) .printLn(
.printLn(`${method.nameAsCamelCase}(`) `callback: (error: ServiceError|null, responseMessage: ${method.responseType}|null) => void`
.indent().printLn(`requestMessage: ${method.requestType},`) )
.printLn(`callback: (error: ServiceError|null, responseMessage: ${method.responseType}|null) => void`) .dedent()
.dedent().printLn(`): UnaryResponse;`); .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) { function printServerStreamStubMethodTypes(
printer.printLn(`${method.nameAsCamelCase}(requestMessage: ${method.requestType}, metadata?: grpc.Metadata): ResponseStream<${method.responseType}>;`); printer: CodePrinter,
method: RPCMethodDescriptor
) {
printer.printLn(
`${method.nameAsCamelCase}(requestMessage: ${method.requestType}, metadata?: grpc.Metadata): ResponseStream<${method.responseType}>;`
);
} }
function printClientStreamStubMethodTypes(printer: CodePrinter, method: RPCMethodDescriptor) { function printClientStreamStubMethodTypes(
printer.printLn(`${method.nameAsCamelCase}(metadata?: grpc.Metadata): RequestStream<${method.requestType}>;`); printer: CodePrinter,
method: RPCMethodDescriptor
) {
printer.printLn(
`${method.nameAsCamelCase}(metadata?: grpc.Metadata): RequestStream<${method.requestType}>;`
);
} }
function printBidirectionalStubMethodTypes(printer: CodePrinter, method: RPCMethodDescriptor) { function printBidirectionalStubMethodTypes(
printer.printLn(`${method.nameAsCamelCase}(metadata?: grpc.Metadata): BidirectionalStream<${method.requestType}, ${method.responseType}>;`); printer: CodePrinter,
method: RPCMethodDescriptor
) {
printer.printLn(
`${method.nameAsCamelCase}(metadata?: grpc.Metadata): BidirectionalStream<${method.requestType}, ${method.responseType}>;`
);
} }

View file

@ -1,59 +1,64 @@
import {filePathToPseudoNamespace, withinNamespaceFromExportEntry} from "../util"; import { filePathToPseudoNamespace, withinNamespaceFromExportEntry } from '../util';
import {ExportMap} from "../ExportMap"; import { ExportMap } from '../ExportMap';
import {FieldDescriptorProto} from "google-protobuf/google/protobuf/descriptor_pb"; import { FieldDescriptorProto } from 'google-protobuf/google/protobuf/descriptor_pb';
export const MESSAGE_TYPE = 11; export const MESSAGE_TYPE = 11;
export const BYTES_TYPE = 12; export const BYTES_TYPE = 12;
export const ENUM_TYPE = 14; export const ENUM_TYPE = 14;
const TypeNumToTypeString: {[key: number]: string} = {}; const TypeNumToTypeString: { [key: number]: string } = {};
TypeNumToTypeString[1] = "number"; // TYPE_DOUBLE TypeNumToTypeString[1] = 'number'; // TYPE_DOUBLE
TypeNumToTypeString[2] = "number"; // TYPE_FLOAT TypeNumToTypeString[2] = 'number'; // TYPE_FLOAT
TypeNumToTypeString[3] = "number"; // TYPE_INT64 TypeNumToTypeString[3] = 'number'; // TYPE_INT64
TypeNumToTypeString[4] = "number"; // TYPE_UINT64 TypeNumToTypeString[4] = 'number'; // TYPE_UINT64
TypeNumToTypeString[5] = "number"; // TYPE_INT32 TypeNumToTypeString[5] = 'number'; // TYPE_INT32
TypeNumToTypeString[6] = "number"; // TYPE_FIXED64 TypeNumToTypeString[6] = 'number'; // TYPE_FIXED64
TypeNumToTypeString[7] = "number"; // TYPE_FIXED32 TypeNumToTypeString[7] = 'number'; // TYPE_FIXED32
TypeNumToTypeString[8] = "boolean"; // TYPE_BOOL TypeNumToTypeString[8] = 'boolean'; // TYPE_BOOL
TypeNumToTypeString[9] = "string"; // TYPE_STRING TypeNumToTypeString[9] = 'string'; // TYPE_STRING
TypeNumToTypeString[10] = "Object"; // TYPE_GROUP TypeNumToTypeString[10] = 'Object'; // TYPE_GROUP
TypeNumToTypeString[MESSAGE_TYPE] = "Object"; // TYPE_MESSAGE - Length-delimited aggregate. TypeNumToTypeString[MESSAGE_TYPE] = 'Object'; // TYPE_MESSAGE - Length-delimited aggregate.
TypeNumToTypeString[BYTES_TYPE] = "Uint8Array"; // TYPE_BYTES TypeNumToTypeString[BYTES_TYPE] = 'Uint8Array'; // TYPE_BYTES
TypeNumToTypeString[13] = "number"; // TYPE_UINT32 TypeNumToTypeString[13] = 'number'; // TYPE_UINT32
TypeNumToTypeString[ENUM_TYPE] = "number"; // TYPE_ENUM TypeNumToTypeString[ENUM_TYPE] = 'number'; // TYPE_ENUM
TypeNumToTypeString[15] = "number"; // TYPE_SFIXED32 TypeNumToTypeString[15] = 'number'; // TYPE_SFIXED32
TypeNumToTypeString[16] = "number"; // TYPE_SFIXED64 TypeNumToTypeString[16] = 'number'; // TYPE_SFIXED64
TypeNumToTypeString[17] = "number"; // TYPE_SINT32 - Uses ZigZag encoding. TypeNumToTypeString[17] = 'number'; // TYPE_SINT32 - Uses ZigZag encoding.
TypeNumToTypeString[18] = "number"; // TYPE_SINT64 - Uses ZigZag encoding. TypeNumToTypeString[18] = 'number'; // TYPE_SINT64 - Uses ZigZag encoding.
export function getTypeName(fieldTypeNum: number): string { 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 { export function getFieldType(
if (type === MESSAGE_TYPE) { type: FieldDescriptorProto.Type,
const fromExport = exportMap.getMessage(typeName); typeName: string,
if (!fromExport) { currentFileName: string,
throw new Error("Could not getFieldType for message: " + typeName); exportMap: ExportMap
} ): string {
const withinNamespace = withinNamespaceFromExportEntry(typeName, fromExport); if (type === MESSAGE_TYPE) {
if (fromExport.fileName === currentFileName) { const fromExport = exportMap.getMessage(typeName);
return withinNamespace; 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 { } 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];
}
} }

View file

@ -1,16 +1,18 @@
import {EnumDescriptorProto} from "google-protobuf/google/protobuf/descriptor_pb"; import { EnumDescriptorProto } from 'google-protobuf/google/protobuf/descriptor_pb';
import {Printer} from "../Printer"; import { Printer } from '../Printer';
export function printEnum(enumDescriptor: EnumDescriptorProto, indentLevel: number) { export function printEnum(enumDescriptor: EnumDescriptorProto, indentLevel: number) {
const printer = new Printer(indentLevel); const printer = new Printer(indentLevel);
const enumInterfaceName = `${enumDescriptor.getName()}Map`; const enumInterfaceName = `${enumDescriptor.getName()}Map`;
printer.printEmptyLn(); printer.printEmptyLn();
printer.printLn(`export interface ${enumInterfaceName} {`); printer.printLn(`export interface ${enumInterfaceName} {`);
enumDescriptor.getValueList().forEach(value => { enumDescriptor.getValueList().forEach((value) => {
printer.printIndentedLn(`${value.getName().toUpperCase()}: ${value.getNumber()};`); printer.printIndentedLn(
}); `${value.getName().toUpperCase()}: ${value.getNumber()};`
printer.printLn(`}`); );
printer.printEmptyLn(); });
printer.printLn(`export const ${enumDescriptor.getName()}: ${enumInterfaceName};`); printer.printLn(`}`);
return printer.getOutput(); printer.printEmptyLn();
printer.printLn(`export const ${enumDescriptor.getName()}: ${enumInterfaceName};`);
return printer.getOutput();
} }

View file

@ -1,14 +1,26 @@
import {Printer} from "../Printer"; import { Printer } from '../Printer';
import {ExportMap} from "../ExportMap"; import { ExportMap } from '../ExportMap';
import {FieldDescriptorProto} from "google-protobuf/google/protobuf/descriptor_pb"; import { FieldDescriptorProto } from 'google-protobuf/google/protobuf/descriptor_pb';
import {snakeToCamel} from "../util"; import { snakeToCamel } from '../util';
import {getFieldType} from "./FieldTypes"; import { getFieldType } from './FieldTypes';
export function printExtension(fileName: string, exportMap: ExportMap, extension: FieldDescriptorProto, indentLevel: number): string { export function printExtension(
const printer = new Printer(indentLevel + 1); fileName: string,
printer.printEmptyLn(); exportMap: ExportMap,
const extensionName = snakeToCamel(extension.getName()); extension: FieldDescriptorProto,
const fieldType = getFieldType(extension.getType(), extension.getTypeName().slice(1), fileName, exportMap); indentLevel: number
printer.printLn(`export const ${extensionName}: jspb.ExtensionFieldInfo<${fieldType}>;`); ): string {
return printer.output; 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;
} }

View file

@ -1,49 +1,56 @@
import {filePathToPseudoNamespace, replaceProtoSuffix, getPathToRoot} from "../util"; import { filePathToPseudoNamespace, replaceProtoSuffix, getPathToRoot } from '../util';
import {ExportMap} from "../ExportMap"; import { ExportMap } from '../ExportMap';
import {Printer} from "../Printer"; import { Printer } from '../Printer';
import {FileDescriptorProto} from "google-protobuf/google/protobuf/descriptor_pb"; import { FileDescriptorProto } from 'google-protobuf/google/protobuf/descriptor_pb';
import {WellKnownTypesMap} from "../WellKnown"; import { WellKnownTypesMap } from '../WellKnown';
import {printMessage} from "./message"; import { printMessage } from './message';
import {printEnum} from "./enum"; import { printEnum } from './enum';
import {printExtension} from "./extensions"; import { printExtension } from './extensions';
export function printFileDescriptorTSD(fileDescriptor: FileDescriptorProto, exportMap: ExportMap) { export function printFileDescriptorTSD(
const fileName = fileDescriptor.getName(); fileDescriptor: FileDescriptorProto,
const packageName = fileDescriptor.getPackage(); 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(`// package: ${packageName}`);
printer.printLn(`// file: ${fileDescriptor.getName()}`); printer.printLn(`// file: ${fileDescriptor.getName()}`);
const upToRoot = getPathToRoot(fileName); const upToRoot = getPathToRoot(fileName);
printer.printEmptyLn(); printer.printEmptyLn();
printer.printLn(`import * as jspb from "google-protobuf";`); printer.printLn(`import * as jspb from "google-protobuf";`);
fileDescriptor.getDependencyList().forEach((dependency: string) => { fileDescriptor.getDependencyList().forEach((dependency: string) => {
const pseudoNamespace = filePathToPseudoNamespace(dependency); const pseudoNamespace = filePathToPseudoNamespace(dependency);
if (dependency in WellKnownTypesMap) { if (dependency in WellKnownTypesMap) {
printer.printLn(`import * as ${pseudoNamespace} from "${WellKnownTypesMap[dependency]}";`); printer.printLn(
} else { `import * as ${pseudoNamespace} from "${WellKnownTypesMap[dependency]}";`
const filePath = replaceProtoSuffix(dependency); );
printer.printLn(`import * as ${pseudoNamespace} from "${upToRoot}${filePath}";`); } else {
} const filePath = replaceProtoSuffix(dependency);
}); printer.printLn(
`import * as ${pseudoNamespace} from "${upToRoot}${filePath}";`
);
}
});
fileDescriptor.getMessageTypeList().forEach(enumType => { fileDescriptor.getMessageTypeList().forEach((enumType) => {
printer.print(printMessage(fileName, exportMap, enumType, 0, fileDescriptor)); printer.print(printMessage(fileName, exportMap, enumType, 0, fileDescriptor));
}); });
fileDescriptor.getExtensionList().forEach(extension => { fileDescriptor.getExtensionList().forEach((extension) => {
printer.print(printExtension(fileName, exportMap, extension, 0)); printer.print(printExtension(fileName, exportMap, extension, 0));
}); });
fileDescriptor.getEnumTypeList().forEach(enumType => { fileDescriptor.getEnumTypeList().forEach((enumType) => {
printer.print(printEnum(enumType, 0)); printer.print(printEnum(enumType, 0));
}); });
printer.printEmptyLn(); printer.printEmptyLn();
return printer.getOutput(); return printer.getOutput();
} }

View file

@ -1,235 +1,361 @@
import { import {
filePathToPseudoNamespace, snakeToCamel, uppercaseFirst, oneOfName, isProto2, filePathToPseudoNamespace,
withinNamespaceFromExportEntry, normaliseFieldObjectName, stripPrefix snakeToCamel,
} from "../util"; uppercaseFirst,
import {ExportMap} from "../ExportMap"; oneOfName,
isProto2,
withinNamespaceFromExportEntry,
normaliseFieldObjectName,
stripPrefix,
} from '../util';
import { ExportMap } from '../ExportMap';
import { import {
FieldDescriptorProto, FileDescriptorProto, DescriptorProto, FieldDescriptorProto,
FieldOptions FileDescriptorProto,
} from "google-protobuf/google/protobuf/descriptor_pb"; DescriptorProto,
import {MESSAGE_TYPE, BYTES_TYPE, ENUM_TYPE, getFieldType, getTypeName} from "./FieldTypes"; FieldOptions,
import {Printer} from "../Printer"; } from 'google-protobuf/google/protobuf/descriptor_pb';
import {printEnum} from "./enum"; import {
import {printOneOfDecl} from "./oneof"; MESSAGE_TYPE,
import {printExtension} from "./extensions"; 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; import JSType = FieldOptions.JSType;
function hasFieldPresence(field: FieldDescriptorProto, fileDescriptor: FileDescriptorProto): boolean { function hasFieldPresence(
if (field.getLabel() === FieldDescriptorProto.Label.LABEL_REPEATED) { field: FieldDescriptorProto,
return false; 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<Array<FieldDescriptorProto>> = [];
messageDescriptor.getFieldList().forEach(field => {
if (field.hasOneofIndex()) { if (field.hasOneofIndex()) {
const oneOfIndex = field.getOneofIndex(); return true;
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; if (field.getType() === MESSAGE_TYPE) {
function printClearIfNotPresent() { return true;
if (!hasClearMethod) {
hasClearMethod = true;
printer.printIndentedLn(`clear${withUppercase}${field.getLabel() === FieldDescriptorProto.Label.LABEL_REPEATED ? "List" : ""}(): void;`);
}
} }
if (hasFieldPresence(field, fileDescriptor)) { if (isProto2(fileDescriptor)) {
printer.printIndentedLn(`has${withUppercase}(): boolean;`); return true;
printClearIfNotPresent();
} }
function printRepeatedAddMethod(valueType: string) { return false;
const optionalValue = field.getType() === MESSAGE_TYPE; }
printer.printIndentedLn(`add${withUppercase}(value${optionalValue ? "?" : ""}: ${valueType}, index?: number): ${valueType};`);
} export function printMessage(
fileName: string,
if (field.getLabel() === FieldDescriptorProto.Label.LABEL_REPEATED) {// is repeated exportMap: ExportMap,
printClearIfNotPresent(); messageDescriptor: DescriptorProto,
if (type === BYTES_TYPE) { indentLevel: number,
toObjectType.printIndentedLn(`${camelCaseName}List: Array<Uint8Array | string>,`); fileDescriptor: FileDescriptorProto
printer.printIndentedLn(`get${withUppercase}List(): Array<Uint8Array | string>;`); ) {
printer.printIndentedLn(`get${withUppercase}List_asU8(): Array<Uint8Array>;`); const messageName = messageDescriptor.getName();
printer.printIndentedLn(`get${withUppercase}List_asB64(): Array<string>;`); const messageOptions = messageDescriptor.getOptions();
printer.printIndentedLn(`set${withUppercase}List(value: Array<Uint8Array | string>): void;`); if (messageOptions !== undefined && messageOptions.getMapEntry()) {
printRepeatedAddMethod("Uint8Array | string"); // this message type is the entry tuple for a map - don't output it
} else { return '';
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;`); const objectTypeName = `AsObject`;
printRepeatedAddMethod(exportType); const toObjectType = new Printer(indentLevel + 1);
} toObjectType.printLn(`export type ${objectTypeName} = {`);
} else {
if (type === BYTES_TYPE) { const printer = new Printer(indentLevel);
toObjectType.printIndentedLn(`${camelCaseName}: Uint8Array | string,`); printer.printEmptyLn();
printer.printIndentedLn(`get${withUppercase}(): Uint8Array | string;`); printer.printLn(`export class ${messageName} extends jspb.Message {`);
printer.printIndentedLn(`get${withUppercase}_asU8(): Uint8Array;`);
printer.printIndentedLn(`get${withUppercase}_asB64(): string;`); const oneOfGroups: Array<Array<FieldDescriptorProto>> = [];
printer.printIndentedLn(`set${withUppercase}(value: Uint8Array | string): void;`);
} else { messageDescriptor.getFieldList().forEach((field) => {
let fieldObjectType = exportType; if (field.hasOneofIndex()) {
let canBeUndefined = false; const oneOfIndex = field.getOneofIndex();
if (type === MESSAGE_TYPE) { let existing = oneOfGroups[oneOfIndex];
fieldObjectType += ".AsObject"; if (existing === undefined) {
if (!isProto2(fileDescriptor) || (field.getLabel() === FieldDescriptorProto.Label.LABEL_OPTIONAL)) { existing = [];
canBeUndefined = true; oneOfGroups[oneOfIndex] = existing;
} }
} else { existing.push(field);
if (isProto2(fileDescriptor)) { }
canBeUndefined = true; const snakeCaseName = stripPrefix(field.getName().toLowerCase(), '_');
} const camelCaseName = snakeToCamel(snakeCaseName);
} const withUppercase = uppercaseFirst(camelCaseName);
const fieldObjectName = normaliseFieldObjectName(camelCaseName); const type = field.getType();
toObjectType.printIndentedLn(`${fieldObjectName}${canBeUndefined ? "?" : ""}: ${fieldObjectType},`);
printer.printIndentedLn(`get${withUppercase}(): ${exportType}${canBeUndefined ? " | undefined" : ""};`); let exportType;
printer.printIndentedLn(`set${withUppercase}(value${type === MESSAGE_TYPE ? "?" : ""}: ${exportType}): void;`); const fullTypeName = field.getTypeName().slice(1);
} if (type === MESSAGE_TYPE) {
} const fieldMessageType = exportMap.getMessage(fullTypeName);
printer.printEmptyLn(); if (fieldMessageType === undefined) {
}); throw new Error('No message export for: ' + fullTypeName);
}
toObjectType.printLn(`}`); if (
fieldMessageType.messageOptions !== undefined &&
messageDescriptor.getOneofDeclList().forEach(oneOfDecl => { fieldMessageType.messageOptions.getMapEntry()
printer.printIndentedLn(`get${oneOfName(oneOfDecl.getName())}Case(): ${messageName}.${oneOfName(oneOfDecl.getName())}Case;`); ) {
}); // This field is a map
const keyTuple = fieldMessageType.mapFieldOptions!.key;
printer.printIndentedLn(`serializeBinary(): Uint8Array;`); const keyType = keyTuple[0];
printer.printIndentedLn(`toObject(includeInstance?: boolean): ${messageName}.${objectTypeName};`); const keyTypeName = getFieldType(
printer.printIndentedLn(`static toObject(includeInstance: boolean, msg: ${messageName}): ${messageName}.${objectTypeName};`); keyType,
printer.printIndentedLn(`static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};`); keyTuple[1],
printer.printIndentedLn(`static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};`); fileName,
printer.printIndentedLn(`static serializeBinaryToWriter(message: ${messageName}, writer: jspb.BinaryWriter): void;`); exportMap
printer.printIndentedLn(`static deserializeBinary(bytes: Uint8Array): ${messageName};`); );
printer.printIndentedLn(`static deserializeBinaryFromReader(message: ${messageName}, reader: jspb.BinaryReader): ${messageName};`); const valueTuple = fieldMessageType.mapFieldOptions!.value;
const valueType = valueTuple[0];
printer.printLn(`}`); let valueTypeName = getFieldType(
printer.printEmptyLn(); valueType,
valueTuple[1],
printer.printLn(`export namespace ${messageName} {`); fileName,
exportMap
printer.print(toObjectType.getOutput()); );
if (valueType === BYTES_TYPE) {
messageDescriptor.getNestedTypeList().forEach(nested => { valueTypeName = 'Uint8Array | string';
const msgOutput = printMessage(fileName, exportMap, nested, indentLevel + 1, fileDescriptor); }
if (msgOutput !== "") { if (valueType === ENUM_TYPE) {
// If the message class is a Map entry then it isn't output, so don't print the namespace block valueTypeName = `${valueTypeName}[keyof ${valueTypeName}]`;
printer.print(msgOutput); }
} printer.printIndentedLn(
}); `get${withUppercase}Map(): jspb.Map<${keyTypeName}, ${valueTypeName}>;`
messageDescriptor.getEnumTypeList().forEach(enumType => { );
printer.print(`${printEnum(enumType, indentLevel + 1)}`); printer.printIndentedLn(`clear${withUppercase}Map(): void;`);
}); toObjectType.printIndentedLn(
messageDescriptor.getOneofDeclList().forEach((oneOfDecl, index) => { `${camelCaseName}Map: Array<[${keyTypeName}${
printer.print(`${printOneOfDecl(oneOfDecl, oneOfGroups[index] || [], indentLevel + 1)}`); keyType === MESSAGE_TYPE ? '.AsObject' : ''
}); }, ${valueTypeName}${
messageDescriptor.getExtensionList().forEach(extension => { valueType === MESSAGE_TYPE ? '.AsObject' : ''
printer.print(printExtension(fileName, exportMap, extension, indentLevel + 1)); }]>,`
}); );
return;
printer.printLn(`}`); }
const withinNamespace = withinNamespaceFromExportEntry(
return printer.getOutput(); 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<Uint8Array | string>,`
);
printer.printIndentedLn(
`get${withUppercase}List(): Array<Uint8Array | string>;`
);
printer.printIndentedLn(
`get${withUppercase}List_asU8(): Array<Uint8Array>;`
);
printer.printIndentedLn(
`get${withUppercase}List_asB64(): Array<string>;`
);
printer.printIndentedLn(
`set${withUppercase}List(value: Array<Uint8Array | string>): 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<jspb.Message>};`
);
printer.printIndentedLn(
`static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};`
);
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();
} }

View file

@ -1,16 +1,25 @@
import {Printer} from "../Printer"; import { Printer } from '../Printer';
import {OneofDescriptorProto, FieldDescriptorProto} from "google-protobuf/google/protobuf/descriptor_pb"; import {
import {oneOfName} from "../util"; OneofDescriptorProto,
FieldDescriptorProto,
} from 'google-protobuf/google/protobuf/descriptor_pb';
import { oneOfName } from '../util';
export function printOneOfDecl(oneOfDecl: OneofDescriptorProto, oneOfFields: Array<FieldDescriptorProto>, indentLevel: number) { export function printOneOfDecl(
const printer = new Printer(indentLevel); oneOfDecl: OneofDescriptorProto,
printer.printEmptyLn(); oneOfFields: Array<FieldDescriptorProto>,
printer.printLn(`export enum ${oneOfName(oneOfDecl.getName())}Case {`); indentLevel: number
printer.printIndentedLn(`${oneOfDecl.getName().toUpperCase()}_NOT_SET = 0,`); ) {
oneOfFields.forEach(field => { const printer = new Printer(indentLevel);
printer.printIndentedLn(`${field.getName().toUpperCase()} = ${field.getNumber()},`); printer.printEmptyLn();
}); printer.printLn(`export enum ${oneOfName(oneOfDecl.getName())}Case {`);
printer.printLn("}"); 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;
} }

View file

@ -1,184 +1,200 @@
import {parse} from "querystring"; import { parse } from 'querystring';
import {FileDescriptorProto} from "google-protobuf/google/protobuf/descriptor_pb"; import { FileDescriptorProto } from 'google-protobuf/google/protobuf/descriptor_pb';
import {ExportEnumEntry, ExportMessageEntry} from "./ExportMap"; import { ExportEnumEntry, ExportMessageEntry } from './ExportMap';
import {ServiceParameter, ModeParameter} from "./parameters"; import { ServiceParameter, ModeParameter } from './parameters';
export function filePathToPseudoNamespace(filePath: string): string { 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) { export function stripPrefix(str: string, prefix: string) {
if (str.substr(0, prefix.length) === prefix) { if (str.substr(0, prefix.length) === prefix) {
return str.substr(prefix.length); return str.substr(prefix.length);
} }
return str; return str;
} }
export function snakeToCamel(str: string): string { export function snakeToCamel(str: string): string {
return str.replace(/(\_\w)/g, function(m) { return str.replace(/(\_\w)/g, function (m) {
return m[1].toUpperCase(); return m[1].toUpperCase();
}); });
} }
export function uppercaseFirst(str: string): string { 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 { export function isProto2(fileDescriptor: FileDescriptorProto): boolean {
// Empty syntax defaults to proto2 // Empty syntax defaults to proto2
return (fileDescriptor.getSyntax() === "" || fileDescriptor.getSyntax() === PROTO2_SYNTAX); return (
fileDescriptor.getSyntax() === '' || fileDescriptor.getSyntax() === PROTO2_SYNTAX
);
} }
export function oneOfName(name: string) { export function oneOfName(name: string) {
return uppercaseFirst(snakeToCamel(name.toLowerCase())); return uppercaseFirst(snakeToCamel(name.toLowerCase()));
} }
export function generateIndent(indentLevel: number): string { export function generateIndent(indentLevel: number): string {
let indent = ""; let indent = '';
for (let i = 0; i < indentLevel; i++) { for (let i = 0; i < indentLevel; i++) {
indent += " "; indent += ' ';
} }
return indent; return indent;
} }
export function getPathToRoot(fileName: string) { export function getPathToRoot(fileName: string) {
const depth = fileName.split("/").length; const depth = fileName.split('/').length;
return depth === 1 ? "./" : new Array(depth).join("../"); return depth === 1 ? './' : new Array(depth).join('../');
} }
export function withinNamespaceFromExportEntry(name: string, exportEntry: ExportMessageEntry | ExportEnumEntry) { export function withinNamespaceFromExportEntry(
return exportEntry.pkg ? name.substring(exportEntry.pkg.length + 1) : name; name: string,
exportEntry: ExportMessageEntry | ExportEnumEntry
) {
return exportEntry.pkg ? name.substring(exportEntry.pkg.length + 1) : name;
} }
export function replaceProtoSuffix(protoFilePath: string): string { export function replaceProtoSuffix(protoFilePath: string): string {
const suffix = ".proto"; const suffix = '.proto';
const hasProtoSuffix = protoFilePath.slice(protoFilePath.length - suffix.length) === suffix; const hasProtoSuffix =
return hasProtoSuffix protoFilePath.slice(protoFilePath.length - suffix.length) === suffix;
? protoFilePath.slice(0, -suffix.length) + "_pb" return hasProtoSuffix
: protoFilePath; ? protoFilePath.slice(0, -suffix.length) + '_pb'
: protoFilePath;
} }
export function withAllStdIn(callback: (buffer: Buffer) => void): void { export function withAllStdIn(callback: (buffer: Buffer) => void): void {
const ret: Buffer[] = []; const ret: Buffer[] = [];
let len = 0; let len = 0;
const stdin = process.stdin; const stdin = process.stdin;
stdin.on("readable", function () { stdin.on('readable', function () {
let chunk; let chunk;
while ((chunk = stdin.read())) { while ((chunk = stdin.read())) {
if (!(chunk instanceof Buffer)) throw new Error("Did not receive buffer"); if (!(chunk instanceof Buffer)) throw new Error('Did not receive buffer');
ret.push(chunk); ret.push(chunk);
len += chunk.length; len += chunk.length;
} }
}); });
stdin.on("end", function () { stdin.on('end', function () {
callback(Buffer.concat(ret, len)); callback(Buffer.concat(ret, len));
}); });
} }
// normaliseFieldObjectName modifies the field name that appears in the `asObject` representation // 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 // to match the logic found in `protobuf/compiler/js/js_generator.cc`. See: https://goo.gl/tX1dPQ
export function normaliseFieldObjectName(name: string): string { export function normaliseFieldObjectName(name: string): string {
switch (name) { switch (name) {
case "abstract": case 'abstract':
case "boolean": case 'boolean':
case "break": case 'break':
case "byte": case 'byte':
case "case": case 'case':
case "catch": case 'catch':
case "char": case 'char':
case "class": case 'class':
case "const": case 'const':
case "continue": case 'continue':
case "debugger": case 'debugger':
case "default": case 'default':
case "delete": case 'delete':
case "do": case 'do':
case "double": case 'double':
case "else": case 'else':
case "enum": case 'enum':
case "export": case 'export':
case "extends": case 'extends':
case "false": case 'false':
case "final": case 'final':
case "finally": case 'finally':
case "float": case 'float':
case "for": case 'for':
case "function": case 'function':
case "goto": case 'goto':
case "if": case 'if':
case "implements": case 'implements':
case "import": case 'import':
case "in": case 'in':
case "instanceof": case 'instanceof':
case "int": case 'int':
case "interface": case 'interface':
case "long": case 'long':
case "native": case 'native':
case "new": case 'new':
case "null": case 'null':
case "package": case 'package':
case "private": case 'private':
case "protected": case 'protected':
case "public": case 'public':
case "return": case 'return':
case "short": case 'short':
case "static": case 'static':
case "super": case 'super':
case "switch": case 'switch':
case "synchronized": case 'synchronized':
case "this": case 'this':
case "throw": case 'throw':
case "throws": case 'throws':
case "transient": case 'transient':
case "try": case 'try':
case "typeof": case 'typeof':
case "var": case 'var':
case "void": case 'void':
case "volatile": case 'volatile':
case "while": case 'while':
case "with": case 'with':
return `pb_${name}`; return `pb_${name}`;
} }
return name; return name;
} }
export function getServiceParameter(service?: string): ServiceParameter { export function getServiceParameter(service?: string): ServiceParameter {
switch (service) { switch (service) {
case "true": case 'true':
console.warn("protoc-gen-ts warning: The service=true parameter has been deprecated. Use service=grpc-web instead."); console.warn(
return ServiceParameter.GrpcWeb; 'protoc-gen-ts warning: The service=true parameter has been deprecated. Use service=grpc-web instead.'
case "grpc-web": );
return ServiceParameter.GrpcWeb; return ServiceParameter.GrpcWeb;
case "grpc-node": case 'grpc-web':
return ServiceParameter.GrpcNode; return ServiceParameter.GrpcWeb;
case undefined: case 'grpc-node':
return ServiceParameter.None; return ServiceParameter.GrpcNode;
default: case undefined:
throw new Error(`Unrecognised service parameter: ${service}`); return ServiceParameter.None;
} default:
throw new Error(`Unrecognised service parameter: ${service}`);
}
} }
export function getModeParameter(mode?: string): ModeParameter { export function getModeParameter(mode?: string): ModeParameter {
switch (mode) { switch (mode) {
case "grpc-js": case 'grpc-js':
return ModeParameter.GrpcJs; return ModeParameter.GrpcJs;
case undefined: case undefined:
return ModeParameter.None; return ModeParameter.None;
default: default:
throw new Error(`Unrecognised mode parameter: ${mode}`); throw new Error(`Unrecognised mode parameter: ${mode}`);
} }
} }
export function getParameterEnums(parameter: string): { export function getParameterEnums(
service: ServiceParameter, parameter: string
mode: ModeParameter ): {
service: ServiceParameter;
mode: ModeParameter;
} { } {
const {service, mode} = parse(parameter, ","); const { service, mode } = parse(parameter, ',');
return { return {
service: getServiceParameter(service), service: getServiceParameter(service),
mode: getModeParameter(mode) mode: getModeParameter(mode),
}; };
} }