initial commit of actions

This commit is contained in:
Dominik Polakovics Polakovics 2026-01-31 18:56:04 +01:00
commit 949ece5785
44660 changed files with 12034344 additions and 0 deletions

View file

@ -0,0 +1,48 @@
import * as ts from "typescript";
import { AnyDescriptorProto, DescriptorRegistry } from "@protobuf-ts/plugin-framework";
export declare class CommentGenerator {
private readonly registry;
constructor(registry: DescriptorRegistry);
/**
* Adds comments from the .proto as a JSDoc block.
*
* Looks up comments for the given descriptor in
* the source code info.
*
* Adds `@deprecated` tag if the element is
* marked deprecated. Also adds @deprecated if
* the descriptor is a type (enum, message) and
* the entire .proto file is marked deprecated.
*
* Adds `@generated` tag with source code
* information.
*
* Leading detached comments are added as line
* comments in front of the JSDoc block.
*
* Trailing comments are a bit weird. For .proto
* enums and messages, they sit between open
* bracket and first member. A message seems to
* only ever have a trailing comment if it is
* empty. For a simple solution, trailing
* comments on enums and messages should simply
* be appended to the leading block so that the
* information is not discarded.
*/
addCommentsForDescriptor(node: ts.Node, descriptor: AnyDescriptorProto, trailingCommentsMode: 'appendToLeadingBlock' | 'trailingLines'): void;
/**
* Returns a block of source comments (no leading detached!),
* with @generated tags and @deprecated tag (if applicable).
*/
getCommentBlock(descriptor: AnyDescriptorProto, appendTrailingComments?: boolean): string;
/**
* Returns "@deprecated\n" if explicitly deprecated.
* For top level types, also returns "@deprecated\n" if entire file is deprecated.
* Otherwise, returns "".
*/
makeDeprecatedTag(descriptor: AnyDescriptorProto): "" | "@deprecated\n";
/**
* Creates string like "@generated from protobuf field: string foo = 1;"
*/
makeGeneratedTag(descriptor: AnyDescriptorProto): string;
}

View file

@ -0,0 +1,117 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CommentGenerator = void 0;
const ts = require("typescript");
const plugin_framework_1 = require("@protobuf-ts/plugin-framework");
class CommentGenerator {
constructor(registry) {
this.registry = registry;
}
/**
* Adds comments from the .proto as a JSDoc block.
*
* Looks up comments for the given descriptor in
* the source code info.
*
* Adds `@deprecated` tag if the element is
* marked deprecated. Also adds @deprecated if
* the descriptor is a type (enum, message) and
* the entire .proto file is marked deprecated.
*
* Adds `@generated` tag with source code
* information.
*
* Leading detached comments are added as line
* comments in front of the JSDoc block.
*
* Trailing comments are a bit weird. For .proto
* enums and messages, they sit between open
* bracket and first member. A message seems to
* only ever have a trailing comment if it is
* empty. For a simple solution, trailing
* comments on enums and messages should simply
* be appended to the leading block so that the
* information is not discarded.
*/
addCommentsForDescriptor(node, descriptor, trailingCommentsMode) {
const source = this.registry.sourceCodeComments(descriptor);
// add leading detached comments as line comments
plugin_framework_1.addCommentBlocksAsLeadingDetachedLines(node, ...source.leadingDetached);
// start with leading block
let leading = this.getCommentBlock(descriptor, trailingCommentsMode === "appendToLeadingBlock");
// add leading block as jsdoc comment block
plugin_framework_1.addCommentBlockAsJsDoc(node, leading);
// add trailing comments as trailing line comments
if (source.trailing && trailingCommentsMode === 'trailingLines') {
let lines = source.trailing.split('\n').map(l => l[0] !== ' ' ? ` ${l}` : l);
for (let line of lines) {
ts.addSyntheticTrailingComment(node, ts.SyntaxKind.SingleLineCommentTrivia, line, true);
}
}
}
/**
* Returns a block of source comments (no leading detached!),
* with @generated tags and @deprecated tag (if applicable).
*/
getCommentBlock(descriptor, appendTrailingComments = false) {
var _a;
const source = this.registry.sourceCodeComments(descriptor);
// start with leading block
let commentBlock = (_a = source.leading) !== null && _a !== void 0 ? _a : '';
// add trailing comments to the leading block
if (source.trailing && appendTrailingComments) {
if (commentBlock.length > 0) {
commentBlock += '\n\n';
}
commentBlock += source.trailing;
}
// if there were any leading comments, we need some space
if (commentBlock.length > 0) {
commentBlock += '\n\n';
}
// add deprecated information to the leading block
commentBlock += this.makeDeprecatedTag(descriptor);
// add source info to the leading block
commentBlock += this.makeGeneratedTag(descriptor);
return commentBlock;
}
/**
* Returns "@deprecated\n" if explicitly deprecated.
* For top level types, also returns "@deprecated\n" if entire file is deprecated.
* Otherwise, returns "".
*/
makeDeprecatedTag(descriptor) {
let deprecated = this.registry.isExplicitlyDeclaredDeprecated(descriptor);
if (!deprecated && plugin_framework_1.isAnyTypeDescriptorProto(descriptor)) {
// an entire .proto file can be marked deprecated.
// this means all types within are deprecated.
// we mark them as deprecated, but dont touch members.
deprecated = this.registry.isExplicitlyDeclaredDeprecated(this.registry.fileOf(descriptor));
}
if (deprecated) {
return '@deprecated\n';
}
return '';
}
/**
* Creates string like "@generated from protobuf field: string foo = 1;"
*/
makeGeneratedTag(descriptor) {
if (plugin_framework_1.OneofDescriptorProto.is(descriptor)) {
return `@generated from protobuf oneof: ${descriptor.name}`;
}
else if (plugin_framework_1.EnumValueDescriptorProto.is(descriptor)) {
return `@generated from protobuf enum value: ${this.registry.formatEnumValueDeclaration(descriptor)}`;
}
else if (plugin_framework_1.FieldDescriptorProto.is(descriptor)) {
return `@generated from protobuf field: ${this.registry.formatFieldDeclaration(descriptor)}`;
}
else if (plugin_framework_1.MethodDescriptorProto.is(descriptor)) {
return `@generated from protobuf rpc: ${this.registry.formatRpcDeclaration(descriptor)}`;
}
else {
return `@generated from protobuf ${this.registry.formatQualifiedName(descriptor)}`;
}
}
}
exports.CommentGenerator = CommentGenerator;

View file

@ -0,0 +1,50 @@
import * as ts from "typescript";
import { DescriptorRegistry, EnumDescriptorProto, SymbolTable, TypescriptFile, TypeScriptImports } from "@protobuf-ts/plugin-framework";
import { CommentGenerator } from "./comment-generator";
import { Interpreter } from "../interpreter";
import { GeneratorBase } from "./generator-base";
export declare class EnumGenerator extends GeneratorBase {
private readonly options;
constructor(symbols: SymbolTable, registry: DescriptorRegistry, imports: TypeScriptImports, comments: CommentGenerator, interpreter: Interpreter, options: {});
/**
* For the following .proto:
*
* ```proto
* enum MyEnum {
* ANY = 0;
* YES = 1;
* NO = 2;
* }
* ```
*
* We generate the following enum:
*
* ```typescript
* enum MyEnum {
* ANY = 0,
* YES = 1,
* NO = 2
* }
* ```
*
* We drop a shared prefix, for example:
*
* ```proto
* enum MyEnum {
* MY_ENUM_FOO = 0;
* MY_ENUM_BAR = 1;
* }
* ```
*
* Becomes:
*
* ```typescript
* enum MyEnum {
* FOO = 0,
* BAR = 1,
* }
* ```
*
*/
generateEnum(source: TypescriptFile, descriptor: EnumDescriptorProto): ts.EnumDeclaration;
}

View file

@ -0,0 +1,69 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EnumGenerator = void 0;
const ts = require("typescript");
const rt = require("@protobuf-ts/runtime");
const plugin_framework_1 = require("@protobuf-ts/plugin-framework");
const generator_base_1 = require("./generator-base");
class EnumGenerator extends generator_base_1.GeneratorBase {
constructor(symbols, registry, imports, comments, interpreter, options) {
super(symbols, registry, imports, comments, interpreter);
this.options = options;
}
/**
* For the following .proto:
*
* ```proto
* enum MyEnum {
* ANY = 0;
* YES = 1;
* NO = 2;
* }
* ```
*
* We generate the following enum:
*
* ```typescript
* enum MyEnum {
* ANY = 0,
* YES = 1,
* NO = 2
* }
* ```
*
* We drop a shared prefix, for example:
*
* ```proto
* enum MyEnum {
* MY_ENUM_FOO = 0;
* MY_ENUM_BAR = 1;
* }
* ```
*
* Becomes:
*
* ```typescript
* enum MyEnum {
* FOO = 0,
* BAR = 1,
* }
* ```
*
*/
generateEnum(source, descriptor) {
let enumObject = this.interpreter.getEnumInfo(descriptor)[1], builder = new plugin_framework_1.TypescriptEnumBuilder();
for (let ev of rt.listEnumValues(enumObject)) {
let evDescriptor = descriptor.value.find(v => v.number === ev.number);
let comments = evDescriptor
? this.comments.getCommentBlock(evDescriptor, true)
: "@generated synthetic value - protobuf-ts requires all enums to have a 0 value";
builder.add(ev.name, ev.number, comments);
}
let statement = builder.build(this.imports.type(source, descriptor), [ts.createModifier(ts.SyntaxKind.ExportKeyword)]);
// add to our file
source.addStatement(statement);
this.comments.addCommentsForDescriptor(statement, descriptor, 'appendToLeadingBlock');
return statement;
}
}
exports.EnumGenerator = EnumGenerator;

View file

@ -0,0 +1,32 @@
import * as rt from "@protobuf-ts/runtime";
import * as ts from "typescript";
import { DescriptorRegistry, TypescriptFile, TypeScriptImports } from "@protobuf-ts/plugin-framework";
/**
* Generates TypeScript code for runtime field information,
* from runtime field information.
*/
export declare class FieldInfoGenerator {
private readonly registry;
private readonly imports;
private readonly options;
constructor(registry: DescriptorRegistry, imports: TypeScriptImports, options: {});
createFieldInfoLiterals(source: TypescriptFile, fieldInfos: readonly rt.PartialFieldInfo[]): ts.ArrayLiteralExpression;
createFieldInfoLiteral(source: TypescriptFile, fieldInfo: rt.PartialFieldInfo): ts.ObjectLiteralExpression;
/**
* Creates the interface field / oneof name based on original proto field name and naming options.
*/
static createTypescriptLocalName(name: string, options: {
useProtoFieldName: boolean;
}): string;
/**
* Turn normalized field info returned by normalizeFieldInfo() back into
* the minimized form.
*/
private static denormalizeFieldInfo;
private createMessageT;
private createEnumT;
private createRepeatType;
private createScalarType;
private createLongType;
private createMapV;
}

View file

@ -0,0 +1,159 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FieldInfoGenerator = void 0;
const rt = require("@protobuf-ts/runtime");
const ts = require("typescript");
const plugin_framework_1 = require("@protobuf-ts/plugin-framework");
/**
* Generates TypeScript code for runtime field information,
* from runtime field information.
*/
class FieldInfoGenerator {
constructor(registry, imports, options) {
this.registry = registry;
this.imports = imports;
this.options = options;
}
createFieldInfoLiterals(source, fieldInfos) {
fieldInfos = fieldInfos.map(fi => FieldInfoGenerator.denormalizeFieldInfo(fi));
return ts.createArrayLiteral(fieldInfos.map(fi => this.createFieldInfoLiteral(source, fi)), true);
}
createFieldInfoLiteral(source, fieldInfo) {
fieldInfo = FieldInfoGenerator.denormalizeFieldInfo(fieldInfo);
let properties = [];
// no: The field number of the .proto field.
// name: The original name of the .proto field.
// kind: discriminator
// localName: The name of the field in the runtime.
// jsonName: The name of the field in JSON.
// oneof: The name of the `oneof` group, if this field belongs to one.
for (let key of ["no", "name", "kind", "localName", "jsonName", "oneof"]) {
if (fieldInfo[key] !== undefined) {
properties.push(ts.createPropertyAssignment(key, plugin_framework_1.typescriptLiteralFromValue(fieldInfo[key])));
}
}
// repeat: Is the field repeated?
if (fieldInfo.repeat !== undefined) {
properties.push(ts.createPropertyAssignment("repeat", this.createRepeatType(fieldInfo.repeat)));
}
// opt: Is the field optional?
if (fieldInfo.opt !== undefined) {
properties.push(ts.createPropertyAssignment("opt", plugin_framework_1.typescriptLiteralFromValue(fieldInfo.opt)));
}
switch (fieldInfo.kind) {
case "scalar":
// T: Scalar field type.
properties.push(ts.createPropertyAssignment("T", this.createScalarType(fieldInfo.T)));
// L?: JavaScript long type
if (fieldInfo.L !== undefined) {
properties.push(ts.createPropertyAssignment("L", this.createLongType(fieldInfo.L)));
}
break;
case "enum":
// T: Return enum field type info.
properties.push(ts.createPropertyAssignment(ts.createIdentifier('T'), this.createEnumT(source, fieldInfo.T())));
break;
case "message":
// T: Return message field type handler.
properties.push(ts.createPropertyAssignment(ts.createIdentifier('T'), this.createMessageT(source, fieldInfo.T())));
break;
case "map":
// K: Map field key type.
properties.push(ts.createPropertyAssignment("K", this.createScalarType(fieldInfo.K)));
// V: Map field value type.
properties.push(ts.createPropertyAssignment("V", this.createMapV(source, fieldInfo.V)));
break;
}
// options:
if (fieldInfo.options) {
properties.push(ts.createPropertyAssignment(ts.createIdentifier('options'), plugin_framework_1.typescriptLiteralFromValue(fieldInfo.options)));
}
return ts.createObjectLiteral(properties, false);
}
/**
* Creates the interface field / oneof name based on original proto field name and naming options.
*/
static createTypescriptLocalName(name, options) {
return options.useProtoFieldName ? name : rt.lowerCamelCase(name);
}
/**
* Turn normalized field info returned by normalizeFieldInfo() back into
* the minimized form.
*/
static denormalizeFieldInfo(info) {
let partial = Object.assign({}, info);
if (info.jsonName === rt.lowerCamelCase(info.name)) {
delete partial.jsonName;
}
if (info.localName === rt.lowerCamelCase(info.name)) {
delete partial.localName;
}
if (info.repeat === rt.RepeatType.NO) {
delete partial.repeat;
}
if (info.opt === false) {
delete partial.opt;
}
else if (info.opt === true && info.kind == "message") {
delete partial.opt;
}
return partial;
}
createMessageT(source, type) {
let descriptor = this.registry.resolveTypeName(type.typeName);
let generatedMessage = this.imports.type(source, descriptor);
return ts.createArrowFunction(undefined, undefined, [], undefined, ts.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.createIdentifier(generatedMessage));
}
createEnumT(source, ei) {
let [pbTypeName, , sharedPrefix] = ei, descriptor = this.registry.resolveTypeName(pbTypeName), generatedEnum = this.imports.type(source, descriptor), enumInfoLiteral = [
ts.createStringLiteral(pbTypeName),
ts.createIdentifier(generatedEnum),
];
if (sharedPrefix) {
enumInfoLiteral.push(ts.createStringLiteral(sharedPrefix));
}
return ts.createArrowFunction(undefined, undefined, [], undefined, ts.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.createArrayLiteral(enumInfoLiteral, false));
}
createRepeatType(type) {
const expr = ts.createNumericLiteral(type.toString());
ts.addSyntheticTrailingComment(expr, ts.SyntaxKind.MultiLineCommentTrivia, `RepeatType.${rt.RepeatType[type]}`);
return expr;
}
createScalarType(type) {
const expr = ts.createNumericLiteral(type.toString());
ts.addSyntheticTrailingComment(expr, ts.SyntaxKind.MultiLineCommentTrivia, `ScalarType.${rt.ScalarType[type]}`);
return expr;
}
createLongType(type) {
const expr = ts.createNumericLiteral(type.toString());
ts.addSyntheticTrailingComment(expr, ts.SyntaxKind.MultiLineCommentTrivia, `LongType.${rt.LongType[type]}`);
return expr;
}
// V: Map field value type.
createMapV(source, mapV) {
let T;
let L = undefined;
switch (mapV.kind) {
case "message":
T = this.createMessageT(source, mapV.T());
break;
case "enum":
T = this.createEnumT(source, mapV.T());
break;
case "scalar":
T = this.createScalarType(mapV.T);
if (mapV.L !== undefined)
L = this.createLongType(mapV.L);
break;
}
const properties = [
ts.createPropertyAssignment(ts.createIdentifier('kind'), ts.createStringLiteral(mapV.kind)),
ts.createPropertyAssignment(ts.createIdentifier('T'), T)
];
if (L) {
properties.push(ts.createPropertyAssignment(ts.createIdentifier('L'), L));
}
return ts.createObjectLiteral(properties);
}
}
exports.FieldInfoGenerator = FieldInfoGenerator;

View file

@ -0,0 +1,11 @@
import { CommentGenerator } from "./comment-generator";
import { DescriptorRegistry, SymbolTable, TypeScriptImports } from "@protobuf-ts/plugin-framework";
import { Interpreter } from "../interpreter";
export declare abstract class GeneratorBase {
protected readonly symbols: SymbolTable;
protected readonly registry: DescriptorRegistry;
protected readonly imports: TypeScriptImports;
protected readonly comments: CommentGenerator;
protected readonly interpreter: Interpreter;
protected constructor(symbols: SymbolTable, registry: DescriptorRegistry, imports: TypeScriptImports, comments: CommentGenerator, interpreter: Interpreter);
}

View file

@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GeneratorBase = void 0;
class GeneratorBase {
constructor(symbols, registry, imports, comments, interpreter) {
this.symbols = symbols;
this.registry = registry;
this.imports = imports;
this.comments = comments;
this.interpreter = interpreter;
}
}
exports.GeneratorBase = GeneratorBase;

View file

@ -0,0 +1,10 @@
import { AnyTypeDescriptorProto, IDescriptorTree } from "@protobuf-ts/plugin-framework";
/**
* Create a name for an enum, message or service.
* - ignores package
* - nested types get the names merged with '_'
* - reserved words are escaped by adding '$' at the end
* - does *not* prevent clashes, for example clash
* of merged nested name with other message name
*/
export declare function createLocalTypeName(descriptor: AnyTypeDescriptorProto, treeLookup: IDescriptorTree): string;

View file

@ -0,0 +1,46 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createLocalTypeName = void 0;
const plugin_framework_1 = require("@protobuf-ts/plugin-framework");
const runtime_1 = require("@protobuf-ts/runtime");
// TODO in generated code, use globalThis for all types in global scope
// rationale: keywords are much less likely to change than objects in the global scope
// TODO move all code creating names into one place (see interpreter.ts for more code)
const reservedKeywords = 'break,case,catch,class,const,continue,debugger,default,delete,do,else,enum,export,extends,false,finally,for,function,if,import,in,instanceof,new,null,return,super,switch,this,throw,true,try,typeof,var,void,while,with,as,implements,interface,let,package,private,protected,public,static,yield,any,boolean,constructor,declare,get,module,require,number,set,string,symbol,type,from,of'.split(',');
const reservedTypeNames = 'object,Uint8Array,array,Array,string,String,number,Number,boolean,Boolean,bigint,BigInt'.split(',');
const escapeCharacter = '$';
/**
* Create a name for an enum, message or service.
* - ignores package
* - nested types get the names merged with '_'
* - reserved words are escaped by adding '$' at the end
* - does *not* prevent clashes, for example clash
* of merged nested name with other message name
*/
function createLocalTypeName(descriptor, treeLookup) {
// build name components for parent types
const components = [];
for (const ancestor of treeLookup.ancestorsOf(descriptor)) {
if (plugin_framework_1.FileDescriptorProto.is(ancestor)) {
continue;
}
const name = ancestor.name;
runtime_1.assert(name !== undefined);
components.push(name);
}
// add name for actual descriptor
const name = descriptor.name;
runtime_1.assert(name !== undefined);
components.push(name);
// join all components with underscore
let fullName = components.join('_');
// escape if reserved
if (reservedKeywords.includes(fullName)) {
fullName = fullName + escapeCharacter;
}
if (reservedTypeNames.includes(fullName)) {
fullName = fullName + escapeCharacter;
}
return fullName;
}
exports.createLocalTypeName = createLocalTypeName;

View file

@ -0,0 +1,65 @@
import * as ts from "typescript";
import * as rt from "@protobuf-ts/runtime";
import { DescriptorProto, DescriptorRegistry, FieldDescriptorProto, OneofDescriptorProto, SymbolTable, TypescriptFile, TypeScriptImports } from "@protobuf-ts/plugin-framework";
import { CommentGenerator } from "./comment-generator";
import { Interpreter } from "../interpreter";
import { GeneratorBase } from "./generator-base";
export declare class MessageInterfaceGenerator extends GeneratorBase {
private readonly options;
constructor(symbols: SymbolTable, registry: DescriptorRegistry, imports: TypeScriptImports, comments: CommentGenerator, interpreter: Interpreter, options: {
oneofKindDiscriminator: string;
normalLongType: rt.LongType;
});
registerSymbols(source: TypescriptFile, descriptor: DescriptorProto): void;
/**
* `message` as an interface.
*
* For the following .proto:
*
* message MyMessage {
* string str_field = 1;
* }
*
* We generate the following interface:
*
* interface MyMessage {
* strField: string;
* }
*
*/
generateMessageInterface(source: TypescriptFile, descriptor: DescriptorProto): ts.InterfaceDeclaration;
/**
* Create property signature for a protobuf field. Example:
*
* fieldName: number
*
*/
protected createFieldPropertySignature(source: TypescriptFile, fieldDescriptor: FieldDescriptorProto, fieldInfo: rt.FieldInfo): ts.PropertySignature;
/**
* `oneof` as an algebraic data type.
*
* For the following .proto:
*
* oneof result {
* int32 value = 1;
* string error = 2;
* }
*
* We generate the following property signature:
*
* result: { oneofKind: "value"; value: number; }
* | { oneofKind: "error"; error: string; }
* | { oneofKind: undefined; };
*/
protected createOneofADTPropertySignature(source: TypescriptFile, oneofDescriptor: OneofDescriptorProto): ts.PropertySignature;
/**
* Helper to find for a OneofDescriptorProto:
* [0] the message descriptor
* [1] a corresponding message type generated by the interpreter
* [2] the runtime local name of the oneof
*/
private oneofInfo;
private createScalarTypeNode;
private createMessageTypeNode;
private createEnumTypeNode;
}

View file

@ -0,0 +1,221 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MessageInterfaceGenerator = void 0;
const ts = require("typescript");
const rt = require("@protobuf-ts/runtime");
const runtime_1 = require("@protobuf-ts/runtime");
const plugin_framework_1 = require("@protobuf-ts/plugin-framework");
const generator_base_1 = require("./generator-base");
const local_type_name_1 = require("./local-type-name");
class MessageInterfaceGenerator extends generator_base_1.GeneratorBase {
constructor(symbols, registry, imports, comments, interpreter, options) {
super(symbols, registry, imports, comments, interpreter);
this.options = options;
}
registerSymbols(source, descriptor) {
const name = local_type_name_1.createLocalTypeName(descriptor, this.registry);
this.symbols.register(name, descriptor, source);
}
/**
* `message` as an interface.
*
* For the following .proto:
*
* message MyMessage {
* string str_field = 1;
* }
*
* We generate the following interface:
*
* interface MyMessage {
* strField: string;
* }
*
*/
generateMessageInterface(source, descriptor) {
const interpreterType = this.interpreter.getMessageType(descriptor), processedOneofs = [], // oneof groups already processed
members = []; // the interface members
for (let fieldInfo of interpreterType.fields) {
let fieldDescriptor = descriptor.field.find(fd => fd.number === fieldInfo.no);
runtime_1.assert(fieldDescriptor !== undefined);
if (fieldInfo.oneof) {
if (processedOneofs.includes(fieldInfo.oneof)) {
continue;
}
// create single property for entire oneof group
runtime_1.assert(fieldDescriptor.oneofIndex !== undefined);
let oneofDescriptor = descriptor.oneofDecl[fieldDescriptor.oneofIndex];
runtime_1.assert(oneofDescriptor !== undefined);
members.push(this.createOneofADTPropertySignature(source, oneofDescriptor));
processedOneofs.push(fieldInfo.oneof);
}
else {
// create regular properties
members.push(this.createFieldPropertySignature(source, fieldDescriptor, fieldInfo));
}
}
// export interface MyMessage { ...
const statement = ts.createInterfaceDeclaration(undefined, [ts.createModifier(ts.SyntaxKind.ExportKeyword)], this.imports.type(source, descriptor), undefined, undefined, members);
// add to our file
source.addStatement(statement);
this.comments.addCommentsForDescriptor(statement, descriptor, 'appendToLeadingBlock');
return statement;
}
/**
* Create property signature for a protobuf field. Example:
*
* fieldName: number
*
*/
createFieldPropertySignature(source, fieldDescriptor, fieldInfo) {
let type; // the property type, may be made optional or wrapped into array at the end
switch (fieldInfo.kind) {
case "scalar":
type = this.createScalarTypeNode(fieldInfo.T, fieldInfo.L);
break;
case "enum":
type = this.createEnumTypeNode(source, fieldInfo.T());
break;
case "message":
type = this.createMessageTypeNode(source, fieldInfo.T());
break;
case "map":
let keyType = fieldInfo.K === rt.ScalarType.BOOL
? ts.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
: this.createScalarTypeNode(fieldInfo.K, rt.LongType.STRING);
let valueType;
switch (fieldInfo.V.kind) {
case "scalar":
valueType = this.createScalarTypeNode(fieldInfo.V.T, fieldInfo.V.L);
break;
case "enum":
valueType = this.createEnumTypeNode(source, fieldInfo.V.T());
break;
case "message":
valueType = this.createMessageTypeNode(source, fieldInfo.V.T());
break;
}
type = ts.createTypeLiteralNode([
ts.createIndexSignature(undefined, undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier('key'), undefined, keyType, undefined)
], valueType)
]);
break;
default:
throw new Error("unkown kind " + fieldDescriptor.name);
}
// if repeated, wrap type into array type
if (fieldInfo.repeat) {
type = ts.createArrayTypeNode(type);
}
// if optional, add question mark
let questionToken = fieldInfo.opt ? ts.createToken(ts.SyntaxKind.QuestionToken) : undefined;
// create property
const property = ts.createPropertySignature(undefined, ts.createIdentifier(fieldInfo.localName), questionToken, type, undefined);
this.comments.addCommentsForDescriptor(property, fieldDescriptor, 'trailingLines');
return property;
}
/**
* `oneof` as an algebraic data type.
*
* For the following .proto:
*
* oneof result {
* int32 value = 1;
* string error = 2;
* }
*
* We generate the following property signature:
*
* result: { oneofKind: "value"; value: number; }
* | { oneofKind: "error"; error: string; }
* | { oneofKind: undefined; };
*/
createOneofADTPropertySignature(source, oneofDescriptor) {
const oneofCases = [], [messageDescriptor, interpreterType, oneofLocalName] = this.oneofInfo(oneofDescriptor), memberFieldInfos = interpreterType.fields.filter(fi => fi.oneof === oneofLocalName);
runtime_1.assert(oneofDescriptor !== undefined);
// create a type for each selection case
for (let fieldInfo of memberFieldInfos) {
// { oneofKind: 'fieldName' ... } part
const kindProperty = ts.createPropertySignature(undefined, ts.createIdentifier(this.options.oneofKindDiscriminator), undefined, ts.createLiteralTypeNode(ts.createStringLiteral(fieldInfo.localName)), undefined);
// { ..., fieldName: type } part
let fieldDescriptor = messageDescriptor.field.find(fd => fd.number === fieldInfo.no);
runtime_1.assert(fieldDescriptor !== undefined);
let valueProperty = this.createFieldPropertySignature(source, fieldDescriptor, fieldInfo);
// add this case
oneofCases.push(ts.createTypeLiteralNode([kindProperty, valueProperty]));
}
// case for no selection: { oneofKind: undefined; }
oneofCases.push(ts.createTypeLiteralNode([
ts.createPropertySignature(undefined, ts.createIdentifier(this.options.oneofKindDiscriminator), undefined, ts.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword), undefined)
]));
// final property signature for the oneof group, with a union type for all oneof cases
const property = ts.createPropertySignature(undefined, ts.createIdentifier(oneofLocalName), undefined, ts.createUnionTypeNode(oneofCases), undefined);
// add comments
this.comments.addCommentsForDescriptor(property, oneofDescriptor, 'appendToLeadingBlock');
return property;
}
/**
* Helper to find for a OneofDescriptorProto:
* [0] the message descriptor
* [1] a corresponding message type generated by the interpreter
* [2] the runtime local name of the oneof
*/
oneofInfo(oneofDescriptor) {
const messageDescriptor = this.registry.parentOf(oneofDescriptor);
runtime_1.assert(plugin_framework_1.DescriptorProto.is(messageDescriptor));
const interpreterType = this.interpreter.getMessageType(messageDescriptor);
const oneofIndex = messageDescriptor.oneofDecl.indexOf(oneofDescriptor);
runtime_1.assert(oneofIndex !== undefined);
const sampleFieldDescriptor = messageDescriptor.field.find(fd => fd.oneofIndex === oneofIndex);
runtime_1.assert(sampleFieldDescriptor !== undefined);
const sampleFieldInfo = interpreterType.fields.find(fi => fi.no === sampleFieldDescriptor.number);
runtime_1.assert(sampleFieldInfo !== undefined);
const oneofName = sampleFieldInfo.oneof;
runtime_1.assert(oneofName !== undefined);
return [messageDescriptor, interpreterType, oneofName];
}
createScalarTypeNode(scalarType, longType) {
switch (scalarType) {
case rt.ScalarType.BOOL:
return ts.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
case rt.ScalarType.STRING:
return ts.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
case rt.ScalarType.BYTES:
return ts.createTypeReferenceNode('Uint8Array', undefined);
case rt.ScalarType.DOUBLE:
case rt.ScalarType.FLOAT:
case rt.ScalarType.INT32:
case rt.ScalarType.FIXED32:
case rt.ScalarType.UINT32:
case rt.ScalarType.SFIXED32:
case rt.ScalarType.SINT32:
return ts.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
case rt.ScalarType.SFIXED64:
case rt.ScalarType.INT64:
case rt.ScalarType.UINT64:
case rt.ScalarType.FIXED64:
case rt.ScalarType.SINT64:
switch (longType !== null && longType !== void 0 ? longType : rt.LongType.STRING) {
case rt.LongType.STRING:
return ts.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
case rt.LongType.NUMBER:
return ts.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
case rt.LongType.BIGINT:
return ts.createKeywordTypeNode(ts.SyntaxKind.BigIntKeyword);
}
}
}
createMessageTypeNode(source, type) {
let messageDescriptor = this.registry.resolveTypeName(type.typeName);
runtime_1.assert(plugin_framework_1.DescriptorProto.is(messageDescriptor));
return ts.createTypeReferenceNode(this.imports.type(source, messageDescriptor), undefined);
}
createEnumTypeNode(source, ei) {
let [enumTypeName] = ei;
let enumDescriptor = this.registry.resolveTypeName(enumTypeName);
runtime_1.assert(plugin_framework_1.EnumDescriptorProto.is(enumDescriptor));
return ts.createTypeReferenceNode(this.imports.type(source, enumDescriptor), undefined);
}
}
exports.MessageInterfaceGenerator = MessageInterfaceGenerator;

View file

@ -0,0 +1,49 @@
import * as ts from "typescript";
import { LongType } from "@protobuf-ts/runtime";
import { DescriptorProto, DescriptorRegistry, FileOptions_OptimizeMode as OptimizeMode, SymbolTable, TypescriptFile, TypeScriptImports } from "@protobuf-ts/plugin-framework";
import { CommentGenerator } from "./comment-generator";
import { Interpreter } from "../interpreter";
import { GeneratorBase } from "./generator-base";
export interface CustomMethodGenerator {
make(source: TypescriptFile, descriptor: DescriptorProto): ts.MethodDeclaration[];
}
export declare class MessageTypeGenerator extends GeneratorBase {
private readonly options;
private readonly wellKnown;
private readonly googleTypes;
private readonly typeMethodCreate;
private readonly typeMethodInternalBinaryRead;
private readonly typeMethodInternalBinaryWrite;
private readonly fieldInfoGenerator;
constructor(symbols: SymbolTable, registry: DescriptorRegistry, imports: TypeScriptImports, comments: CommentGenerator, interpreter: Interpreter, options: {
runtimeImportPath: string;
normalLongType: LongType;
oneofKindDiscriminator: string;
useProtoFieldName: boolean;
});
/**
* Declare a handler for the message. The handler provides
* functions to read / write messages of the specific type.
*
* For the following .proto:
*
* package test;
* message MyMessage {
* string str_field = 1;
* }
*
* We generate the following variable declaration:
*
* import { H } from "R";
* const MyMessage: H<MyMessage> =
* new H<MyMessage>(
* ".test.MyMessage",
* [{ no: 0, name: "str_field", kind: "scalar", T: 9 }]
* );
*
* H is the concrete class imported from runtime R.
* Some field information is passed to the handler's
* constructor.
*/
generateMessageType(source: TypescriptFile, descriptor: DescriptorProto, optimizeFor: OptimizeMode): void;
}

View file

@ -0,0 +1,85 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MessageTypeGenerator = void 0;
const ts = require("typescript");
const plugin_framework_1 = require("@protobuf-ts/plugin-framework");
const well_known_types_1 = require("../message-type-extensions/well-known-types");
const google_types_1 = require("../message-type-extensions/google-types");
const create_1 = require("../message-type-extensions/create");
const internal_binary_read_1 = require("../message-type-extensions/internal-binary-read");
const internal_binary_write_1 = require("../message-type-extensions/internal-binary-write");
const field_info_generator_1 = require("./field-info-generator");
const generator_base_1 = require("./generator-base");
class MessageTypeGenerator extends generator_base_1.GeneratorBase {
constructor(symbols, registry, imports, comments, interpreter, options) {
super(symbols, registry, imports, comments, interpreter);
this.options = options;
this.fieldInfoGenerator = new field_info_generator_1.FieldInfoGenerator(this.registry, this.imports, this.options);
this.wellKnown = new well_known_types_1.WellKnownTypes(this.registry, this.imports, this.options);
this.googleTypes = new google_types_1.GoogleTypes(this.registry, this.imports, this.options);
this.typeMethodCreate = new create_1.Create(this.registry, this.imports, this.interpreter, this.options);
this.typeMethodInternalBinaryRead = new internal_binary_read_1.InternalBinaryRead(this.registry, this.imports, this.interpreter, this.options);
this.typeMethodInternalBinaryWrite = new internal_binary_write_1.InternalBinaryWrite(this.registry, this.imports, this.interpreter, this.options);
}
/**
* Declare a handler for the message. The handler provides
* functions to read / write messages of the specific type.
*
* For the following .proto:
*
* package test;
* message MyMessage {
* string str_field = 1;
* }
*
* We generate the following variable declaration:
*
* import { H } from "R";
* const MyMessage: H<MyMessage> =
* new H<MyMessage>(
* ".test.MyMessage",
* [{ no: 0, name: "str_field", kind: "scalar", T: 9 }]
* );
*
* H is the concrete class imported from runtime R.
* Some field information is passed to the handler's
* constructor.
*/
generateMessageType(source, descriptor, optimizeFor) {
const
// identifier for the message
MyMessage = this.imports.type(source, descriptor), Message$Type = ts.createIdentifier(this.imports.type(source, descriptor) + '$Type'), MessageType = ts.createIdentifier(this.imports.name(source, "MessageType", this.options.runtimeImportPath)), interpreterType = this.interpreter.getMessageType(descriptor), classDecMembers = [], classDecSuperArgs = [
// arg 0: type name
ts.createStringLiteral(this.registry.makeTypeName(descriptor)),
// arg 1: field infos
this.fieldInfoGenerator.createFieldInfoLiterals(source, interpreterType.fields)
];
// if present, add message options in json format to MessageType CTOR args
if (Object.keys(interpreterType.options).length) {
classDecSuperArgs.push(plugin_framework_1.typescriptLiteralFromValue(interpreterType.options));
}
// "MyMessage$Type" constructor() { super(...) }
classDecMembers.push(ts.createConstructor(undefined, undefined, [], ts.createBlock([ts.createExpressionStatement(ts.createCall(ts.createSuper(), undefined, classDecSuperArgs))], true)));
// "MyMessage$Type" members for supported standard types
classDecMembers.push(...this.wellKnown.make(source, descriptor));
classDecMembers.push(...this.googleTypes.make(source, descriptor));
// "MyMessage$Type" members for optimized binary format
if (optimizeFor === plugin_framework_1.FileOptions_OptimizeMode.SPEED) {
classDecMembers.push(...this.typeMethodCreate.make(source, descriptor), ...this.typeMethodInternalBinaryRead.make(source, descriptor), ...this.typeMethodInternalBinaryWrite.make(source, descriptor));
}
// class "MyMessage$Type" extends "MessageType"<"MyMessage"> {
const classDec = ts.createClassDeclaration(undefined, undefined, Message$Type, undefined, [ts.createHeritageClause(ts.SyntaxKind.ExtendsKeyword, [ts.createExpressionWithTypeArguments([ts.createTypeReferenceNode(MyMessage, undefined)], MessageType)])], classDecMembers);
// export const "messageId" = new "MessageTypeId"();
const exportConst = ts.createVariableStatement([ts.createModifier(ts.SyntaxKind.ExportKeyword)], ts.createVariableDeclarationList([ts.createVariableDeclaration(MyMessage, undefined, ts.createNew(Message$Type, undefined, []))], ts.NodeFlags.Const));
// add to our file
source.addStatement(classDec);
source.addStatement(exportConst);
// add comments
ts.addSyntheticLeadingComment(classDec, ts.SyntaxKind.SingleLineCommentTrivia, " @generated message type with reflection information, may provide speed optimized methods", false);
let comment = this.comments.makeDeprecatedTag(descriptor);
comment += this.comments.makeGeneratedTag(descriptor).replace("@generated from ", "@generated MessageType for ");
plugin_framework_1.addCommentBlockAsJsDoc(exportConst, comment);
return;
}
}
exports.MessageTypeGenerator = MessageTypeGenerator;

View file

@ -0,0 +1,19 @@
import * as rpc from "@protobuf-ts/runtime-rpc";
import * as ts from "typescript";
import { DescriptorRegistry, TypescriptFile, TypeScriptImports } from "@protobuf-ts/plugin-framework";
/**
* Generates TypeScript code for runtime method information,
* from method field information.
*/
export declare class MethodInfoGenerator {
private readonly registry;
private readonly imports;
constructor(registry: DescriptorRegistry, imports: TypeScriptImports);
createMethodInfoLiterals(source: TypescriptFile, methodInfos: readonly rpc.PartialMethodInfo[]): ts.ArrayLiteralExpression;
createMethodInfoLiteral(source: TypescriptFile, methodInfo: rpc.PartialMethodInfo): ts.ObjectLiteralExpression;
/**
* Turn normalized method info returned by normalizeMethodInfo() back into
* the minimized form.
*/
private static denormalizeMethodInfo;
}

View file

@ -0,0 +1,67 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MethodInfoGenerator = void 0;
const rt = require("@protobuf-ts/runtime");
const ts = require("typescript");
const plugin_framework_1 = require("@protobuf-ts/plugin-framework");
/**
* Generates TypeScript code for runtime method information,
* from method field information.
*/
class MethodInfoGenerator {
constructor(registry, imports) {
this.registry = registry;
this.imports = imports;
}
createMethodInfoLiterals(source, methodInfos) {
const mi = methodInfos
.map(mi => MethodInfoGenerator.denormalizeMethodInfo(mi))
.map(mi => this.createMethodInfoLiteral(source, mi));
return ts.createArrayLiteral(mi, true);
}
createMethodInfoLiteral(source, methodInfo) {
methodInfo = MethodInfoGenerator.denormalizeMethodInfo(methodInfo);
const properties = [];
// name: The name of the method as declared in .proto
// localName: The name of the method in the runtime.
// idempotency: The idempotency level as specified in .proto.
// serverStreaming: Was the rpc declared with server streaming?
// clientStreaming: Was the rpc declared with client streaming?
// options: Contains custom method options from the .proto source in JSON format.
for (let key of ["name", "localName", "idempotency", "serverStreaming", "clientStreaming", "options"]) {
if (methodInfo[key] !== undefined) {
properties.push(ts.createPropertyAssignment(key, plugin_framework_1.typescriptLiteralFromValue(methodInfo[key])));
}
}
// I: The generated type handler for the input message.
properties.push(ts.createPropertyAssignment(ts.createIdentifier('I'), ts.createIdentifier(this.imports.type(source, this.registry.resolveTypeName(methodInfo.I.typeName)))));
// O: The generated type handler for the output message.
properties.push(ts.createPropertyAssignment(ts.createIdentifier('O'), ts.createIdentifier(this.imports.type(source, this.registry.resolveTypeName(methodInfo.O.typeName)))));
return ts.createObjectLiteral(properties, false);
}
/**
* Turn normalized method info returned by normalizeMethodInfo() back into
* the minimized form.
*/
static denormalizeMethodInfo(info) {
let partial = Object.assign({}, info);
delete partial.service;
if (info.localName === rt.lowerCamelCase(info.name)) {
delete partial.localName;
}
if (!info.serverStreaming) {
delete partial.serverStreaming;
}
if (!info.clientStreaming) {
delete partial.clientStreaming;
}
if (info.options && Object.keys(info.options).length) {
delete partial.info;
}
if (info.idempotency === undefined) {
delete partial.idempotency;
}
return partial;
}
}
exports.MethodInfoGenerator = MethodInfoGenerator;

View file

@ -0,0 +1,68 @@
import { DescriptorRegistry, ServiceDescriptorProto, SymbolTable, TypescriptFile, TypeScriptImports } from "@protobuf-ts/plugin-framework";
import * as ts from "typescript";
import * as rpc from "@protobuf-ts/runtime-rpc";
import { CommentGenerator } from "./comment-generator";
import { Interpreter } from "../interpreter";
import { GeneratorBase } from "./generator-base";
export declare abstract class ServiceClientGeneratorBase extends GeneratorBase {
protected readonly options: {
runtimeImportPath: string;
runtimeRpcImportPath: string;
};
abstract readonly symbolKindInterface: string;
abstract readonly symbolKindImplementation: string;
constructor(symbols: SymbolTable, registry: DescriptorRegistry, imports: TypeScriptImports, comments: CommentGenerator, interpreter: Interpreter, options: {
runtimeImportPath: string;
runtimeRpcImportPath: string;
});
registerSymbols(source: TypescriptFile, descriptor: ServiceDescriptorProto): void;
/**
* For the following .proto:
*
* service SimpleService {
* rpc Get (GetRequest) returns (GetResponse);
* }
*
* We generate the following interface:
*
* interface ISimpleServiceClient {
* get(request: GetRequest, options?: RpcOptions): UnaryCall<ExampleRequest, ExampleResponse>;
* }
*
*/
generateInterface(source: TypescriptFile, descriptor: ServiceDescriptorProto): ts.InterfaceDeclaration;
protected createMethodSignatures(source: TypescriptFile, methodInfo: rpc.MethodInfo): ts.MethodSignature[];
protected createUnarySignatures(source: TypescriptFile, methodInfo: rpc.MethodInfo): ts.MethodSignature[];
protected createServerStreamingSignatures(source: TypescriptFile, methodInfo: rpc.MethodInfo): ts.MethodSignature[];
protected createClientStreamingSignatures(source: TypescriptFile, methodInfo: rpc.MethodInfo): ts.MethodSignature[];
protected createDuplexStreamingSignatures(source: TypescriptFile, methodInfo: rpc.MethodInfo): ts.MethodSignature[];
/**
* For the following .proto:
*
* service SimpleService {
* rpc Get (GetRequest) returns (GetResponse);
* }
*
* We generate:
*
* class SimpleService implements ISimpleService {
* readonly typeName = ".spec.SimpleService";
* readonly methods: MethodInfo[] = [
* {name: 'Get', localName: 'get', I: GetRequest, O: GetResponse}
* ];
* ...
* }
*
*/
generateImplementationClass(source: TypescriptFile, descriptor: ServiceDescriptorProto): ts.ClassDeclaration;
/**
* Create any method type, switching to specific methods.
*/
protected createMethod(source: TypescriptFile, methodInfo: rpc.MethodInfo): ts.MethodDeclaration;
protected abstract createUnary(source: TypescriptFile, methodInfo: rpc.MethodInfo): ts.MethodDeclaration;
protected abstract createServerStreaming(source: TypescriptFile, methodInfo: rpc.MethodInfo): ts.MethodDeclaration;
protected abstract createClientStreaming(source: TypescriptFile, methodInfo: rpc.MethodInfo): ts.MethodDeclaration;
protected abstract createDuplexStreaming(source: TypescriptFile, methodInfo: rpc.MethodInfo): ts.MethodDeclaration;
protected makeI(source: TypescriptFile, methodInfo: rpc.MethodInfo, isTypeOnly?: boolean): ts.TypeReferenceNode;
protected makeO(source: TypescriptFile, methodInfo: rpc.MethodInfo, isTypeOnly?: boolean): ts.TypeReferenceNode;
}

View file

@ -0,0 +1,164 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ServiceClientGeneratorBase = void 0;
const ts = require("typescript");
const generator_base_1 = require("./generator-base");
const local_type_name_1 = require("./local-type-name");
const runtime_1 = require("@protobuf-ts/runtime");
class ServiceClientGeneratorBase extends generator_base_1.GeneratorBase {
constructor(symbols, registry, imports, comments, interpreter, options) {
super(symbols, registry, imports, comments, interpreter);
this.options = options;
}
registerSymbols(source, descriptor) {
const basename = local_type_name_1.createLocalTypeName(descriptor, this.registry);
const interfaceName = `I${basename}Client`;
const implementationName = `${basename}Client`;
this.symbols.register(interfaceName, descriptor, source, this.symbolKindInterface);
this.symbols.register(implementationName, descriptor, source, this.symbolKindImplementation);
}
/**
* For the following .proto:
*
* service SimpleService {
* rpc Get (GetRequest) returns (GetResponse);
* }
*
* We generate the following interface:
*
* interface ISimpleServiceClient {
* get(request: GetRequest, options?: RpcOptions): UnaryCall<ExampleRequest, ExampleResponse>;
* }
*
*/
generateInterface(source, descriptor) {
const interpreterType = this.interpreter.getServiceType(descriptor), IServiceClient = this.imports.type(source, descriptor, this.symbolKindInterface), signatures = [];
for (let mi of interpreterType.methods) {
const sig = this.createMethodSignatures(source, mi);
// add comment to the first signature
if (sig.length > 0) {
const methodDescriptor = descriptor.method.find(md => md.name === mi.name);
runtime_1.assert(methodDescriptor);
this.comments.addCommentsForDescriptor(sig[0], methodDescriptor, 'appendToLeadingBlock');
}
signatures.push(...sig);
}
// export interface MyService {...
let statement = ts.createInterfaceDeclaration(undefined, [ts.createModifier(ts.SyntaxKind.ExportKeyword)], IServiceClient, undefined, undefined, [...signatures]);
// add to our file
this.comments.addCommentsForDescriptor(statement, descriptor, 'appendToLeadingBlock');
source.addStatement(statement);
return statement;
}
createMethodSignatures(source, methodInfo) {
let signatures;
if (methodInfo.serverStreaming && methodInfo.clientStreaming) {
signatures = this.createDuplexStreamingSignatures(source, methodInfo);
}
else if (methodInfo.serverStreaming) {
signatures = this.createServerStreamingSignatures(source, methodInfo);
}
else if (methodInfo.clientStreaming) {
signatures = this.createClientStreamingSignatures(source, methodInfo);
}
else {
signatures = this.createUnarySignatures(source, methodInfo);
}
return signatures;
}
createUnarySignatures(source, methodInfo) {
const method = this.createUnary(source, methodInfo);
return [ts.createMethodSignature(method.typeParameters, method.parameters, method.type, method.name, method.questionToken)];
}
createServerStreamingSignatures(source, methodInfo) {
const method = this.createServerStreaming(source, methodInfo);
return [ts.createMethodSignature(method.typeParameters, method.parameters, method.type, method.name, method.questionToken)];
}
createClientStreamingSignatures(source, methodInfo) {
const method = this.createClientStreaming(source, methodInfo);
return [ts.createMethodSignature(method.typeParameters, method.parameters, method.type, method.name, method.questionToken)];
}
createDuplexStreamingSignatures(source, methodInfo) {
const method = this.createDuplexStreaming(source, methodInfo);
return [ts.createMethodSignature(method.typeParameters, method.parameters, method.type, method.name, method.questionToken)];
}
/**
* For the following .proto:
*
* service SimpleService {
* rpc Get (GetRequest) returns (GetResponse);
* }
*
* We generate:
*
* class SimpleService implements ISimpleService {
* readonly typeName = ".spec.SimpleService";
* readonly methods: MethodInfo[] = [
* {name: 'Get', localName: 'get', I: GetRequest, O: GetResponse}
* ];
* ...
* }
*
*/
generateImplementationClass(source, descriptor) {
const interpreterType = this.interpreter.getServiceType(descriptor), ServiceType = this.imports.type(source, descriptor), ServiceClient = this.imports.type(source, descriptor, this.symbolKindImplementation), IServiceClient = this.imports.type(source, descriptor, this.symbolKindInterface), ServiceInfo = this.imports.name(source, 'ServiceInfo', this.options.runtimeRpcImportPath, true), RpcTransport = this.imports.name(source, 'RpcTransport', this.options.runtimeRpcImportPath, true);
const classDecorators = [];
const constructorDecorators = [];
const members = [
// typeName = Haberdasher.typeName;
ts.createProperty(undefined, undefined, ts.createIdentifier("typeName"), undefined, undefined, ts.createPropertyAccess(ts.createIdentifier(ServiceType), ts.createIdentifier("typeName"))),
// methods = Haberdasher.methods;
ts.createProperty(undefined, undefined, ts.createIdentifier("methods"), undefined, undefined, ts.createPropertyAccess(ts.createIdentifier(ServiceType), ts.createIdentifier("methods"))),
// options = Haberdasher.options;
ts.createProperty(undefined, undefined, ts.createIdentifier("options"), undefined, undefined, ts.createPropertyAccess(ts.createIdentifier(ServiceType), ts.createIdentifier("options"))),
// constructor(@Inject(RPC_TRANSPORT) private readonly _transport: RpcTransport) {}
ts.createConstructor(undefined, undefined, [ts.createParameter(constructorDecorators, [
ts.createModifier(ts.SyntaxKind.PrivateKeyword),
ts.createModifier(ts.SyntaxKind.ReadonlyKeyword)
], undefined, ts.createIdentifier("_transport"), undefined, ts.createTypeReferenceNode(ts.createIdentifier(RpcTransport), undefined), undefined)], ts.createBlock([], true)),
...interpreterType.methods.map(mi => {
const declaration = this.createMethod(source, mi);
const methodDescriptor = descriptor.method.find(md => md.name === mi.name);
runtime_1.assert(methodDescriptor);
this.comments.addCommentsForDescriptor(declaration, methodDescriptor, 'appendToLeadingBlock');
return declaration;
})
];
// export class MyService implements MyService, ServiceInfo
const statement = ts.createClassDeclaration(classDecorators, [ts.createModifier(ts.SyntaxKind.ExportKeyword)], ServiceClient, undefined, [
ts.createHeritageClause(ts.SyntaxKind.ImplementsKeyword, [
ts.createExpressionWithTypeArguments(undefined, ts.createIdentifier(IServiceClient)),
ts.createExpressionWithTypeArguments(undefined, ts.createIdentifier(ServiceInfo)),
]),
], members);
source.addStatement(statement);
this.comments.addCommentsForDescriptor(statement, descriptor, 'appendToLeadingBlock');
return statement;
}
/**
* Create any method type, switching to specific methods.
*/
createMethod(source, methodInfo) {
let declaration;
if (methodInfo.serverStreaming && methodInfo.clientStreaming) {
declaration = this.createDuplexStreaming(source, methodInfo);
}
else if (methodInfo.serverStreaming) {
declaration = this.createServerStreaming(source, methodInfo);
}
else if (methodInfo.clientStreaming) {
declaration = this.createClientStreaming(source, methodInfo);
}
else {
declaration = this.createUnary(source, methodInfo);
}
return declaration;
}
makeI(source, methodInfo, isTypeOnly = false) {
return ts.createTypeReferenceNode(ts.createIdentifier(this.imports.type(source, this.registry.resolveTypeName(methodInfo.I.typeName), 'default', isTypeOnly)), undefined);
}
makeO(source, methodInfo, isTypeOnly = false) {
return ts.createTypeReferenceNode(ts.createIdentifier(this.imports.type(source, this.registry.resolveTypeName(methodInfo.O.typeName), 'default', isTypeOnly)), undefined);
}
}
exports.ServiceClientGeneratorBase = ServiceClientGeneratorBase;

View file

@ -0,0 +1,12 @@
import * as ts from "typescript";
import { ServiceClientGeneratorBase } from "./service-client-generator-base";
import * as rpc from "@protobuf-ts/runtime-rpc";
import { TypescriptFile } from "@protobuf-ts/plugin-framework";
export declare class ServiceClientGeneratorGeneric extends ServiceClientGeneratorBase {
readonly symbolKindInterface = "call-client-interface";
readonly symbolKindImplementation = "call-client";
createUnary(source: TypescriptFile, methodInfo: rpc.MethodInfo): ts.MethodDeclaration;
createServerStreaming(source: TypescriptFile, methodInfo: rpc.MethodInfo): ts.MethodDeclaration;
createClientStreaming(source: TypescriptFile, methodInfo: rpc.MethodInfo): ts.MethodDeclaration;
createDuplexStreaming(source: TypescriptFile, methodInfo: rpc.MethodInfo): ts.MethodDeclaration;
}

View file

@ -0,0 +1,130 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ServiceClientGeneratorGeneric = void 0;
const ts = require("typescript");
const service_client_generator_base_1 = require("./service-client-generator-base");
const runtime_1 = require("@protobuf-ts/runtime");
class ServiceClientGeneratorGeneric extends service_client_generator_base_1.ServiceClientGeneratorBase {
constructor() {
super(...arguments);
this.symbolKindInterface = 'call-client-interface';
this.symbolKindImplementation = 'call-client';
}
createUnary(source, methodInfo) {
let RpcOptions = this.imports.name(source, 'RpcOptions', this.options.runtimeRpcImportPath, true);
let UnaryCall = this.imports.name(source, 'UnaryCall', this.options.runtimeRpcImportPath, true);
let methodIndex = methodInfo.service.methods.indexOf(methodInfo);
runtime_1.assert(methodIndex >= 0);
return ts.createMethod(undefined, undefined, undefined, ts.createIdentifier(methodInfo.localName), undefined, undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("input"), undefined, this.makeI(source, methodInfo, true)),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("options"), ts.createToken(ts.SyntaxKind.QuestionToken), ts.createTypeReferenceNode(ts.createIdentifier(RpcOptions), undefined), undefined)
], ts.createTypeReferenceNode(UnaryCall, [
this.makeI(source, methodInfo, true),
this.makeO(source, methodInfo, true),
]), ts.createBlock([
// const method = this.methods[0], opt = this._transport.mergeOptions(options);
ts.createVariableStatement(undefined, ts.createVariableDeclarationList([
ts.createVariableDeclaration(ts.createIdentifier("method"), undefined, ts.createElementAccess(ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("methods")), ts.createNumericLiteral(methodIndex.toString()))),
ts.createVariableDeclaration(ts.createIdentifier("opt"), undefined, ts.createCall(ts.createPropertyAccess(ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("_transport")), ts.createIdentifier("mergeOptions")), undefined, [ts.createIdentifier("options")])),
], ts.NodeFlags.Const)),
// return stackIntercept("unary", this._transport, method, opt, input);
ts.createReturn(ts.createCall(ts.createIdentifier(this.imports.name(source, 'stackIntercept', this.options.runtimeRpcImportPath)), [
this.makeI(source, methodInfo, true),
this.makeO(source, methodInfo, true)
], [
ts.createStringLiteral("unary"),
ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("_transport")),
ts.createIdentifier("method"),
ts.createIdentifier("opt"),
ts.createIdentifier("input"),
])),
], true));
}
createServerStreaming(source, methodInfo) {
let RpcOptions = this.imports.name(source, 'RpcOptions', this.options.runtimeRpcImportPath, true);
let ServerStreamingCall = this.imports.name(source, 'ServerStreamingCall', this.options.runtimeRpcImportPath, true);
let methodIndex = methodInfo.service.methods.indexOf(methodInfo);
runtime_1.assert(methodIndex >= 0);
return ts.createMethod(undefined, undefined, undefined, ts.createIdentifier(methodInfo.localName), undefined, undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("input"), undefined, this.makeI(source, methodInfo, true)),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("options"), ts.createToken(ts.SyntaxKind.QuestionToken), ts.createTypeReferenceNode(ts.createIdentifier(RpcOptions), undefined), undefined)
], ts.createTypeReferenceNode(ServerStreamingCall, [
this.makeI(source, methodInfo, true),
this.makeO(source, methodInfo, true),
]), ts.createBlock([
// const method = this.methods[0], opt = this._transport.mergeOptions(options);
ts.createVariableStatement(undefined, ts.createVariableDeclarationList([
ts.createVariableDeclaration(ts.createIdentifier("method"), undefined, ts.createElementAccess(ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("methods")), ts.createNumericLiteral(methodIndex.toString()))),
ts.createVariableDeclaration(ts.createIdentifier("opt"), undefined, ts.createCall(ts.createPropertyAccess(ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("_transport")), ts.createIdentifier("mergeOptions")), undefined, [ts.createIdentifier("options")])),
], ts.NodeFlags.Const)),
// return stackIntercept("serverStreaming", this._transport, method, opt, i);
ts.createReturn(ts.createCall(ts.createIdentifier(this.imports.name(source, 'stackIntercept', this.options.runtimeRpcImportPath)), [
this.makeI(source, methodInfo, true),
this.makeO(source, methodInfo, true)
], [
ts.createStringLiteral("serverStreaming"),
ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("_transport")),
ts.createIdentifier("method"),
ts.createIdentifier("opt"),
ts.createIdentifier("input"),
])),
], true));
}
createClientStreaming(source, methodInfo) {
let RpcOptions = this.imports.name(source, 'RpcOptions', this.options.runtimeRpcImportPath, true);
let ClientStreamingCall = this.imports.name(source, 'ClientStreamingCall', this.options.runtimeRpcImportPath, true);
let methodIndex = methodInfo.service.methods.indexOf(methodInfo);
runtime_1.assert(methodIndex >= 0);
return ts.createMethod(undefined, undefined, undefined, ts.createIdentifier(methodInfo.localName), undefined, undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("options"), ts.createToken(ts.SyntaxKind.QuestionToken), ts.createTypeReferenceNode(ts.createIdentifier(RpcOptions), undefined), undefined)
], ts.createTypeReferenceNode(ClientStreamingCall, [
this.makeI(source, methodInfo, true),
this.makeO(source, methodInfo, true),
]), ts.createBlock([
// const method = this.methods[0], opt = this._transport.mergeOptions(options)
ts.createVariableStatement(undefined, ts.createVariableDeclarationList([
ts.createVariableDeclaration(ts.createIdentifier("method"), undefined, ts.createElementAccess(ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("methods")), ts.createNumericLiteral(methodIndex.toString()))),
ts.createVariableDeclaration(ts.createIdentifier("opt"), undefined, ts.createCall(ts.createPropertyAccess(ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("_transport")), ts.createIdentifier("mergeOptions")), undefined, [ts.createIdentifier("options")]))
], ts.NodeFlags.Const)),
// return stackIntercept("clientStreaming", this._transport, methods, opt);
ts.createReturn(ts.createCall(ts.createIdentifier(this.imports.name(source, 'stackIntercept', this.options.runtimeRpcImportPath)), [
this.makeI(source, methodInfo, true),
this.makeO(source, methodInfo, true)
], [
ts.createStringLiteral("clientStreaming"),
ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("_transport")),
ts.createIdentifier("method"),
ts.createIdentifier("opt")
])),
], true));
}
createDuplexStreaming(source, methodInfo) {
let RpcOptions = this.imports.name(source, 'RpcOptions', this.options.runtimeRpcImportPath, true);
let DuplexStreamingCall = this.imports.name(source, 'DuplexStreamingCall', this.options.runtimeRpcImportPath, true);
let methodIndex = methodInfo.service.methods.indexOf(methodInfo);
runtime_1.assert(methodIndex >= 0);
return ts.createMethod(undefined, undefined, undefined, ts.createIdentifier(methodInfo.localName), undefined, undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("options"), ts.createToken(ts.SyntaxKind.QuestionToken), ts.createTypeReferenceNode(ts.createIdentifier(RpcOptions), undefined), undefined)
], ts.createTypeReferenceNode(DuplexStreamingCall, [
this.makeI(source, methodInfo, true),
this.makeO(source, methodInfo, true),
]), ts.createBlock([
// const method = this.methods[0], opt = this._transport.mergeOptions(options)
ts.createVariableStatement(undefined, ts.createVariableDeclarationList([
ts.createVariableDeclaration(ts.createIdentifier("method"), undefined, ts.createElementAccess(ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("methods")), ts.createNumericLiteral(methodIndex.toString()))),
ts.createVariableDeclaration(ts.createIdentifier("opt"), undefined, ts.createCall(ts.createPropertyAccess(ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("_transport")), ts.createIdentifier("mergeOptions")), undefined, [ts.createIdentifier("options")]))
], ts.NodeFlags.Const)),
// return stackIntercept("duplex", this._transport, this, methods, opt);
ts.createReturn(ts.createCall(ts.createIdentifier(this.imports.name(source, 'stackIntercept', this.options.runtimeRpcImportPath)), [
this.makeI(source, methodInfo, true),
this.makeO(source, methodInfo, true)
], [
ts.createStringLiteral("duplex"),
ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("_transport")),
ts.createIdentifier("method"),
ts.createIdentifier("opt")
])),
], true));
}
}
exports.ServiceClientGeneratorGeneric = ServiceClientGeneratorGeneric;

View file

@ -0,0 +1,17 @@
import { ServiceClientGeneratorBase } from "./service-client-generator-base";
import { ServiceDescriptorProto, TypescriptFile } from "@protobuf-ts/plugin-framework";
import * as ts from "typescript";
import * as rpc from "@protobuf-ts/runtime-rpc";
export declare class ServiceClientGeneratorGrpc extends ServiceClientGeneratorBase {
readonly symbolKindInterface = "grpc1-client-interface";
readonly symbolKindImplementation = "grpc1-client";
generateImplementationClass(source: TypescriptFile, descriptor: ServiceDescriptorProto): ts.ClassDeclaration;
protected createUnarySignatures(source: TypescriptFile, methodInfo: rpc.MethodInfo): ts.MethodSignature[];
protected createServerStreamingSignatures(source: TypescriptFile, methodInfo: rpc.MethodInfo): ts.MethodSignature[];
protected createClientStreamingSignatures(source: TypescriptFile, methodInfo: rpc.MethodInfo): ts.MethodSignature[];
protected createDuplexStreamingSignatures(source: TypescriptFile, methodInfo: rpc.MethodInfo): ts.MethodSignature[];
protected createUnary(source: TypescriptFile, methodInfo: rpc.MethodInfo): ts.MethodDeclaration;
protected createServerStreaming(source: TypescriptFile, methodInfo: rpc.MethodInfo): ts.MethodDeclaration;
protected createClientStreaming(source: TypescriptFile, methodInfo: rpc.MethodInfo): ts.MethodDeclaration;
protected createDuplexStreaming(source: TypescriptFile, methodInfo: rpc.MethodInfo): ts.MethodDeclaration;
}

View file

@ -0,0 +1,371 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ServiceClientGeneratorGrpc = void 0;
const service_client_generator_base_1 = require("./service-client-generator-base");
const ts = require("typescript");
const runtime_1 = require("@protobuf-ts/runtime");
class ServiceClientGeneratorGrpc extends service_client_generator_base_1.ServiceClientGeneratorBase {
constructor() {
super(...arguments);
this.symbolKindInterface = 'grpc1-client-interface';
this.symbolKindImplementation = 'grpc1-client';
}
generateImplementationClass(source, descriptor) {
const interpreterType = this.interpreter.getServiceType(descriptor), ServiceClient = this.imports.type(source, descriptor, this.symbolKindImplementation), IServiceClient = this.imports.type(source, descriptor, this.symbolKindInterface), BinaryReadOptions = this.imports.name(source, 'BinaryReadOptions', this.options.runtimeImportPath, true), BinaryWriteOptions = this.imports.name(source, 'BinaryWriteOptions', this.options.runtimeImportPath, true), grpc = this.imports.namespace(source, 'grpc', '@grpc/grpc-js');
const members = [
// private readonly _binaryOptions: Partial<BinaryReadOptions & BinaryWriteOptions>;
ts.createProperty(undefined, [
ts.createModifier(ts.SyntaxKind.PrivateKeyword),
ts.createModifier(ts.SyntaxKind.ReadonlyKeyword)
], ts.createIdentifier("_binaryOptions"), undefined, ts.createTypeReferenceNode(ts.createIdentifier("Partial"), [ts.createIntersectionTypeNode([
ts.createTypeReferenceNode(ts.createIdentifier(BinaryReadOptions), undefined),
ts.createTypeReferenceNode(ts.createIdentifier(BinaryWriteOptions), undefined)
])]), undefined),
//
ts.createConstructor(undefined, undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("address"), undefined, ts.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("credentials"), undefined, ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ChannelCredentials")), undefined), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("options"), undefined, ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ClientOptions")), undefined), ts.createObjectLiteral([], false)),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("binaryOptions"), undefined, ts.createTypeReferenceNode(ts.createIdentifier("Partial"), [ts.createIntersectionTypeNode([
ts.createTypeReferenceNode(ts.createIdentifier(BinaryReadOptions), undefined),
ts.createTypeReferenceNode(ts.createIdentifier(BinaryWriteOptions), undefined)
])]), ts.createObjectLiteral([], false))
], ts.createBlock([
ts.createExpressionStatement(ts.createCall(ts.createSuper(), undefined, [
ts.createIdentifier("address"),
ts.createIdentifier("credentials"),
ts.createIdentifier("options")
])),
ts.createExpressionStatement(ts.createBinary(ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("_binaryOptions")), ts.createToken(ts.SyntaxKind.EqualsToken), ts.createIdentifier("binaryOptions")))
], true)),
...interpreterType.methods.map(mi => {
const declaration = this.createMethod(source, mi);
const methodDescriptor = descriptor.method.find(md => md.name === mi.name);
runtime_1.assert(methodDescriptor);
this.comments.addCommentsForDescriptor(declaration, methodDescriptor, 'appendToLeadingBlock');
return declaration;
})
];
// export class MyService implements MyService, ServiceInfo
const statement = ts.createClassDeclaration(undefined, [ts.createModifier(ts.SyntaxKind.ExportKeyword)], ServiceClient, undefined, [
ts.createHeritageClause(ts.SyntaxKind.ExtendsKeyword, [ts.createExpressionWithTypeArguments(undefined, ts.createPropertyAccess(ts.createIdentifier(grpc), ts.createIdentifier("Client")))]),
ts.createHeritageClause(ts.SyntaxKind.ImplementsKeyword, [ts.createExpressionWithTypeArguments(undefined, ts.createIdentifier(IServiceClient))])
], members);
source.addStatement(statement);
this.comments.addCommentsForDescriptor(statement, descriptor, 'appendToLeadingBlock');
return statement;
}
createUnarySignatures(source, methodInfo) {
let grpc = this.imports.namespace(source, 'grpc', '@grpc/grpc-js');
return [
ts.createMethodSignature(undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("input"), undefined, this.makeI(source, methodInfo, true), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("metadata"), undefined, ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("Metadata")), undefined), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("options"), undefined, ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("CallOptions")), undefined), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("callback"), undefined, ts.createFunctionTypeNode(undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("err"), undefined, ts.createUnionTypeNode([
ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ServiceError")), undefined),
ts.createKeywordTypeNode(ts.SyntaxKind.NullKeyword)
]), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("value"), ts.createToken(ts.SyntaxKind.QuestionToken), this.makeO(source, methodInfo, true), undefined)
], ts.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword)), undefined)
], ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ClientUnaryCall")), undefined), ts.createIdentifier(methodInfo.localName), undefined),
ts.createMethodSignature(undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("input"), undefined, this.makeI(source, methodInfo, true), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("metadata"), undefined, ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("Metadata")), undefined), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("callback"), undefined, ts.createFunctionTypeNode(undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("err"), undefined, ts.createUnionTypeNode([
ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ServiceError")), undefined),
ts.createKeywordTypeNode(ts.SyntaxKind.NullKeyword)
]), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("value"), ts.createToken(ts.SyntaxKind.QuestionToken), this.makeO(source, methodInfo, true), undefined)
], ts.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword)), undefined)
], ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ClientUnaryCall")), undefined), ts.createIdentifier(methodInfo.localName), undefined),
ts.createMethodSignature(undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("input"), undefined, this.makeI(source, methodInfo, true), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("options"), undefined, ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("CallOptions")), undefined), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("callback"), undefined, ts.createFunctionTypeNode(undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("err"), undefined, ts.createUnionTypeNode([
ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ServiceError")), undefined),
ts.createKeywordTypeNode(ts.SyntaxKind.NullKeyword)
]), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("value"), ts.createToken(ts.SyntaxKind.QuestionToken), this.makeO(source, methodInfo, true), undefined)
], ts.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword)), undefined)
], ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ClientUnaryCall")), undefined), ts.createIdentifier(methodInfo.localName), undefined),
ts.createMethodSignature(undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("input"), undefined, this.makeI(source, methodInfo, true), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("callback"), undefined, ts.createFunctionTypeNode(undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("err"), undefined, ts.createUnionTypeNode([
ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ServiceError")), undefined),
ts.createKeywordTypeNode(ts.SyntaxKind.NullKeyword)
]), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("value"), ts.createToken(ts.SyntaxKind.QuestionToken), this.makeO(source, methodInfo, true), undefined)
], ts.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword)), undefined)
], ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ClientUnaryCall")), undefined), ts.createIdentifier(methodInfo.localName), undefined)
];
}
createServerStreamingSignatures(source, methodInfo) {
let grpc = this.imports.namespace(source, 'grpc', '@grpc/grpc-js');
return [
ts.createMethodSignature(undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("input"), undefined, this.makeI(source, methodInfo, true), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("metadata"), ts.createToken(ts.SyntaxKind.QuestionToken), ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("Metadata")), undefined), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("options"), ts.createToken(ts.SyntaxKind.QuestionToken), ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("CallOptions")), undefined), undefined)
], ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ClientReadableStream")), [this.makeO(source, methodInfo, true)]), ts.createIdentifier(methodInfo.localName), undefined),
ts.createMethodSignature(undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("input"), undefined, this.makeI(source, methodInfo, true), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("options"), ts.createToken(ts.SyntaxKind.QuestionToken), ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("CallOptions")), undefined), undefined)
], ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ClientReadableStream")), [this.makeO(source, methodInfo, true)]), ts.createIdentifier(methodInfo.localName), undefined)
];
}
createClientStreamingSignatures(source, methodInfo) {
let grpc = this.imports.namespace(source, 'grpc', '@grpc/grpc-js');
return [
ts.createMethodSignature(undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("metadata"), undefined, ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("Metadata")), undefined), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("options"), undefined, ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("CallOptions")), undefined), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("callback"), undefined, ts.createFunctionTypeNode(undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("err"), undefined, ts.createUnionTypeNode([
ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ServiceError")), undefined),
ts.createKeywordTypeNode(ts.SyntaxKind.NullKeyword)
]), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("value"), ts.createToken(ts.SyntaxKind.QuestionToken), this.makeO(source, methodInfo, true), undefined)
], ts.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword)), undefined)
], ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ClientWritableStream")), [this.makeI(source, methodInfo, true)]), ts.createIdentifier(methodInfo.localName), undefined),
ts.createMethodSignature(undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("metadata"), undefined, ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("Metadata")), undefined), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("callback"), undefined, ts.createFunctionTypeNode(undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("err"), undefined, ts.createUnionTypeNode([
ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ServiceError")), undefined),
ts.createKeywordTypeNode(ts.SyntaxKind.NullKeyword)
]), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("value"), ts.createToken(ts.SyntaxKind.QuestionToken), this.makeO(source, methodInfo, true), undefined)
], ts.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword)), undefined)
], ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ClientWritableStream")), [this.makeI(source, methodInfo, true)]), ts.createIdentifier(methodInfo.localName), undefined),
ts.createMethodSignature(undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("options"), undefined, ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("CallOptions")), undefined), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("callback"), undefined, ts.createFunctionTypeNode(undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("err"), undefined, ts.createUnionTypeNode([
ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ServiceError")), undefined),
ts.createKeywordTypeNode(ts.SyntaxKind.NullKeyword)
]), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("value"), ts.createToken(ts.SyntaxKind.QuestionToken), this.makeO(source, methodInfo, true), undefined)
], ts.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword)), undefined)
], ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ClientWritableStream")), [
this.makeI(source, methodInfo, true),
]), ts.createIdentifier(methodInfo.localName), undefined),
ts.createMethodSignature(undefined, [ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("callback"), undefined, ts.createFunctionTypeNode(undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("err"), undefined, ts.createUnionTypeNode([
ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ServiceError")), undefined),
ts.createKeywordTypeNode(ts.SyntaxKind.NullKeyword)
]), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("value"), ts.createToken(ts.SyntaxKind.QuestionToken), this.makeO(source, methodInfo, true), undefined)
], ts.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword)), undefined)], ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ClientWritableStream")), [
this.makeI(source, methodInfo, true),
]), ts.createIdentifier(methodInfo.localName), undefined)
];
}
createDuplexStreamingSignatures(source, methodInfo) {
let grpc = this.imports.namespace(source, 'grpc', '@grpc/grpc-js');
return [
ts.createMethodSignature(undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("metadata"), undefined, ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("Metadata")), undefined), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("options"), ts.createToken(ts.SyntaxKind.QuestionToken), ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("CallOptions")), undefined), undefined)
], ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ClientDuplexStream")), [
this.makeI(source, methodInfo, true),
this.makeO(source, methodInfo, true),
]), ts.createIdentifier(methodInfo.localName), undefined),
ts.createMethodSignature(undefined, [ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("options"), ts.createToken(ts.SyntaxKind.QuestionToken), ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("CallOptions")), undefined), undefined)], ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ClientDuplexStream")), [
this.makeI(source, methodInfo, true),
this.makeO(source, methodInfo, true),
]), ts.createIdentifier(methodInfo.localName), undefined)
];
}
createUnary(source, methodInfo) {
let grpc = this.imports.namespace(source, 'grpc', '@grpc/grpc-js');
let ServiceType = this.imports.type(source, this.registry.resolveTypeName(methodInfo.service.typeName));
let methodIndex = methodInfo.service.methods.indexOf(methodInfo);
return ts.createMethod(undefined, undefined, undefined, ts.createIdentifier(methodInfo.localName), undefined, undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("input"), undefined, this.makeI(source, methodInfo, true), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("metadata"), undefined, ts.createUnionTypeNode([
ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("Metadata")), undefined),
ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("CallOptions")), undefined),
ts.createParenthesizedType(ts.createFunctionTypeNode(undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("err"), undefined, ts.createUnionTypeNode([
ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ServiceError")), undefined),
ts.createKeywordTypeNode(ts.SyntaxKind.NullKeyword)
]), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("value"), ts.createToken(ts.SyntaxKind.QuestionToken), this.makeO(source, methodInfo, true), undefined)
], ts.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword)))
]), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("options"), ts.createToken(ts.SyntaxKind.QuestionToken), ts.createUnionTypeNode([
ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("CallOptions")), undefined),
ts.createParenthesizedType(ts.createFunctionTypeNode(undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("err"), undefined, ts.createUnionTypeNode([
ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ServiceError")), undefined),
ts.createKeywordTypeNode(ts.SyntaxKind.NullKeyword)
]), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("value"), ts.createToken(ts.SyntaxKind.QuestionToken), this.makeO(source, methodInfo, true), undefined)
], ts.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword)))
]), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("callback"), ts.createToken(ts.SyntaxKind.QuestionToken), ts.createParenthesizedType(ts.createFunctionTypeNode(undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("err"), undefined, ts.createUnionTypeNode([
ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ServiceError")), undefined),
ts.createKeywordTypeNode(ts.SyntaxKind.NullKeyword)
]), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("value"), ts.createToken(ts.SyntaxKind.QuestionToken), this.makeO(source, methodInfo, true), undefined)
], ts.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword))), undefined)
], ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ClientUnaryCall")), undefined), ts.createBlock([
ts.createVariableStatement(undefined, ts.createVariableDeclarationList([ts.createVariableDeclaration(ts.createIdentifier("method"), undefined, ts.createElementAccess(ts.createPropertyAccess(ts.createIdentifier(ServiceType), ts.createIdentifier("methods")), ts.createNumericLiteral(methodIndex.toString())))], ts.NodeFlags.Const)),
ts.createReturn(ts.createCall(ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("makeUnaryRequest")), [
this.makeI(source, methodInfo, true),
this.makeO(source, methodInfo, true)
], [
ts.createTemplateExpression(ts.createTemplateHead("/", "/"), [
ts.createTemplateSpan(ts.createPropertyAccess(ts.createIdentifier(ServiceType), ts.createIdentifier("typeName")), ts.createTemplateMiddle("/", "/")),
ts.createTemplateSpan(ts.createPropertyAccess(ts.createIdentifier("method"), ts.createIdentifier("name")), ts.createTemplateTail("", ""))
]),
ts.createArrowFunction(undefined, undefined, [ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("value"), undefined, this.makeI(source, methodInfo, true), undefined)], ts.createTypeReferenceNode(ts.createIdentifier("Buffer"), undefined), ts.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.createCall(ts.createPropertyAccess(ts.createIdentifier("Buffer"), ts.createIdentifier("from")), undefined, [ts.createCall(ts.createPropertyAccess(ts.createPropertyAccess(ts.createIdentifier("method"), ts.createIdentifier("I")), ts.createIdentifier("toBinary")), undefined, [
ts.createIdentifier("value"),
ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("_binaryOptions"))
])])),
ts.createArrowFunction(undefined, undefined, [ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("value"), undefined, ts.createTypeReferenceNode(ts.createIdentifier("Buffer"), undefined), undefined)], this.makeO(source, methodInfo, true), ts.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.createCall(ts.createPropertyAccess(ts.createPropertyAccess(ts.createIdentifier("method"), ts.createIdentifier("O")), ts.createIdentifier("fromBinary")), undefined, [
ts.createIdentifier("value"),
ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("_binaryOptions"))
])),
ts.createIdentifier("input"),
ts.createAsExpression(ts.createIdentifier("metadata"), ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)),
ts.createAsExpression(ts.createIdentifier("options"), ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)),
ts.createAsExpression(ts.createIdentifier("callback"), ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword))
]))
], true));
}
createServerStreaming(source, methodInfo) {
let grpc = this.imports.namespace(source, 'grpc', '@grpc/grpc-js');
let ServiceType = this.imports.type(source, this.registry.resolveTypeName(methodInfo.service.typeName));
let methodIndex = methodInfo.service.methods.indexOf(methodInfo);
return ts.createMethod(undefined, undefined, undefined, ts.createIdentifier(methodInfo.localName), undefined, undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("input"), undefined, this.makeI(source, methodInfo, true), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("metadata"), ts.createToken(ts.SyntaxKind.QuestionToken), ts.createUnionTypeNode([
ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("Metadata")), undefined),
ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("CallOptions")), undefined)
]), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("options"), ts.createToken(ts.SyntaxKind.QuestionToken), ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("CallOptions")), undefined), undefined)
], ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ClientReadableStream")), [this.makeO(source, methodInfo, true)]), ts.createBlock([
ts.createVariableStatement(undefined, ts.createVariableDeclarationList([ts.createVariableDeclaration(ts.createIdentifier("method"), undefined, ts.createElementAccess(ts.createPropertyAccess(ts.createIdentifier(ServiceType), ts.createIdentifier("methods")), ts.createNumericLiteral(methodIndex.toString())))], ts.NodeFlags.Const)),
ts.createReturn(ts.createCall(ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("makeServerStreamRequest")), [
this.makeI(source, methodInfo, true),
this.makeO(source, methodInfo, true)
], [
ts.createTemplateExpression(ts.createTemplateHead("/", "/"), [
ts.createTemplateSpan(ts.createPropertyAccess(ts.createIdentifier(ServiceType), ts.createIdentifier("typeName")), ts.createTemplateMiddle("/", "/")),
ts.createTemplateSpan(ts.createPropertyAccess(ts.createIdentifier("method"), ts.createIdentifier("name")), ts.createTemplateTail("", ""))
]),
ts.createArrowFunction(undefined, undefined, [ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("value"), undefined, this.makeI(source, methodInfo, true), undefined)], ts.createTypeReferenceNode(ts.createIdentifier("Buffer"), undefined), ts.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.createCall(ts.createPropertyAccess(ts.createIdentifier("Buffer"), ts.createIdentifier("from")), undefined, [ts.createCall(ts.createPropertyAccess(ts.createPropertyAccess(ts.createIdentifier("method"), ts.createIdentifier("I")), ts.createIdentifier("toBinary")), undefined, [
ts.createIdentifier("value"),
ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("_binaryOptions"))
])])),
ts.createArrowFunction(undefined, undefined, [ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("value"), undefined, ts.createTypeReferenceNode(ts.createIdentifier("Buffer"), undefined), undefined)], this.makeO(source, methodInfo, true), ts.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.createCall(ts.createPropertyAccess(ts.createPropertyAccess(ts.createIdentifier("method"), ts.createIdentifier("O")), ts.createIdentifier("fromBinary")), undefined, [
ts.createIdentifier("value"),
ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("_binaryOptions"))
])),
ts.createIdentifier("input"),
ts.createAsExpression(ts.createIdentifier("metadata"), ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)),
ts.createIdentifier("options")
]))
], true));
}
createClientStreaming(source, methodInfo) {
let grpc = this.imports.namespace(source, 'grpc', '@grpc/grpc-js');
let ServiceType = this.imports.type(source, this.registry.resolveTypeName(methodInfo.service.typeName));
let methodIndex = methodInfo.service.methods.indexOf(methodInfo);
return ts.createMethod(undefined, undefined, undefined, ts.createIdentifier(methodInfo.localName), undefined, undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("metadata"), undefined, ts.createUnionTypeNode([
ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("Metadata")), undefined),
ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("CallOptions")), undefined),
ts.createParenthesizedType(ts.createFunctionTypeNode(undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("err"), undefined, ts.createUnionTypeNode([
ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ServiceError")), undefined),
ts.createKeywordTypeNode(ts.SyntaxKind.NullKeyword)
]), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("value"), ts.createToken(ts.SyntaxKind.QuestionToken), this.makeO(source, methodInfo, true), undefined)
], ts.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword)))
]), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("options"), ts.createToken(ts.SyntaxKind.QuestionToken), ts.createUnionTypeNode([
ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("CallOptions")), undefined),
ts.createParenthesizedType(ts.createFunctionTypeNode(undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("err"), undefined, ts.createUnionTypeNode([
ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ServiceError")), undefined),
ts.createKeywordTypeNode(ts.SyntaxKind.NullKeyword)
]), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("value"), ts.createToken(ts.SyntaxKind.QuestionToken), this.makeO(source, methodInfo, true), undefined)
], ts.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword)))
]), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("callback"), ts.createToken(ts.SyntaxKind.QuestionToken), ts.createParenthesizedType(ts.createFunctionTypeNode(undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("err"), undefined, ts.createUnionTypeNode([
ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ServiceError")), undefined),
ts.createKeywordTypeNode(ts.SyntaxKind.NullKeyword)
]), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("value"), ts.createToken(ts.SyntaxKind.QuestionToken), this.makeO(source, methodInfo, true), undefined)
], ts.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword))), undefined)
], ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ClientWritableStream")), [this.makeI(source, methodInfo, true)]), ts.createBlock([
ts.createVariableStatement(undefined, ts.createVariableDeclarationList([ts.createVariableDeclaration(ts.createIdentifier("method"), undefined, ts.createElementAccess(ts.createPropertyAccess(ts.createIdentifier(ServiceType), ts.createIdentifier("methods")), ts.createNumericLiteral(methodIndex.toString())))], ts.NodeFlags.Const)),
ts.createReturn(ts.createCall(ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("makeClientStreamRequest")), [
this.makeI(source, methodInfo, true),
this.makeO(source, methodInfo, true)
], [
ts.createTemplateExpression(ts.createTemplateHead("/", "/"), [
ts.createTemplateSpan(ts.createPropertyAccess(ts.createIdentifier(ServiceType), ts.createIdentifier("typeName")), ts.createTemplateMiddle("/", "/")),
ts.createTemplateSpan(ts.createPropertyAccess(ts.createIdentifier("method"), ts.createIdentifier("name")), ts.createTemplateTail("", ""))
]),
ts.createArrowFunction(undefined, undefined, [ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("value"), undefined, this.makeI(source, methodInfo, true), undefined)], ts.createTypeReferenceNode(ts.createIdentifier("Buffer"), undefined), ts.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.createCall(ts.createPropertyAccess(ts.createIdentifier("Buffer"), ts.createIdentifier("from")), undefined, [ts.createCall(ts.createPropertyAccess(ts.createPropertyAccess(ts.createIdentifier("method"), ts.createIdentifier("I")), ts.createIdentifier("toBinary")), undefined, [
ts.createIdentifier("value"),
ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("_binaryOptions"))
])])),
ts.createArrowFunction(undefined, undefined, [ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("value"), undefined, ts.createTypeReferenceNode(ts.createIdentifier("Buffer"), undefined), undefined)], this.makeO(source, methodInfo, true), ts.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.createCall(ts.createPropertyAccess(ts.createPropertyAccess(ts.createIdentifier("method"), ts.createIdentifier("O")), ts.createIdentifier("fromBinary")), undefined, [
ts.createIdentifier("value"),
ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("_binaryOptions"))
])),
ts.createAsExpression(ts.createIdentifier("metadata"), ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)),
ts.createAsExpression(ts.createIdentifier("options"), ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)),
ts.createAsExpression(ts.createIdentifier("callback"), ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword))
]))
], true));
}
createDuplexStreaming(source, methodInfo) {
let grpc = this.imports.namespace(source, 'grpc', '@grpc/grpc-js');
let ServiceType = this.imports.type(source, this.registry.resolveTypeName(methodInfo.service.typeName));
return ts.createMethod(undefined, undefined, undefined, ts.createIdentifier(methodInfo.localName), undefined, undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("metadata"), ts.createToken(ts.SyntaxKind.QuestionToken), ts.createUnionTypeNode([
ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("Metadata")), undefined),
ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("CallOptions")), undefined)
]), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("options"), ts.createToken(ts.SyntaxKind.QuestionToken), ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("CallOptions")), undefined), undefined)
], ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ClientDuplexStream")), [
this.makeI(source, methodInfo, true),
this.makeO(source, methodInfo, true)
]), ts.createBlock([
ts.createVariableStatement(undefined, ts.createVariableDeclarationList([ts.createVariableDeclaration(ts.createIdentifier("method"), undefined, ts.createElementAccess(ts.createPropertyAccess(ts.createIdentifier(ServiceType), ts.createIdentifier("methods")), ts.createNumericLiteral(methodInfo.service.methods.indexOf(methodInfo).toString())))], ts.NodeFlags.Const)),
ts.createReturn(ts.createCall(ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("makeBidiStreamRequest")), [
this.makeI(source, methodInfo, true),
this.makeO(source, methodInfo, true)
], [
ts.createTemplateExpression(ts.createTemplateHead("/", "/"), [
ts.createTemplateSpan(ts.createPropertyAccess(ts.createIdentifier(ServiceType), ts.createIdentifier("typeName")), ts.createTemplateMiddle("/", "/")),
ts.createTemplateSpan(ts.createPropertyAccess(ts.createIdentifier("method"), ts.createIdentifier("name")), ts.createTemplateTail("", ""))
]),
ts.createArrowFunction(undefined, undefined, [ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("value"), undefined, this.makeI(source, methodInfo, true), undefined)], ts.createTypeReferenceNode(ts.createIdentifier("Buffer"), undefined), ts.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.createCall(ts.createPropertyAccess(ts.createIdentifier("Buffer"), ts.createIdentifier("from")), undefined, [ts.createCall(ts.createPropertyAccess(ts.createPropertyAccess(ts.createIdentifier("method"), ts.createIdentifier("I")), ts.createIdentifier("toBinary")), undefined, [
ts.createIdentifier("value"),
ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("_binaryOptions"))
])])),
ts.createArrowFunction(undefined, undefined, [ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("value"), undefined, ts.createTypeReferenceNode(ts.createIdentifier("Buffer"), undefined), undefined)], this.makeO(source, methodInfo, true), ts.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.createCall(ts.createPropertyAccess(ts.createPropertyAccess(ts.createIdentifier("method"), ts.createIdentifier("O")), ts.createIdentifier("fromBinary")), undefined, [
ts.createIdentifier("value"),
ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("_binaryOptions"))
])),
ts.createAsExpression(ts.createIdentifier("metadata"), ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)),
ts.createIdentifier("options")
]))
], true));
}
}
exports.ServiceClientGeneratorGrpc = ServiceClientGeneratorGrpc;

View file

@ -0,0 +1,18 @@
import { GeneratorBase } from "./generator-base";
import { DescriptorRegistry, ServiceDescriptorProto, SymbolTable, TypescriptFile, TypeScriptImports } from "@protobuf-ts/plugin-framework";
import { Interpreter } from "../interpreter";
import * as ts from "typescript";
import { CommentGenerator } from "./comment-generator";
export declare class ServiceServerGeneratorGeneric extends GeneratorBase {
private readonly options;
private readonly symbolKindInterface;
constructor(symbols: SymbolTable, registry: DescriptorRegistry, imports: TypeScriptImports, comments: CommentGenerator, interpreter: Interpreter, options: {
runtimeRpcImportPath: string;
});
registerSymbols(source: TypescriptFile, descriptor: ServiceDescriptorProto): void;
generateInterface(source: TypescriptFile, descriptor: ServiceDescriptorProto): ts.InterfaceDeclaration;
private createUnary;
private createServerStreaming;
private createClientStreaming;
private createBidi;
}

View file

@ -0,0 +1,78 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ServiceServerGeneratorGeneric = void 0;
const generator_base_1 = require("./generator-base");
const ts = require("typescript");
const runtime_1 = require("@protobuf-ts/runtime");
const local_type_name_1 = require("./local-type-name");
class ServiceServerGeneratorGeneric extends generator_base_1.GeneratorBase {
constructor(symbols, registry, imports, comments, interpreter, options) {
super(symbols, registry, imports, comments, interpreter);
this.options = options;
this.symbolKindInterface = 'generic-server-interface';
}
registerSymbols(source, descriptor) {
const basename = local_type_name_1.createLocalTypeName(descriptor, this.registry);
const interfaceName = `I${basename}`;
this.symbols.register(interfaceName, descriptor, source, this.symbolKindInterface);
}
generateInterface(source, descriptor) {
const interpreterType = this.interpreter.getServiceType(descriptor), IGenericServer = this.imports.type(source, descriptor, this.symbolKindInterface), ServerCallContext = this.imports.name(source, "ServerCallContext", this.options.runtimeRpcImportPath);
const statement = ts.createInterfaceDeclaration(undefined, [ts.createModifier(ts.SyntaxKind.ExportKeyword)], ts.createIdentifier(IGenericServer), [
ts.createTypeParameterDeclaration("T", undefined, ts.createTypeReferenceNode(ts.createIdentifier(ServerCallContext), undefined))
], undefined, interpreterType.methods.map(mi => {
const methodDescriptor = descriptor.method.find(md => md.name === mi.name);
runtime_1.assert(methodDescriptor);
let signature;
if (mi.serverStreaming && mi.clientStreaming) {
signature = this.createBidi(source, mi);
}
else if (mi.serverStreaming) {
signature = this.createServerStreaming(source, mi);
}
else if (mi.clientStreaming) {
signature = this.createClientStreaming(source, mi);
}
else {
signature = this.createUnary(source, mi);
}
this.comments.addCommentsForDescriptor(signature, methodDescriptor, 'appendToLeadingBlock');
return signature;
}));
// add to our file
this.comments.addCommentsForDescriptor(statement, descriptor, 'appendToLeadingBlock');
source.addStatement(statement);
return statement;
}
createUnary(source, methodInfo) {
const I = ts.createTypeReferenceNode(ts.createIdentifier(this.imports.type(source, this.registry.resolveTypeName(methodInfo.I.typeName))), undefined), O = ts.createTypeReferenceNode(ts.createIdentifier(this.imports.type(source, this.registry.resolveTypeName(methodInfo.O.typeName))), undefined);
return ts.createMethodSignature(undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("request"), undefined, I, undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("context"), undefined, ts.createTypeReferenceNode(ts.createIdentifier("T"), undefined), undefined)
], ts.createTypeReferenceNode(ts.createIdentifier("Promise"), [O]), ts.createIdentifier(methodInfo.localName), undefined);
}
createServerStreaming(source, methodInfo) {
const I = ts.createTypeReferenceNode(ts.createIdentifier(this.imports.type(source, this.registry.resolveTypeName(methodInfo.I.typeName))), undefined), O = ts.createTypeReferenceNode(ts.createIdentifier(this.imports.type(source, this.registry.resolveTypeName(methodInfo.O.typeName))), undefined), RpcInputStream = this.imports.name(source, 'RpcInputStream', this.options.runtimeRpcImportPath);
return ts.createMethodSignature(undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("request"), undefined, I, undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("responses"), undefined, ts.createTypeReferenceNode(ts.createIdentifier(RpcInputStream), [O]), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("context"), undefined, ts.createTypeReferenceNode(ts.createIdentifier("T"), undefined), undefined)
], ts.createTypeReferenceNode(ts.createIdentifier("Promise"), [ts.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword)]), ts.createIdentifier(methodInfo.localName), undefined);
}
createClientStreaming(source, methodInfo) {
const I = ts.createTypeReferenceNode(ts.createIdentifier(this.imports.type(source, this.registry.resolveTypeName(methodInfo.I.typeName))), undefined), O = ts.createTypeReferenceNode(ts.createIdentifier(this.imports.type(source, this.registry.resolveTypeName(methodInfo.O.typeName))), undefined), RpcOutputStream = this.imports.name(source, 'RpcOutputStream', this.options.runtimeRpcImportPath);
return ts.createMethodSignature(undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("requests"), undefined, ts.createTypeReferenceNode(ts.createIdentifier(RpcOutputStream), [I]), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("context"), undefined, ts.createTypeReferenceNode(ts.createIdentifier("T"), undefined), undefined)
], ts.createTypeReferenceNode(ts.createIdentifier("Promise"), [O]), ts.createIdentifier(methodInfo.localName), undefined);
}
createBidi(source, methodInfo) {
const I = ts.createTypeReferenceNode(ts.createIdentifier(this.imports.type(source, this.registry.resolveTypeName(methodInfo.I.typeName))), undefined), O = ts.createTypeReferenceNode(ts.createIdentifier(this.imports.type(source, this.registry.resolveTypeName(methodInfo.O.typeName))), undefined), RpcOutputStream = this.imports.name(source, 'RpcOutputStream', this.options.runtimeRpcImportPath), RpcInputStream = this.imports.name(source, 'RpcInputStream', this.options.runtimeRpcImportPath);
return ts.createMethodSignature(undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("requests"), undefined, ts.createTypeReferenceNode(ts.createIdentifier(RpcOutputStream), [I]), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("responses"), undefined, ts.createTypeReferenceNode(ts.createIdentifier(RpcInputStream), [O]), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("context"), undefined, ts.createTypeReferenceNode(ts.createIdentifier("T"), undefined), undefined)
], ts.createTypeReferenceNode(ts.createIdentifier("Promise"), [ts.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword)]), ts.createIdentifier(methodInfo.localName), undefined);
}
}
exports.ServiceServerGeneratorGeneric = ServiceServerGeneratorGeneric;

View file

@ -0,0 +1,16 @@
import { GeneratorBase } from "./generator-base";
import { DescriptorRegistry, ServiceDescriptorProto, SymbolTable, TypescriptFile, TypeScriptImports } from "@protobuf-ts/plugin-framework";
import { Interpreter } from "../interpreter";
import * as ts from "typescript";
import { CommentGenerator } from "./comment-generator";
export declare class ServiceServerGeneratorGrpc extends GeneratorBase {
private readonly options;
private readonly symbolKindInterface;
private readonly symbolKindDefinition;
constructor(symbols: SymbolTable, registry: DescriptorRegistry, imports: TypeScriptImports, comments: CommentGenerator, interpreter: Interpreter, options: {});
registerSymbols(source: TypescriptFile, descriptor: ServiceDescriptorProto): void;
generateInterface(source: TypescriptFile, descriptor: ServiceDescriptorProto): ts.InterfaceDeclaration;
private createMethodPropertySignature;
generateDefinition(source: TypescriptFile, descriptor: ServiceDescriptorProto): ts.VariableStatement;
private makeDefinitionProperty;
}

View file

@ -0,0 +1,89 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ServiceServerGeneratorGrpc = void 0;
const generator_base_1 = require("./generator-base");
const plugin_framework_1 = require("@protobuf-ts/plugin-framework");
const ts = require("typescript");
const runtime_1 = require("@protobuf-ts/runtime");
const local_type_name_1 = require("./local-type-name");
class ServiceServerGeneratorGrpc extends generator_base_1.GeneratorBase {
constructor(symbols, registry, imports, comments, interpreter, options) {
super(symbols, registry, imports, comments, interpreter);
this.options = options;
this.symbolKindInterface = 'grpc-server-interface';
this.symbolKindDefinition = 'grpc-server-definition';
}
registerSymbols(source, descriptor) {
const basename = local_type_name_1.createLocalTypeName(descriptor, this.registry);
const interfaceName = `I${basename}`;
const definitionName = `${basename[0].toLowerCase()}${basename.substring(1)}Definition`;
this.symbols.register(interfaceName, descriptor, source, this.symbolKindInterface);
this.symbols.register(definitionName, descriptor, source, this.symbolKindDefinition);
}
generateInterface(source, descriptor) {
const interpreterType = this.interpreter.getServiceType(descriptor), IGrpcServer = this.imports.type(source, descriptor, this.symbolKindInterface), grpc = this.imports.namespace(source, 'grpc', '@grpc/grpc-js', true);
const statement = ts.createInterfaceDeclaration(undefined, [ts.createModifier(ts.SyntaxKind.ExportKeyword)], ts.createIdentifier(IGrpcServer), undefined, [ts.createHeritageClause(ts.SyntaxKind.ExtendsKeyword, [ts.createExpressionWithTypeArguments(undefined, ts.createPropertyAccess(ts.createIdentifier(grpc), ts.createIdentifier("UntypedServiceImplementation")))])], interpreterType.methods.map(mi => {
const methodDescriptor = descriptor.method.find(md => md.name === mi.name);
runtime_1.assert(methodDescriptor);
return this.createMethodPropertySignature(source, mi, methodDescriptor);
}));
// add to our file
this.comments.addCommentsForDescriptor(statement, descriptor, 'appendToLeadingBlock');
source.addStatement(statement);
return statement;
}
createMethodPropertySignature(source, methodInfo, methodDescriptor) {
const grpc = this.imports.namespace(source, 'grpc', '@grpc/grpc-js', true);
let handler;
if (methodInfo.serverStreaming && methodInfo.clientStreaming) {
handler = 'handleBidiStreamingCall';
}
else if (methodInfo.serverStreaming) {
handler = 'handleServerStreamingCall';
}
else if (methodInfo.clientStreaming) {
handler = 'handleClientStreamingCall';
}
else {
handler = 'handleUnaryCall';
}
const signature = ts.createPropertySignature(undefined, ts.createIdentifier(methodInfo.localName), undefined, ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier(handler)), [
ts.createTypeReferenceNode(ts.createIdentifier(this.imports.type(source, this.registry.resolveTypeName(methodInfo.I.typeName))), undefined),
ts.createTypeReferenceNode(ts.createIdentifier(this.imports.type(source, this.registry.resolveTypeName(methodInfo.O.typeName))), undefined),
]), undefined);
this.comments.addCommentsForDescriptor(signature, methodDescriptor, 'appendToLeadingBlock');
return signature;
}
generateDefinition(source, descriptor) {
const grpcServerDefinition = this.imports.type(source, descriptor, this.symbolKindDefinition), IGrpcServer = this.imports.type(source, descriptor, this.symbolKindInterface), interpreterType = this.interpreter.getServiceType(descriptor), grpc = this.imports.namespace(source, 'grpc', '@grpc/grpc-js', true);
const statement = ts.createVariableStatement([ts.createModifier(ts.SyntaxKind.ExportKeyword)], ts.createVariableDeclarationList([ts.createVariableDeclaration(ts.createIdentifier(grpcServerDefinition), ts.createTypeReferenceNode(ts.createQualifiedName(ts.createIdentifier(grpc), ts.createIdentifier("ServiceDefinition")), [ts.createTypeReferenceNode(ts.createIdentifier(IGrpcServer), undefined)]), ts.createObjectLiteral(interpreterType.methods.map(mi => this.makeDefinitionProperty(source, mi)), true))], ts.NodeFlags.Const));
// add to our file
const doc = `@grpc/grpc-js definition for the protobuf ${this.registry.formatQualifiedName(descriptor)}.\n` +
`\n` +
`Usage: Implement the interface ${IGrpcServer} and add to a grpc server.\n` +
`\n` +
'```typescript\n' +
`const server = new grpc.Server();\n` +
`const service: ${IGrpcServer} = ...\n` +
`server.addService(${grpcServerDefinition}, service);\n` +
'```';
plugin_framework_1.addCommentBlockAsJsDoc(statement, doc);
source.addStatement(statement);
return statement;
}
makeDefinitionProperty(source, methodInfo) {
const I = this.imports.type(source, this.registry.resolveTypeName(methodInfo.I.typeName));
const O = this.imports.type(source, this.registry.resolveTypeName(methodInfo.O.typeName));
return ts.createPropertyAssignment(ts.createIdentifier(methodInfo.localName), ts.createObjectLiteral([
ts.createPropertyAssignment(ts.createIdentifier("path"), ts.createStringLiteral(`/${methodInfo.service.typeName}/${methodInfo.name}`)),
ts.createPropertyAssignment(ts.createIdentifier("originalName"), ts.createStringLiteral(methodInfo.name)),
ts.createPropertyAssignment(ts.createIdentifier("requestStream"), methodInfo.clientStreaming ? ts.createTrue() : ts.createFalse()),
ts.createPropertyAssignment(ts.createIdentifier("responseStream"), methodInfo.serverStreaming ? ts.createTrue() : ts.createFalse()),
ts.createPropertyAssignment(ts.createIdentifier("responseDeserialize"), ts.createArrowFunction(undefined, undefined, [ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("bytes"), undefined, undefined, undefined)], undefined, ts.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.createCall(ts.createPropertyAccess(ts.createIdentifier(O), ts.createIdentifier("fromBinary")), undefined, [ts.createIdentifier("bytes")]))),
ts.createPropertyAssignment(ts.createIdentifier("requestDeserialize"), ts.createArrowFunction(undefined, undefined, [ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("bytes"), undefined, undefined, undefined)], undefined, ts.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.createCall(ts.createPropertyAccess(ts.createIdentifier(I), ts.createIdentifier("fromBinary")), undefined, [ts.createIdentifier("bytes")]))),
ts.createPropertyAssignment(ts.createIdentifier("responseSerialize"), ts.createArrowFunction(undefined, undefined, [ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("value"), undefined, undefined, undefined)], undefined, ts.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.createCall(ts.createPropertyAccess(ts.createIdentifier("Buffer"), ts.createIdentifier("from")), undefined, [ts.createCall(ts.createPropertyAccess(ts.createIdentifier(O), ts.createIdentifier("toBinary")), undefined, [ts.createIdentifier("value")])]))),
ts.createPropertyAssignment(ts.createIdentifier("requestSerialize"), ts.createArrowFunction(undefined, undefined, [ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("value"), undefined, undefined, undefined)], undefined, ts.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.createCall(ts.createPropertyAccess(ts.createIdentifier("Buffer"), ts.createIdentifier("from")), undefined, [ts.createCall(ts.createPropertyAccess(ts.createIdentifier(I), ts.createIdentifier("toBinary")), undefined, [ts.createIdentifier("value")])])))
], true));
}
}
exports.ServiceServerGeneratorGrpc = ServiceServerGeneratorGrpc;

View file

@ -0,0 +1,12 @@
import { DescriptorRegistry, ServiceDescriptorProto, SymbolTable, TypescriptFile, TypeScriptImports } from "@protobuf-ts/plugin-framework";
import { Interpreter } from "../interpreter";
import { CommentGenerator } from "./comment-generator";
import { GeneratorBase } from "./generator-base";
export declare class ServiceTypeGenerator extends GeneratorBase {
private readonly options;
private readonly methodInfoGenerator;
constructor(symbols: SymbolTable, registry: DescriptorRegistry, imports: TypeScriptImports, comments: CommentGenerator, interpreter: Interpreter, options: {
runtimeRpcImportPath: string;
});
generateServiceType(source: TypescriptFile, descriptor: ServiceDescriptorProto): void;
}

View file

@ -0,0 +1,38 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ServiceTypeGenerator = void 0;
const plugin_framework_1 = require("@protobuf-ts/plugin-framework");
const ts = require("typescript");
const method_info_generator_1 = require("./method-info-generator");
const generator_base_1 = require("./generator-base");
class ServiceTypeGenerator extends generator_base_1.GeneratorBase {
constructor(symbols, registry, imports, comments, interpreter, options) {
super(symbols, registry, imports, comments, interpreter);
this.options = options;
this.methodInfoGenerator = new method_info_generator_1.MethodInfoGenerator(this.registry, this.imports);
}
// export const Haberdasher = new ServiceType("spec.haberdasher.Haberdasher", [
// { name: "MakeHat", localName: "makeHat", I: Size, O: Hat },
// ], {});
generateServiceType(source, descriptor) {
const
// identifier for the service
MyService = this.imports.type(source, descriptor), ServiceType = this.imports.name(source, "ServiceType", this.options.runtimeRpcImportPath), interpreterType = this.interpreter.getServiceType(descriptor);
const args = [
ts.createStringLiteral(interpreterType.typeName),
this.methodInfoGenerator.createMethodInfoLiterals(source, interpreterType.methods)
];
if (Object.keys(interpreterType.options).length) {
args.push(plugin_framework_1.typescriptLiteralFromValue(interpreterType.options));
}
const exportConst = ts.createVariableStatement([ts.createModifier(ts.SyntaxKind.ExportKeyword)], ts.createVariableDeclarationList([ts.createVariableDeclaration(ts.createIdentifier(MyService), undefined, ts.createNew(ts.createIdentifier(ServiceType), undefined, args))], ts.NodeFlags.Const));
// add to our file
source.addStatement(exportConst);
// add comments
let comment = this.comments.makeDeprecatedTag(descriptor);
comment += this.comments.makeGeneratedTag(descriptor).replace("@generated from ", "@generated ServiceType for ");
plugin_framework_1.addCommentBlockAsJsDoc(exportConst, comment);
return;
}
}
exports.ServiceTypeGenerator = ServiceTypeGenerator;

View file

@ -0,0 +1,6 @@
import { CodeGeneratorRequest, CodeGeneratorResponse_Feature, GeneratedFile, PluginBase } from "@protobuf-ts/plugin-framework";
export declare class DumpPlugin extends PluginBase<GeneratedFile> {
generate(request: CodeGeneratorRequest): GeneratedFile[];
private static mkdir;
protected getSupportedFeatures: () => CodeGeneratorResponse_Feature[];
}

View file

@ -0,0 +1,57 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DumpPlugin = void 0;
const fs_1 = require("fs");
const path = require("path");
const plugin_framework_1 = require("@protobuf-ts/plugin-framework");
const BIN_SUFFIX = '.codegenreq';
const JSON_SUFFIX = '.json';
const USAGE = `protoc-gen-dump can be run in 2 ways, depending on the given parameter (--dump_opt=<parameter>)
1) parameter ending in ${BIN_SUFFIX}
Dumps a 'CodeGeneratorRequest' in binary format.
The request is dumped to the path specified by the parameter.
Example:
protoc --plugin node_modules/.bin/protoc-gen-dump --dump_opt my-dump.bin --dump_out . -I protos/ protos/*.proto
2) parameter ending in ${JSON_SUFFIX}
Dumps a 'CodeGeneratorRequest' in JSON format.
The request is dumped to the path specified by the parameter.
Example:
protoc --plugin node_modules/.bin/protoc-gen-dump --dump_opt my-dump.json --dump_out . -I protos/ protos/*.proto
`;
class DumpPlugin extends plugin_framework_1.PluginBase {
constructor() {
super(...arguments);
// we support proto3-optionals, so we let protoc know
this.getSupportedFeatures = () => [plugin_framework_1.CodeGeneratorResponse_Feature.PROTO3_OPTIONAL];
}
generate(request) {
const parameter = request.parameter;
if (parameter === null || parameter === void 0 ? void 0 : parameter.endsWith(JSON_SUFFIX)) {
DumpPlugin.mkdir(parameter);
fs_1.writeFileSync(parameter, plugin_framework_1.CodeGeneratorRequest.toJsonString(request, { prettySpaces: 2 }));
}
else if (parameter === null || parameter === void 0 ? void 0 : parameter.endsWith(BIN_SUFFIX)) {
DumpPlugin.mkdir(parameter);
let bytes = plugin_framework_1.CodeGeneratorRequest.toBinary(request);
fs_1.writeFileSync(parameter, bytes);
try {
plugin_framework_1.CodeGeneratorRequest.fromBinary(bytes);
}
catch (e) {
throw new Error("Sanity check failed: " + e);
}
}
else {
throw USAGE;
}
return [];
}
static mkdir(file) {
if (!fs_1.existsSync(path.dirname(file))) {
fs_1.mkdirSync(path.dirname(file), { recursive: true });
}
}
}
exports.DumpPlugin = DumpPlugin;

View file

@ -0,0 +1,31 @@
import { FileDescriptorProto } from "@protobuf-ts/plugin-framework";
export declare class FileTable {
private readonly entries;
private readonly clashResolveMaxTries;
private readonly clashResolver;
constructor(clashResolver?: ClashResolver);
register(requestedName: string, descriptor: FileDescriptorProto, kind?: string): string;
protected hasName: (name: string) => boolean;
/**
* Find a symbol (of the given kind) for the given descriptor.
* Return `undefined` if not found.
*/
find(descriptor: FileDescriptorProto, kind?: string): FileTableEntry | undefined;
/**
* Find a symbol (of the given kind) for the given descriptor.
* Raises error if not found.
*/
get(descriptor: FileDescriptorProto, kind?: string): FileTableEntry;
/**
* Is a name (of the given kind) registered for the the given descriptor?
*/
has(descriptor: FileDescriptorProto, kind?: string): boolean;
static defaultClashResolver(descriptor: FileDescriptorProto, requestedName: string, kind: string, tryCount: number): string;
}
interface FileTableEntry {
descriptor: FileDescriptorProto;
name: string;
kind: string;
}
declare type ClashResolver = (descriptor: FileDescriptorProto, requestedName: string, kind: string, tryCount: number, failedName: string) => string;
export {};

View file

@ -0,0 +1,72 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FileTable = void 0;
const plugin_framework_1 = require("@protobuf-ts/plugin-framework");
class FileTable {
constructor(clashResolver) {
this.entries = [];
this.clashResolveMaxTries = 100;
this.hasName = (name) => this.entries.some(e => e.name === name);
this.clashResolver = clashResolver !== null && clashResolver !== void 0 ? clashResolver : FileTable.defaultClashResolver;
}
register(requestedName, descriptor, kind = 'default') {
// Only one symbol per kind can be registered for a descriptor.
if (this.has(descriptor, kind)) {
let { name } = this.get(descriptor, kind);
let msg = `Cannot register name "${requestedName}" of kind "${kind}" for ${plugin_framework_1.StringFormat.formatName(descriptor)}. `
+ `The descriptor is already registered with name "${name}". `
+ `Use a different 'kind' to register multiple symbols for a descriptor.`;
throw new Error(msg);
}
// find a free name within the file
let name = requestedName;
let count = 0;
while (this.hasName(name) && count < this.clashResolveMaxTries) {
name = this.clashResolver(descriptor, requestedName, kind, ++count, name);
}
if (this.hasName(name)) {
let msg = `Failed to register name "${requestedName}" for ${plugin_framework_1.StringFormat.formatName(descriptor)}. `
+ `Gave up finding alternative name after ${this.clashResolveMaxTries} tries. `
+ `There is something wrong with the clash resolver.`;
throw new Error(msg);
}
// add the entry and return name
this.entries.push({ descriptor, kind, name });
return name;
}
/**
* Find a symbol (of the given kind) for the given descriptor.
* Return `undefined` if not found.
*/
find(descriptor, kind = 'default') {
return this.entries.find(e => e.descriptor === descriptor && e.kind === kind);
}
/**
* Find a symbol (of the given kind) for the given descriptor.
* Raises error if not found.
*/
get(descriptor, kind = 'default') {
const found = this.find(descriptor, kind);
if (!found) {
let msg = `Failed to find name for ${plugin_framework_1.StringFormat.formatName(descriptor)} of kind "${kind}". `
+ `Searched in ${this.entries.length} files.`;
throw new Error(msg);
}
return found;
}
/**
* Is a name (of the given kind) registered for the the given descriptor?
*/
has(descriptor, kind = 'default') {
return !!this.find(descriptor, kind);
}
static defaultClashResolver(descriptor, requestedName, kind, tryCount) {
const lastDotI = requestedName.lastIndexOf('.');
let basename = lastDotI > 0 ? requestedName.substring(0, lastDotI) : requestedName;
const suffix = lastDotI > 0 ? requestedName.substring(lastDotI) : '';
basename = basename.endsWith('$') ? basename.substring(1) : basename;
basename = basename + '$' + tryCount;
return basename + suffix;
}
}
exports.FileTable = FileTable;

View file

@ -0,0 +1,122 @@
import { DescriptorProto, DescriptorRegistry, EnumDescriptorProto, FieldDescriptorProto, FieldOptions_JSType, FileDescriptorProto, MethodDescriptorProto, ServiceDescriptorProto } from "@protobuf-ts/plugin-framework";
import * as rt from "@protobuf-ts/runtime";
import * as rpc from "@protobuf-ts/runtime-rpc";
import { OurFileOptions, OurServiceOptions } from "./our-options";
declare type JsonOptionsMap = {
[extensionName: string]: rt.JsonValue;
};
/**
* The protobuf-ts plugin generates code for message types from descriptor
* protos. This class also creates message types from descriptor protos, but
* but instead of generating code, it creates the type in-memory.
*
* This means that it is possible, for example, to read a message from binary
* data without any generated code.
*
* The protobuf-ts plugin uses the interpreter to read custom options at
* compile time and convert them to JSON.
*
* Since the interpreter creates fully functional message types including
* reflection information, the protobuf-ts plugin uses the interpreter as
* single source of truth for generating message interfaces and reflection
* information.
*/
export declare class Interpreter {
private readonly registry;
private readonly options;
private readonly serviceTypes;
private readonly messageTypes;
private readonly enumInfos;
constructor(registry: DescriptorRegistry, options: {
normalLongType: rt.LongType;
oneofKindDiscriminator: string;
synthesizeEnumZeroValue: string | false;
forceExcludeAllOptions: boolean;
keepEnumPrefix: boolean;
useProtoFieldName: boolean;
});
/**
* Returns a map of custom options for the provided descriptor.
* The map is an object indexed by the extension field name.
* The value of the extension field is provided in JSON format.
*
* This works by:
* - searching for option extensions for the given descriptor proto
* in the registry.
* - for example, providing a google.protobuf.FieldDescriptorProto
* searches for all extensions on google.protobuf.FieldOption.
* - extensions are just fields, so we build a synthetic message
* type with all the (extension) fields.
* - the field names are created by DescriptorRegistry.getExtensionName(),
* which produces for example "spec.option_name", where "spec" is
* the package and "option_name" is the field name.
* - then we concatenate all unknown field data of the option and
* read the data with our synthetic message type
* - the read message is then simply converted to JSON
*
* The optional "optionBlacklist" will exclude matching options.
* The blacklist can contain exact extension names, or use the wildcard
* character `*` to match a namespace or even all options.
*
* Note that options on options (google.protobuf.*Options) are not
* supported.
*/
readOptions(descriptor: FieldDescriptorProto | MethodDescriptorProto | FileDescriptorProto | ServiceDescriptorProto | DescriptorProto, excludeOptions: readonly string[]): JsonOptionsMap | undefined;
/**
* Read the custom file options declared in protobuf-ts.proto
*/
readOurFileOptions(file: FileDescriptorProto): OurFileOptions;
/**
* Read the custom service options declared in protobuf-ts.proto
*/
readOurServiceOptions(service: ServiceDescriptorProto): OurServiceOptions;
/**
* Get a runtime type for the given message type name or message descriptor.
* Creates the type if not created previously.
*
* Honors our file option "ts.exclude_options".
*/
getMessageType(descriptorOrTypeName: string | DescriptorProto): rt.IMessageType<rt.UnknownMessage>;
/**
* Get a runtime type for the given service type name or service descriptor.
* Creates the type if not created previously.
*
* Honors our file option "ts.exclude_options".
*/
getServiceType(descriptorOrTypeName: string | ServiceDescriptorProto): rpc.ServiceType;
/**
* Get runtime information for an enum.
* Creates the info if not created previously.
*/
getEnumInfo(descriptorOrTypeName: string | EnumDescriptorProto): rt.EnumInfo;
private static createTypescriptNameForMethod;
private buildServiceType;
private buildMethodInfo;
/**
* Create a name for a field or a oneof.
* - use lowerCamelCase unless useProtoFieldName option is enabled
* - escape reserved object property names by
* adding '$' at the end
* - don't have to escape reserved keywords
*/
private createTypescriptNameForField;
private buildFieldInfos;
private buildFieldInfo;
protected buildEnumInfo(descriptor: EnumDescriptorProto): rt.EnumInfo;
protected determineNonDefaultLongType(scalarType: rt.ScalarType, jsTypeOption?: FieldOptions_JSType): rt.LongType | undefined;
/**
* Is this a 64 bit integral or fixed type?
*/
static isLongValueType(type: rt.ScalarType): boolean;
}
/**
* Builds a typescript enum lookup object,
* compatible with enums generated by @protobuf-ts/plugin.
*/
export declare class RuntimeEnumBuilder {
private readonly values;
add(name: string, number: number): void;
isValid(): boolean;
build(): rt.EnumInfo[1];
}
export {};

View file

@ -0,0 +1,526 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RuntimeEnumBuilder = exports.Interpreter = void 0;
const plugin_framework_1 = require("@protobuf-ts/plugin-framework");
const rt = require("@protobuf-ts/runtime");
const runtime_1 = require("@protobuf-ts/runtime");
const rpc = require("@protobuf-ts/runtime-rpc");
const field_info_generator_1 = require("./code-gen/field-info-generator");
const our_options_1 = require("./our-options");
/**
* The protobuf-ts plugin generates code for message types from descriptor
* protos. This class also creates message types from descriptor protos, but
* but instead of generating code, it creates the type in-memory.
*
* This means that it is possible, for example, to read a message from binary
* data without any generated code.
*
* The protobuf-ts plugin uses the interpreter to read custom options at
* compile time and convert them to JSON.
*
* Since the interpreter creates fully functional message types including
* reflection information, the protobuf-ts plugin uses the interpreter as
* single source of truth for generating message interfaces and reflection
* information.
*/
class Interpreter {
constructor(registry, options) {
this.registry = registry;
this.options = options;
this.serviceTypes = new Map();
this.messageTypes = new Map();
this.enumInfos = new Map();
}
/**
* Returns a map of custom options for the provided descriptor.
* The map is an object indexed by the extension field name.
* The value of the extension field is provided in JSON format.
*
* This works by:
* - searching for option extensions for the given descriptor proto
* in the registry.
* - for example, providing a google.protobuf.FieldDescriptorProto
* searches for all extensions on google.protobuf.FieldOption.
* - extensions are just fields, so we build a synthetic message
* type with all the (extension) fields.
* - the field names are created by DescriptorRegistry.getExtensionName(),
* which produces for example "spec.option_name", where "spec" is
* the package and "option_name" is the field name.
* - then we concatenate all unknown field data of the option and
* read the data with our synthetic message type
* - the read message is then simply converted to JSON
*
* The optional "optionBlacklist" will exclude matching options.
* The blacklist can contain exact extension names, or use the wildcard
* character `*` to match a namespace or even all options.
*
* Note that options on options (google.protobuf.*Options) are not
* supported.
*/
readOptions(descriptor, excludeOptions) {
// the option to force exclude all options takes precedence
if (this.options.forceExcludeAllOptions) {
return undefined;
}
// if options message not present, there cannot be any extension options
if (!descriptor.options) {
return undefined;
}
// if no unknown fields present, can exit early
let unknownFields = rt.UnknownFieldHandler.list(descriptor.options);
if (!unknownFields.length) {
return undefined;
}
let optionsTypeName;
if (plugin_framework_1.FieldDescriptorProto.is(descriptor) && plugin_framework_1.DescriptorProto.is(this.registry.parentOf(descriptor))) {
optionsTypeName = 'google.protobuf.FieldOptions';
}
else if (plugin_framework_1.MethodDescriptorProto.is(descriptor)) {
optionsTypeName = 'google.protobuf.MethodOptions';
}
else if (this.registry.fileOf(descriptor) === descriptor) {
optionsTypeName = 'google.protobuf.FileOptions';
}
else if (plugin_framework_1.ServiceDescriptorProto.is(descriptor)) {
optionsTypeName = 'google.protobuf.ServiceOptions';
}
else if (plugin_framework_1.DescriptorProto.is(descriptor)) {
optionsTypeName = 'google.protobuf.MessageOptions';
}
else {
throw new Error("interpreter expected field or method descriptor");
}
// create a synthetic type that has all extension fields for field options
const typeName = `$synthetic.${optionsTypeName}`;
let type = this.messageTypes.get(typeName);
if (!type) {
type = new rt.MessageType(typeName, this.buildFieldInfos(this.registry.extensionsFor(optionsTypeName)), {});
this.messageTypes.set(typeName, type);
}
// concat all unknown field data
const unknownWriter = new rt.BinaryWriter();
for (let { no, wireType, data } of unknownFields) {
unknownWriter.tag(no, wireType).raw(data);
}
const unknownBytes = unknownWriter.finish();
// read data, to json
const json = type.toJson(type.fromBinary(unknownBytes, { readUnknownField: false }));
runtime_1.assert(rt.isJsonObject(json));
// apply blacklist
if (excludeOptions) {
// we distinguish between literal blacklist (no wildcard)
let literals = excludeOptions.filter(str => !str.includes("*"));
// and wildcard, which we turn into RE
let wildcards = excludeOptions.filter(str => str.includes("*"))
.map(str => str.replace(/[.+\-?^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*'));
// then we delete the blacklisted options
for (let key of Object.keys(json)) {
for (let str of literals)
if (key === str)
delete json[key];
for (let re of wildcards)
if (key.match(re))
delete json[key];
}
}
// were *all* options blacklisted?
if (!Object.keys(json).length) {
return undefined;
}
return json;
}
/**
* Read the custom file options declared in protobuf-ts.proto
*/
readOurFileOptions(file) {
return our_options_1.readOurFileOptions(file);
}
/**
* Read the custom service options declared in protobuf-ts.proto
*/
readOurServiceOptions(service) {
return our_options_1.readOurServiceOptions(service);
}
/**
* Get a runtime type for the given message type name or message descriptor.
* Creates the type if not created previously.
*
* Honors our file option "ts.exclude_options".
*/
getMessageType(descriptorOrTypeName) {
let descriptor = typeof descriptorOrTypeName === "string"
? this.registry.resolveTypeName(descriptorOrTypeName)
: descriptorOrTypeName;
let typeName = this.registry.makeTypeName(descriptor);
runtime_1.assert(plugin_framework_1.DescriptorProto.is(descriptor));
let type = this.messageTypes.get(typeName);
if (!type) {
// Create and store the message type
const optionsPlaceholder = {};
type = new rt.MessageType(typeName, this.buildFieldInfos(descriptor.field), optionsPlaceholder);
this.messageTypes.set(typeName, type);
const ourFileOptions = this.readOurFileOptions(this.registry.fileOf(descriptor));
// add message options *after* storing, so that the option can refer to itself
const messageOptions = this.readOptions(descriptor, ourFileOptions["ts.exclude_options"]);
if (messageOptions) {
for (let key of Object.keys(messageOptions)) {
optionsPlaceholder[key] = messageOptions[key];
}
}
// same for field options
for (let i = 0; i < type.fields.length; i++) {
const fd = descriptor.field[i];
const fi = type.fields[i];
fi.options = this.readOptions(fd, ourFileOptions["ts.exclude_options"]);
}
}
return type;
}
/**
* Get a runtime type for the given service type name or service descriptor.
* Creates the type if not created previously.
*
* Honors our file option "ts.exclude_options".
*/
getServiceType(descriptorOrTypeName) {
let descriptor = typeof descriptorOrTypeName === "string"
? this.registry.resolveTypeName(descriptorOrTypeName)
: descriptorOrTypeName;
let typeName = this.registry.makeTypeName(descriptor);
runtime_1.assert(plugin_framework_1.ServiceDescriptorProto.is(descriptor));
let type = this.serviceTypes.get(typeName);
if (!type) {
const ourFileOptions = this.readOurFileOptions(this.registry.fileOf(descriptor));
type = this.buildServiceType(typeName, descriptor.method, ourFileOptions["ts.exclude_options"].concat("ts.client"));
this.serviceTypes.set(typeName, type);
}
return type;
}
/**
* Get runtime information for an enum.
* Creates the info if not created previously.
*/
getEnumInfo(descriptorOrTypeName) {
var _a;
let descriptor = typeof descriptorOrTypeName === "string"
? this.registry.resolveTypeName(descriptorOrTypeName)
: descriptorOrTypeName;
let typeName = this.registry.makeTypeName(descriptor);
runtime_1.assert(plugin_framework_1.EnumDescriptorProto.is(descriptor));
let enumInfo = (_a = this.enumInfos.get(typeName)) !== null && _a !== void 0 ? _a : this.buildEnumInfo(descriptor);
this.enumInfos.set(typeName, enumInfo);
return enumInfo;
}
static createTypescriptNameForMethod(descriptor) {
let escapeCharacter = '$';
let reservedClassProperties = [
// js built in
"__proto__", "toString", "name", "constructor",
// generic clients
"methods", "typeName", "options", "_transport",
// @grpc/grpc-js clients
"close", "getChannel", "waitForReady", "makeUnaryRequest", "makeClientStreamRequest", "makeServerStreamRequest", "makeBidiStreamRequest"
];
let name = descriptor.name;
runtime_1.assert(name !== undefined);
name = rt.lowerCamelCase(name);
if (reservedClassProperties.includes(name)) {
name = name + escapeCharacter;
}
return name;
}
buildServiceType(typeName, methods, excludeOptions) {
let desc = this.registry.resolveTypeName(typeName);
runtime_1.assert(plugin_framework_1.ServiceDescriptorProto.is(desc));
return new rpc.ServiceType(typeName, methods.map(m => this.buildMethodInfo(m, excludeOptions)), this.readOptions(desc, excludeOptions));
}
buildMethodInfo(methodDescriptor, excludeOptions) {
runtime_1.assert(methodDescriptor.name);
runtime_1.assert(methodDescriptor.inputType);
runtime_1.assert(methodDescriptor.outputType);
let info = {};
// name: The name of the method as declared in .proto
info.name = methodDescriptor.name;
// localName: The name of the method in the runtime.
let localName = Interpreter.createTypescriptNameForMethod(methodDescriptor);
if (localName !== rt.lowerCamelCase(methodDescriptor.name)) {
info.localName = localName;
}
// idempotency: The idempotency level as specified in .proto.
if (methodDescriptor.options) {
let idem = methodDescriptor.options.idempotencyLevel;
if (idem !== undefined && idem !== plugin_framework_1.MethodOptions_IdempotencyLevel.IDEMPOTENCY_UNKNOWN) {
info.idempotency = plugin_framework_1.MethodOptions_IdempotencyLevel[idem];
}
}
// serverStreaming: Was the rpc declared with server streaming?
if (methodDescriptor.serverStreaming) {
info.serverStreaming = true;
}
// clientStreaming: Was the rpc declared with client streaming?
if (methodDescriptor.clientStreaming) {
info.clientStreaming = true;
}
// I: The generated type handler for the input message.
info.I = this.getMessageType(methodDescriptor.inputType);
// O: The generated type handler for the output message.
info.O = this.getMessageType(methodDescriptor.outputType);
// options: Contains custom method options from the .proto source in JSON format.
info.options = this.readOptions(methodDescriptor, excludeOptions);
return info;
}
/**
* Create a name for a field or a oneof.
* - use lowerCamelCase unless useProtoFieldName option is enabled
* - escape reserved object property names by
* adding '$' at the end
* - don't have to escape reserved keywords
*/
createTypescriptNameForField(descriptor, escapeCharacter = '$') {
const reservedObjectProperties = '__proto__,toString'.split(',');
let name = descriptor.name;
runtime_1.assert(name !== undefined);
name = field_info_generator_1.FieldInfoGenerator.createTypescriptLocalName(name, this.options);
if (reservedObjectProperties.includes(name)) {
name = name + escapeCharacter;
}
if (this.options.oneofKindDiscriminator.split(',').includes(name)) {
name = name + escapeCharacter;
}
return name;
}
// skips GROUP field type
buildFieldInfos(fieldDescriptors) {
const result = [];
for (const fd of fieldDescriptors) {
if (this.registry.isGroupField(fd)) {
// We ignore groups.
// Note that groups are deprecated and not supported in proto3.
continue;
}
const fi = this.buildFieldInfo(fd);
if (fi) {
result.push(fi);
}
}
return result;
}
// throws on unexpected field types, notably GROUP
buildFieldInfo(fieldDescriptor) {
var _a, _b;
runtime_1.assert(fieldDescriptor.number);
runtime_1.assert(fieldDescriptor.name);
let info = {};
// no: The field number of the .proto field.
info.no = fieldDescriptor.number;
// name: The original name of the .proto field.
info.name = fieldDescriptor.name;
// kind: discriminator
info.kind = undefined;
// localName: The name of the field in the runtime.
let localName = this.createTypescriptNameForField(fieldDescriptor);
if (localName !== rt.lowerCamelCase(fieldDescriptor.name)) {
info.localName = localName;
}
// jsonName: The name of the field in JSON.
const jsonName = this.registry.getFieldCustomJsonName(fieldDescriptor);
if (jsonName !== undefined) {
info.jsonName = jsonName;
}
// oneof: The name of the `oneof` group, if this field belongs to one.
if (this.registry.isUserDeclaredOneof(fieldDescriptor)) {
runtime_1.assert(fieldDescriptor.oneofIndex !== undefined);
const parentDescriptor = this.registry.parentOf(fieldDescriptor);
runtime_1.assert(plugin_framework_1.DescriptorProto.is(parentDescriptor));
const ooDecl = parentDescriptor.oneofDecl[fieldDescriptor.oneofIndex];
info.oneof = this.createTypescriptNameForField(ooDecl);
}
// repeat: Is the field repeated?
if (this.registry.isUserDeclaredRepeated(fieldDescriptor)) {
let packed = this.registry.shouldBePackedRepeated(fieldDescriptor);
info.repeat = packed ? rt.RepeatType.PACKED : rt.RepeatType.UNPACKED;
}
// opt: Is the field optional?
if (this.registry.isScalarField(fieldDescriptor) || this.registry.isEnumField(fieldDescriptor)) {
if (this.registry.isUserDeclaredOptional(fieldDescriptor)) {
info.opt = true;
}
}
// jsonName: The name for JSON serialization / deserialization.
if (fieldDescriptor.jsonName) {
info.jsonName = fieldDescriptor.jsonName;
}
if (this.registry.isScalarField(fieldDescriptor)) {
// kind:
info.kind = "scalar";
// T: Scalar field type.
info.T = this.registry.getScalarFieldType(fieldDescriptor);
// L?: JavaScript long type
let L = this.determineNonDefaultLongType(info.T, (_a = fieldDescriptor.options) === null || _a === void 0 ? void 0 : _a.jstype);
if (L !== undefined) {
info.L = L;
}
}
else if (this.registry.isEnumField(fieldDescriptor)) {
// kind:
info.kind = "enum";
// T: Return enum field type info.
info.T = () => this.getEnumInfo(this.registry.getEnumFieldEnum(fieldDescriptor));
}
else if (this.registry.isMessageField(fieldDescriptor)) {
// kind:
info.kind = "message";
// T: Return message field type handler.
info.T = () => this.getMessageType(this.registry.getMessageFieldMessage(fieldDescriptor));
}
else if (this.registry.isMapField(fieldDescriptor)) {
// kind:
info.kind = "map";
// K: Map field key type.
info.K = this.registry.getMapKeyType(fieldDescriptor);
// V: Map field value type.
info.V = {};
let mapV = this.registry.getMapValueType(fieldDescriptor);
if (typeof mapV === "number") {
info.V = {
kind: "scalar",
T: mapV
};
let L = this.determineNonDefaultLongType(info.V.T, (_b = fieldDescriptor.options) === null || _b === void 0 ? void 0 : _b.jstype);
if (L !== undefined) {
info.V.L = L;
}
}
else if (plugin_framework_1.DescriptorProto.is(mapV)) {
const messageDescriptor = mapV;
info.V = {
kind: "message",
T: () => this.getMessageType(messageDescriptor)
};
}
else {
const enumDescriptor = mapV;
info.V = {
kind: "enum",
T: () => this.getEnumInfo(enumDescriptor)
};
}
}
else {
throw new Error(`Unexpected field type for ${this.registry.formatQualifiedName(fieldDescriptor)}`);
}
// extension fields are treated differently
if (this.registry.isExtension(fieldDescriptor)) {
let extensionName = this.registry.getExtensionName(fieldDescriptor);
// always optional (unless repeated...)
info.opt = info.repeat === undefined || info.repeat === rt.RepeatType.NO;
info.name = extensionName;
info.localName = extensionName;
info.jsonName = extensionName;
info.oneof = undefined;
}
return info;
}
buildEnumInfo(descriptor) {
let sharedPrefix = this.options.keepEnumPrefix
? undefined
: this.registry.findEnumSharedPrefix(descriptor, `${descriptor.name}`);
let hasZero = descriptor.value.some(v => v.number === 0);
let builder = new RuntimeEnumBuilder();
if (!hasZero && typeof this.options.synthesizeEnumZeroValue == 'string') {
builder.add(this.options.synthesizeEnumZeroValue, 0);
}
for (let enumValueDescriptor of descriptor.value) {
let name = enumValueDescriptor.name;
runtime_1.assert(name !== undefined);
runtime_1.assert(enumValueDescriptor.number !== undefined);
if (sharedPrefix) {
name = name.substring(sharedPrefix.length);
}
builder.add(name, enumValueDescriptor.number);
}
let enumInfo = [
this.registry.makeTypeName(descriptor),
builder.build(),
];
if (sharedPrefix) {
enumInfo = [enumInfo[0], enumInfo[1], sharedPrefix];
}
return enumInfo;
}
determineNonDefaultLongType(scalarType, jsTypeOption) {
if (!Interpreter.isLongValueType(scalarType)) {
return undefined;
}
if (jsTypeOption !== undefined) {
switch (jsTypeOption) {
case plugin_framework_1.FieldOptions_JSType.JS_STRING:
// omitting L equals to STRING
return undefined;
case plugin_framework_1.FieldOptions_JSType.JS_NORMAL:
return rt.LongType.BIGINT;
case plugin_framework_1.FieldOptions_JSType.JS_NUMBER:
return rt.LongType.NUMBER;
}
}
// at this point, there either was no js_type option or it was JS_NORMAL,
// so we use our normal long type
if (this.options.normalLongType === rt.LongType.STRING) {
// since STRING is default, we do not set it
return undefined;
}
return this.options.normalLongType;
}
/**
* Is this a 64 bit integral or fixed type?
*/
static isLongValueType(type) {
switch (type) {
case rt.ScalarType.INT64:
case rt.ScalarType.UINT64:
case rt.ScalarType.FIXED64:
case rt.ScalarType.SFIXED64:
case rt.ScalarType.SINT64:
return true;
default:
return false;
}
}
}
exports.Interpreter = Interpreter;
/**
* Builds a typescript enum lookup object,
* compatible with enums generated by @protobuf-ts/plugin.
*/
class RuntimeEnumBuilder {
constructor() {
this.values = [];
}
add(name, number) {
this.values.push({ name, number });
}
isValid() {
try {
this.build();
}
catch (e) {
return false;
}
return true;
}
build() {
if (this.values.map(v => v.name).some((name, i, a) => a.indexOf(name) !== i)) {
throw new Error("duplicate names");
}
let object = {};
for (let v of this.values) {
object[v.number] = v.name;
object[v.name] = v.number;
}
if (rt.isEnumObject(object)) {
return object;
}
throw new Error("not a typescript enum object");
}
}
exports.RuntimeEnumBuilder = RuntimeEnumBuilder;

View file

@ -0,0 +1,24 @@
import { DescriptorProto, DescriptorRegistry, TypescriptFile, TypeScriptImports } from "@protobuf-ts/plugin-framework";
import * as ts from "typescript";
import { LongType } from "@protobuf-ts/runtime";
import { Interpreter } from "../interpreter";
import { CustomMethodGenerator } from "../code-gen/message-type-generator";
/**
* Generates a "create()" method for an `IMessageType`
*/
export declare class Create implements CustomMethodGenerator {
private readonly registry;
private readonly imports;
private readonly interpreter;
private readonly options;
constructor(registry: DescriptorRegistry, imports: TypeScriptImports, interpreter: Interpreter, options: {
normalLongType: LongType;
oneofKindDiscriminator: string;
runtimeImportPath: string;
});
make(source: TypescriptFile, descriptor: DescriptorProto): ts.MethodDeclaration[];
makeMethod(source: TypescriptFile, descriptor: DescriptorProto, ...bodyStatements: readonly ts.Statement[]): ts.MethodDeclaration;
makeMessageVariable(): ts.VariableStatement;
makeMessagePropertyAssignments(source: TypescriptFile, descriptor: DescriptorProto): ts.ExpressionStatement[];
makeMergeIf(source: TypescriptFile, descriptor: DescriptorProto): ts.IfStatement;
}

View file

@ -0,0 +1,61 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Create = void 0;
const plugin_framework_1 = require("@protobuf-ts/plugin-framework");
const ts = require("typescript");
/**
* Generates a "create()" method for an `IMessageType`
*/
class Create {
constructor(registry, imports, interpreter, options) {
this.registry = registry;
this.imports = imports;
this.interpreter = interpreter;
this.options = options;
}
// create(value?: PartialMessage<ScalarValuesMessage>): ScalarValuesMessage {
make(source, descriptor) {
// create(value?: PartialMessage<ScalarValuesMessage>): ScalarValuesMessage {
let methodDeclaration = this.makeMethod(source, descriptor,
// const message = globalThis.Object.create(this.messagePrototype);
this.makeMessageVariable(),
// message.boolField = false;
// message.repeatedField = [];
// message.mapField = {};
// ...
...this.makeMessagePropertyAssignments(source, descriptor),
// if (value !== undefined)
// reflectionMergePartial<ScalarValuesMessage>(message, value, this);
this.makeMergeIf(source, descriptor),
// return message;
ts.createReturn(ts.createIdentifier("message")));
return [methodDeclaration];
}
makeMethod(source, descriptor, ...bodyStatements) {
const MessageInterface = this.imports.type(source, descriptor), PartialMessage = this.imports.name(source, 'PartialMessage', this.options.runtimeImportPath, true);
return ts.createMethod(undefined, undefined, undefined, ts.createIdentifier("create"), undefined, undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("value"), ts.createToken(ts.SyntaxKind.QuestionToken), ts.createTypeReferenceNode(PartialMessage, [
ts.createTypeReferenceNode((MessageInterface), undefined)
]), undefined)
], ts.createTypeReferenceNode(MessageInterface, undefined), ts.createBlock(bodyStatements, true));
}
makeMessageVariable() {
return ts.createVariableStatement(undefined, ts.createVariableDeclarationList([ts.createVariableDeclaration(ts.createIdentifier("message"), undefined, ts.createCall(ts.createPropertyAccess(ts.createPropertyAccess(ts.createIdentifier("globalThis"), ts.createIdentifier("Object")), ts.createIdentifier("create")), undefined, [
ts.createNonNullExpression(ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("messagePrototype")))
]))], ts.NodeFlags.Const));
}
makeMessagePropertyAssignments(source, descriptor) {
let messageType = this.interpreter.getMessageType(descriptor);
let defaultMessage = messageType.create();
return Object.entries(defaultMessage).map(([key, value]) => (ts.createExpressionStatement(ts.createBinary(ts.createPropertyAccess(ts.createIdentifier("message"), ts.createIdentifier(key)), ts.createToken(ts.SyntaxKind.EqualsToken), plugin_framework_1.typescriptLiteralFromValue(value)))));
}
makeMergeIf(source, descriptor) {
const MessageInterface = this.imports.type(source, descriptor);
return ts.createIf(ts.createBinary(ts.createIdentifier("value"), ts.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken), ts.createIdentifier("undefined")), ts.createExpressionStatement(ts.createCall(ts.createIdentifier(this.imports.name(source, 'reflectionMergePartial', this.options.runtimeImportPath)), [ts.createTypeReferenceNode(MessageInterface, undefined)], [
ts.createThis(),
ts.createIdentifier("message"),
ts.createIdentifier("value"),
])), undefined);
}
}
exports.Create = Create;

View file

@ -0,0 +1,22 @@
import { DescriptorProto, ITypeNameLookup, TypescriptFile, TypeScriptImports } from "@protobuf-ts/plugin-framework";
import * as ts from "typescript";
import { LongType } from "@protobuf-ts/runtime";
import { CustomMethodGenerator } from "../code-gen/message-type-generator";
export declare class GoogleTypes implements CustomMethodGenerator {
private readonly typeNameLookup;
private readonly imports;
private readonly options;
constructor(typeNameLookup: ITypeNameLookup, imports: TypeScriptImports, options: {
normalLongType: LongType;
runtimeImportPath: string;
useProtoFieldName: boolean;
});
/**
* Create custom methods for the handlers of some google types.
*/
make(source: TypescriptFile, descriptor: DescriptorProto): ts.MethodDeclaration[];
['google.type.Color'](source: TypescriptFile, descriptor: DescriptorProto): string[];
['google.type.Date'](source: TypescriptFile, descriptor: DescriptorProto): string[];
['google.type.DateTime'](source: TypescriptFile, descriptor: DescriptorProto): string[];
['google.type.TimeOfDay'](source: TypescriptFile, descriptor: DescriptorProto): string[];
}

View file

@ -0,0 +1,241 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GoogleTypes = void 0;
const plugin_framework_1 = require("@protobuf-ts/plugin-framework");
const runtime_1 = require("@protobuf-ts/runtime");
const field_info_generator_1 = require("../code-gen/field-info-generator");
class GoogleTypes {
constructor(typeNameLookup, imports, options) {
this.typeNameLookup = typeNameLookup;
this.imports = imports;
this.options = options;
}
/**
* Create custom methods for the handlers of some google types.
*/
make(source, descriptor) {
const typeName = this.typeNameLookup.makeTypeName(descriptor), fn = this[typeName];
if (fn) {
let r = fn.apply(this, [source, descriptor]);
if (typeof r == "string") {
return [plugin_framework_1.typescriptMethodFromText(r)];
}
if (Array.isArray(r)) {
return r.map(txt => plugin_framework_1.typescriptMethodFromText(txt));
}
}
return [];
}
['google.type.Color'](source, descriptor) {
const Color = this.imports.type(source, descriptor);
return [
`
/**
* Returns hexadecimal notation of the color: #RRGGBB[AA]
*
* R (red), G (green), B (blue), and A (alpha) are hexadecimal characters
* (09, AF). A is optional. For example, #ff0000 is equivalent to
* #ff0000ff.
*
* See https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#RGB_colors
*/
function toHex(message: ${Color}): string {
let hex = [
message.red.toString(16),
message.green.toString(16),
message.blue.toString(16),
];
if (message.alpha) {
let alpha = Math.max(Math.min(message.alpha.value, 1), 0);
hex.push(Math.round(alpha * 255).toString(16));
}
return '#' + hex.map(i => i.length < 2 ? '0' + i : i).join('');
}
`,
`
/**
* Parses a hexadecimal color notation.
*
* Recognizes the following forms:
* - three-digit (#RGB)
* - six-digit (#RRGGBB)
* - four-digit (#RGBA)
* - eight-digit (#RRGGBBAA)
*/
function fromHex(hex: string): ${Color} {
if (/^#(?:[0-9a-fA-F]{3}){1}$/.test(hex)) {
// #RGB
return {
red: parseInt(hex.substring(1, 2) + hex.substring(1, 2), 16),
green: parseInt(hex.substring(2, 3) + hex.substring(2, 3), 16),
blue: parseInt(hex.substring(3, 4) + hex.substring(3, 4), 16),
};
} else if (/^#(?:[0-9a-fA-F]{3}){2}$/.test(hex)) {
// #RRGGBB
return {
red: parseInt(hex.substring(1, 3), 16),
green: parseInt(hex.substring(3, 5), 16),
blue: parseInt(hex.substring(5, 7), 16),
};
} else if (/^#(?:[0-9a-fA-F]{4}){1}$/.test(hex)) {
// #RGBA
return {
red: parseInt(hex.substring(1, 2) + hex.substring(1, 2), 16),
green: parseInt(hex.substring(2, 3) + hex.substring(2, 3), 16),
blue: parseInt(hex.substring(3, 4) + hex.substring(3, 4), 16),
alpha: {
value: parseInt(hex.substring(4, 5) + hex.substring(4, 5), 16) / 255,
}
};
} else if (/^#(?:[0-9a-fA-F]{4}){2}$/.test(hex)) {
// #RRGGBBAA
return {
red: parseInt(hex.substring(1, 3), 16),
green: parseInt(hex.substring(3, 5), 16),
blue: parseInt(hex.substring(5, 7), 16),
alpha: {
value: parseInt(hex.substring(7, 9), 16) / 255,
}
};
}
throw new Error('invalid hex color');
}
`,
];
}
['google.type.Date'](source, descriptor) {
const Date = this.imports.type(source, descriptor);
return [
`
/**
* Creates a javascript Date object from the message.
*
* If you do not provide the optional parameters for time,
* the current time is used.
*/
function toJsDate(message: ${Date}, hours?: number, minutes?: number, seconds?: number, ms?: number): globalThis.Date {
let now = new globalThis.Date();
return new globalThis.Date(
message.year,
message.month - 1,
message.day,
hours ?? now.getHours(),
minutes ?? now.getMinutes(),
seconds ?? now.getSeconds(),
ms ?? now.getMilliseconds(),
);
}
`, `
/**
* Creates a Date message from a javascript Date object.
*/
function fromJsDate(date: globalThis.Date): ${Date} {
return {
year: date.getFullYear(),
month: date.getMonth() + 1,
day: date.getDate(),
};
}
`,
];
}
['google.type.DateTime'](source, descriptor) {
const DateTime = this.imports.type(source, descriptor);
const PbLong = this.imports.name(source, 'PbLong', this.options.runtimeImportPath);
let longConvertMethod = 'toBigInt';
if (this.options.normalLongType === runtime_1.LongType.NUMBER)
longConvertMethod = 'toNumber';
else if (this.options.normalLongType === runtime_1.LongType.STRING)
longConvertMethod = 'toString';
const utcOffsetField = field_info_generator_1.FieldInfoGenerator.createTypescriptLocalName('utc_offset', this.options), timeOffsetField = field_info_generator_1.FieldInfoGenerator.createTypescriptLocalName('time_offset', this.options), timeZoneField = field_info_generator_1.FieldInfoGenerator.createTypescriptLocalName('time_zone', this.options);
return [
`
/**
* Creates \`DateTime\` for the current time.
*/
function now(): ${DateTime} {
return this.fromJsDate(new globalThis.Date());
}
`, `
/**
* Creates a javascript Date object from the message.
*
* If a the message has a UTC offset, the javascript Date is converted
* into your local time zone, because javascript Dates are always in the
* local time zone.
*
* If the message has an offset given as an IANA timezone id, an error is
* thrown, because javascript has no on-board support for IANA time zone
* ids.
*/
function toJsDate(message: ${DateTime}): globalThis.Date {
let dt = new globalThis.Date(
message.year,
message.month - 1,
message.day,
message.hours,
message.minutes,
message.seconds,
message.nanos / 1000,
),
to = message.${timeOffsetField};
if (to) {
if (to.oneofKind === "${timeZoneField}")
throw new globalThis.Error("IANA time zone not supported");
if (to.oneofKind === "${utcOffsetField}") {
let s = ${PbLong}.from(to.${utcOffsetField}.seconds).toNumber();
dt = new globalThis.Date(dt.getTime() - (s * 1000));
}
}
return dt;
}
`, `
/**
* Creates a Date message from a javascript Date object.
*
* Values are in local time and a proper UTF offset is provided.
*/
function fromJsDate(date: globalThis.Date): ${DateTime} {
return {
year: date.getFullYear(),
month: date.getMonth() + 1,
day: date.getDate(),
hours: date.getHours(),
minutes: date.getMinutes(),
seconds: date.getSeconds(),
nanos: date.getMilliseconds() * 1000,
${timeOffsetField}: {
oneofKind: "${utcOffsetField}",
${utcOffsetField}: {
seconds: ${PbLong}.from(date.getTimezoneOffset() * 60).${longConvertMethod}(),
nanos: 0,
}
}
};
}
`,
];
}
['google.type.TimeOfDay'](source, descriptor) {
const TimeOfDay = this.imports.type(source, descriptor);
return [
`
/**
* Creates a TimeOfDay message from a javascript Date object.
*/
function fromJsDate(date: globalThis.Date): ${TimeOfDay} {
return {
hours: date.getHours(),
minutes: date.getMinutes(),
seconds: date.getSeconds(),
nanos: date.getMilliseconds() * 1000,
};
}
`,
];
}
}
exports.GoogleTypes = GoogleTypes;

View file

@ -0,0 +1,67 @@
import * as ts from "typescript";
import { DescriptorProto, DescriptorRegistry, TypescriptFile, TypeScriptImports } from "@protobuf-ts/plugin-framework";
import * as rt from "@protobuf-ts/runtime";
import { LongType } from "@protobuf-ts/runtime";
import { CustomMethodGenerator } from "../code-gen/message-type-generator";
import { Interpreter } from "../interpreter";
/**
* Generates a "internalBinaryRead()" method for an `IMessageType`
*/
export declare class InternalBinaryRead implements CustomMethodGenerator {
private readonly registry;
private readonly imports;
private readonly interpreter;
private readonly options;
constructor(registry: DescriptorRegistry, imports: TypeScriptImports, interpreter: Interpreter, options: {
normalLongType: LongType;
oneofKindDiscriminator: string;
runtimeImportPath: string;
});
private readonly binaryReadMapEntryMethodName;
make(source: TypescriptFile, descriptor: DescriptorProto): ts.MethodDeclaration[];
makeMethod(source: TypescriptFile, descriptor: DescriptorProto, ...bodyStatements: readonly ts.Statement[]): ts.MethodDeclaration;
makeVariables(): ts.VariableStatement;
makeWhileSwitch(switchCases: ts.CaseClause[], defaultClause: ts.DefaultClause): ts.WhileStatement;
makeCaseClauses(source: TypescriptFile, descriptor: DescriptorProto): ts.CaseClause[];
makeDefaultClause(source: TypescriptFile): ts.DefaultClause;
map(field: rt.FieldInfo & {
kind: "map";
}, fieldPropertyAccess: ts.PropertyAccessExpression): ts.Statement[];
message(source: TypescriptFile, field: rt.FieldInfo & {
kind: "message";
repeat: undefined | rt.RepeatType.NO;
oneof: undefined;
}, fieldPropertyAccess: ts.PropertyAccessExpression): ts.Statement[];
messageOneof(source: TypescriptFile, field: rt.FieldInfo & {
kind: "message";
repeat: undefined | rt.RepeatType.NO;
oneof: string;
}): ts.Statement[];
messageRepeated(source: TypescriptFile, field: rt.FieldInfo & {
kind: "message";
repeat: rt.RepeatType.PACKED | rt.RepeatType.UNPACKED;
oneof: undefined;
}, fieldPropertyAccess: ts.PropertyAccessExpression): ts.Statement[];
scalar(field: rt.FieldInfo & {
kind: "scalar" | "enum";
oneof: undefined;
repeat: undefined | rt.RepeatType.NO;
}, fieldPropertyAccess: ts.PropertyAccessExpression): ts.Statement[];
scalarOneof(field: rt.FieldInfo & {
kind: "scalar" | "enum";
oneof: string;
repeat: undefined | rt.RepeatType.NO;
}): ts.Statement[];
scalarRepeated(source: TypescriptFile, field: rt.FieldInfo & {
kind: "scalar" | "enum";
oneof: undefined;
repeat: rt.RepeatType.PACKED | rt.RepeatType.UNPACKED;
}, fieldPropertyAccess: ts.PropertyAccessExpression): ts.Statement[];
makeMapEntryReadMethod(source: TypescriptFile, messageDescriptor: DescriptorProto, field: rt.FieldInfo & {
kind: "map";
}): ts.MethodDeclaration;
private createMapKeyDefaultValue;
private createMapValueDefaultValue;
private createScalarDefaultValue;
makeReaderCall(readerExpressionOrName: string | ts.Expression, type: rt.ScalarType, longType?: rt.LongType): ts.Expression;
}

View file

@ -0,0 +1,368 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.InternalBinaryRead = void 0;
const ts = require("typescript");
const plugin_framework_1 = require("@protobuf-ts/plugin-framework");
const rt = require("@protobuf-ts/runtime");
const runtime_1 = require("@protobuf-ts/runtime");
const interpreter_1 = require("../interpreter");
/**
* Generates a "internalBinaryRead()" method for an `IMessageType`
*/
class InternalBinaryRead {
constructor(registry, imports, interpreter, options) {
this.registry = registry;
this.imports = imports;
this.interpreter = interpreter;
this.options = options;
this.binaryReadMapEntryMethodName = "binaryReadMap";
}
make(source, descriptor) {
const methods = [];
// if the message has no fields, we produce a much shorter body:
// return target ?? this.create();
if (descriptor.field.length === 0) {
return [this.makeMethod(source, descriptor, ts.createReturn(ts.createBinary(ts.createIdentifier("target"), ts.createToken(ts.SyntaxKind.QuestionQuestionToken), ts.createCall(ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("create")), undefined, []))))];
}
// internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: ${messageInterfaceId}): ${messageInterfaceId} {
let internalBinaryRead = this.makeMethod(source, descriptor,
// let message = target ?? this.create(), end = reader.pos + length;
this.makeVariables(),
// while (reader.pos < end) {
// let [fieldNo, wireType] = reader.tag();
// switch (fieldNo) {
this.makeWhileSwitch(
// case ...:
this.makeCaseClauses(source, descriptor),
// default:
// ...
this.makeDefaultClause(source)),
// return message
ts.createReturn(ts.createIdentifier("message")));
methods.push(internalBinaryRead);
for (let fieldInfo of this.interpreter.getMessageType(descriptor).fields) {
if (fieldInfo.kind == "map") {
methods.push(this.makeMapEntryReadMethod(source, descriptor, fieldInfo));
}
}
return methods;
}
makeMethod(source, descriptor, ...bodyStatements) {
const MessageInterface = this.imports.type(source, descriptor), IBinaryReader = this.imports.name(source, 'IBinaryReader', this.options.runtimeImportPath, true), BinaryReadOptions = this.imports.name(source, 'BinaryReadOptions', this.options.runtimeImportPath, true);
return ts.createMethod(undefined, undefined, undefined, ts.createIdentifier("internalBinaryRead"), undefined, undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("reader"), undefined, ts.createTypeReferenceNode(IBinaryReader, undefined), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("length"), undefined, ts.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("options"), undefined, ts.createTypeReferenceNode(BinaryReadOptions, undefined), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("target"), ts.createToken(ts.SyntaxKind.QuestionToken), ts.createTypeReferenceNode(MessageInterface, undefined), undefined)
], ts.createTypeReferenceNode(MessageInterface, undefined), ts.createBlock(bodyStatements, true));
}
makeVariables() {
// let message = target ?? this.create(), end = reader.pos + length;
return ts.createVariableStatement(undefined, ts.createVariableDeclarationList([
ts.createVariableDeclaration(ts.createIdentifier("message"), undefined, ts.createBinary(ts.createIdentifier("target"), ts.createToken(ts.SyntaxKind.QuestionQuestionToken), ts.createCall(ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("create")), undefined, []))),
ts.createVariableDeclaration(ts.createIdentifier("end"), undefined, ts.createBinary(ts.createPropertyAccess(ts.createIdentifier("reader"), ts.createIdentifier("pos")), ts.createToken(ts.SyntaxKind.PlusToken), ts.createIdentifier("length")))
], ts.NodeFlags.Let));
}
makeWhileSwitch(switchCases, defaultClause) {
return ts.createWhile(ts.createBinary(ts.createPropertyAccess(ts.createIdentifier("reader"), ts.createIdentifier("pos")), ts.createToken(ts.SyntaxKind.LessThanToken), ts.createIdentifier("end")), ts.createBlock([
ts.createVariableStatement(undefined, ts.createVariableDeclarationList([ts.createVariableDeclaration(ts.createArrayBindingPattern([
ts.createBindingElement(undefined, undefined, ts.createIdentifier("fieldNo"), undefined),
ts.createBindingElement(undefined, undefined, ts.createIdentifier("wireType"), undefined)
]), undefined, ts.createCall(ts.createPropertyAccess(ts.createIdentifier("reader"), ts.createIdentifier("tag")), undefined, []))], ts.NodeFlags.Let)),
ts.createSwitch(ts.createIdentifier("fieldNo"), ts.createCaseBlock([
...switchCases,
defaultClause
]))
], true));
}
makeCaseClauses(source, descriptor) {
const interpreterType = this.interpreter.getMessageType(descriptor), clauses = [];
for (let fieldInfo of interpreterType.fields) {
let statements, fieldPropertyAccess = ts.createPropertyAccess(ts.createIdentifier("message"), fieldInfo.localName);
switch (fieldInfo.kind) {
case "scalar":
case "enum":
if (fieldInfo.repeat) {
statements = this.scalarRepeated(source, fieldInfo, fieldPropertyAccess);
}
else if (fieldInfo.oneof !== undefined) {
statements = this.scalarOneof(fieldInfo);
}
else {
statements = this.scalar(fieldInfo, fieldPropertyAccess);
}
break;
case "message":
if (fieldInfo.repeat) {
statements = this.messageRepeated(source, fieldInfo, fieldPropertyAccess);
}
else if (fieldInfo.oneof !== undefined) {
statements = this.messageOneof(source, fieldInfo);
}
else {
statements = this.message(source, fieldInfo, fieldPropertyAccess);
}
break;
case "map":
statements = this.map(fieldInfo, fieldPropertyAccess);
break;
}
// case /* double double_field */ 1:
let fieldDescriptor = descriptor.field.find(fd => fd.number === fieldInfo.no);
runtime_1.assert(fieldDescriptor !== undefined);
let fieldNumber = ts.createNumericLiteral(`${fieldInfo.no}`);
ts.addSyntheticLeadingComment(fieldNumber, ts.SyntaxKind.MultiLineCommentTrivia, ' ' + this.registry.formatFieldDeclaration(fieldDescriptor).replace(/= \d+;$/, ''), false);
clauses.push(ts.createCaseClause(fieldNumber, [...statements, ts.createBreak(undefined)]));
}
return clauses;
}
makeDefaultClause(source) {
let UnknownFieldHandler = this.imports.name(source, 'UnknownFieldHandler', this.options.runtimeImportPath);
return ts.createDefaultClause([
ts.createVariableStatement(undefined, ts.createVariableDeclarationList([ts.createVariableDeclaration(ts.createIdentifier("u"), undefined, ts.createPropertyAccess(ts.createIdentifier("options"), ts.createIdentifier("readUnknownField")))], ts.NodeFlags.Let)),
ts.createIf(ts.createBinary(ts.createIdentifier("u"), ts.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), ts.createStringLiteral("throw")), ts.createThrow(ts.createNew(ts.createPropertyAccess(ts.createIdentifier("globalThis"), ts.createIdentifier("Error")), undefined, [ts.createTemplateExpression(ts.createTemplateHead("Unknown field ", "Unknown field "), [
ts.createTemplateSpan(ts.createIdentifier("fieldNo"), ts.createTemplateMiddle(" (wire type ", " (wire type ")),
ts.createTemplateSpan(ts.createIdentifier("wireType"), ts.createTemplateMiddle(") for ", ") for ")),
ts.createTemplateSpan(ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("typeName")), ts.createTemplateTail("", ""))
])])), undefined),
ts.createVariableStatement(undefined, ts.createVariableDeclarationList([ts.createVariableDeclaration(ts.createIdentifier("d"), undefined, ts.createCall(ts.createPropertyAccess(ts.createIdentifier("reader"), ts.createIdentifier("skip")), undefined, [ts.createIdentifier("wireType")]))], ts.NodeFlags.Let)),
ts.createIf(ts.createBinary(ts.createIdentifier("u"), ts.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken), ts.createFalse()), ts.createExpressionStatement(ts.createCall(ts.createParen(ts.createConditional(ts.createBinary(ts.createIdentifier("u"), ts.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), ts.createTrue()), ts.createToken(ts.SyntaxKind.QuestionToken), ts.createPropertyAccess(ts.createIdentifier(UnknownFieldHandler), ts.createIdentifier("onRead")), ts.createToken(ts.SyntaxKind.ColonToken), ts.createIdentifier("u"))), undefined, [
ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("typeName")),
ts.createIdentifier("message"),
ts.createIdentifier("fieldNo"),
ts.createIdentifier("wireType"),
ts.createIdentifier("d")
])), undefined)
]);
}
// message.int32StrField[reader.skip(0).skipBytes(1).int32()] = reader.skipBytes(1).string();
// message.msgField[reader.skip(0).skipBytes(1).int32()] = OtherMessage.internalBinaryRead(reader, reader.skipBytes(1).uint32(), options);
map(field, fieldPropertyAccess) {
return [ts.createExpressionStatement(ts.createCall(ts.createPropertyAccess(ts.createThis(), ts.createIdentifier(this.binaryReadMapEntryMethodName + field.no)), undefined, [
fieldPropertyAccess,
ts.createIdentifier("reader"),
ts.createIdentifier("options")
]))];
}
// message.field = OtherMessage.internalBinaryRead(reader, reader.uint32(), options, message.field);
message(source, field, fieldPropertyAccess) {
let messageDescriptor = this.registry.resolveTypeName(field.T().typeName);
runtime_1.assert(plugin_framework_1.DescriptorProto.is(messageDescriptor));
let handlerMergeCall = ts.createCall(ts.createPropertyAccess(ts.createIdentifier(this.imports.type(source, messageDescriptor)), ts.createIdentifier("internalBinaryRead")), undefined, [
ts.createIdentifier("reader"),
this.makeReaderCall("reader", rt.ScalarType.UINT32),
ts.createIdentifier("options"),
fieldPropertyAccess
]);
return [ts.createExpressionStatement(ts.createBinary(ts.createPropertyAccess(ts.createIdentifier("message"), field.localName), ts.createToken(ts.SyntaxKind.EqualsToken), handlerMergeCall))];
}
// message.result = {
// oneofKind: "msg",
// msg: OtherMessage.internalBinaryRead(reader, reader.uint32(), options, (message.result as any).msg)
// };
messageOneof(source, field) {
let messageDescriptor = this.registry.resolveTypeName(field.T().typeName);
runtime_1.assert(plugin_framework_1.DescriptorProto.is(messageDescriptor));
let handlerMergeCall = ts.createCall(ts.createPropertyAccess(ts.createIdentifier(this.imports.type(source, messageDescriptor)), ts.createIdentifier("internalBinaryRead")), undefined, [
ts.createIdentifier("reader"),
this.makeReaderCall("reader", rt.ScalarType.UINT32),
ts.createIdentifier("options"),
ts.createPropertyAccess(ts.createParen(ts.createAsExpression(ts.createPropertyAccess(ts.createIdentifier("message"), field.oneof), ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword))), ts.createIdentifier(field.localName))
]);
return [ts.createExpressionStatement(ts.createBinary(ts.createPropertyAccess(ts.createIdentifier("message"), field.oneof), ts.createToken(ts.SyntaxKind.EqualsToken), ts.createObjectLiteral([
// oneofKind: "msg",
ts.createPropertyAssignment(ts.createIdentifier(this.options.oneofKindDiscriminator), ts.createStringLiteral(field.localName)),
// msg: OtherMessage.internalBinaryRead(reader, reader.uint32(), options, (message.result as any).msg)
ts.createPropertyAssignment(field.localName, handlerMergeCall)
], true)))];
}
// message.field.push(OtherMessage.internalBinaryRead(reader, reader.uint32(), options));
messageRepeated(source, field, fieldPropertyAccess) {
let messageDescriptor = this.registry.resolveTypeName(field.T().typeName);
runtime_1.assert(plugin_framework_1.DescriptorProto.is(messageDescriptor));
let handlerMergeCall = ts.createCall(ts.createPropertyAccess(ts.createIdentifier(this.imports.type(source, messageDescriptor)), ts.createIdentifier("internalBinaryRead")), undefined, [
ts.createIdentifier("reader"),
this.makeReaderCall("reader", rt.ScalarType.UINT32),
ts.createIdentifier("options"),
]);
return [ts.createExpressionStatement(ts.createCall(ts.createPropertyAccess(fieldPropertyAccess, ts.createIdentifier("push")), undefined, [handlerMergeCall]))];
}
// message.doubleField = reader.double();
scalar(field, fieldPropertyAccess) {
let type = field.kind == "enum" ? rt.ScalarType.INT32 : field.T;
let longType = field.kind == "enum" ? undefined : field.L;
let readerCall = this.makeReaderCall("reader", type, longType);
return [ts.createExpressionStatement(ts.createBinary(fieldPropertyAccess, ts.createToken(ts.SyntaxKind.EqualsToken), readerCall))];
}
// message.result = {
// oneofKind: "err",
// err: reader.string()
// };
scalarOneof(field) {
let type = field.kind == "enum" ? rt.ScalarType.INT32 : field.T;
let longType = field.kind == "enum" ? undefined : field.L;
return [ts.createExpressionStatement(ts.createBinary(ts.createPropertyAccess(ts.createIdentifier("message"), field.oneof), ts.createToken(ts.SyntaxKind.EqualsToken), ts.createObjectLiteral([
// oneofKind: "err",
ts.createPropertyAssignment(ts.createIdentifier(this.options.oneofKindDiscriminator), ts.createStringLiteral(field.localName)),
// err: reader.string()
ts.createPropertyAssignment(field.localName, this.makeReaderCall("reader", type, longType))
], true)))];
}
// if (wireType === WireType.LengthDelimited)
// for (const e = reader.int32() + reader.pos; reader.pos < e;)
// message.doubleField.push(reader.double());
// else
// message.doubleField.push(reader.double());
scalarRepeated(source, field, fieldPropertyAccess) {
let type = field.kind == "enum" ? rt.ScalarType.INT32 : field.T;
let longType = field.kind == "enum" ? undefined : field.L;
switch (type) {
case rt.ScalarType.STRING:
case rt.ScalarType.BYTES:
// never packed
// message.${fieldName}.push(reader.${readerMethod}());
return [ts.createExpressionStatement(ts.createCall(ts.createPropertyAccess(fieldPropertyAccess, ts.createIdentifier("push")), undefined, [this.makeReaderCall('reader', type, longType)]))];
default:
// maybe packed
return [
ts.createIf(ts.createBinary(ts.createIdentifier("wireType"), ts.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), ts.createPropertyAccess(ts.createIdentifier(this.imports.name(source, 'WireType', this.options.runtimeImportPath)), 'LengthDelimited')
// ts.addSyntheticTrailingComment(
// ts.createNumericLiteral(WireType.LengthDelimited.toString()),
// ts.SyntaxKind.MultiLineCommentTrivia, " packed! ", false
// )
), ts.createFor(ts.createVariableDeclarationList([ts.createVariableDeclaration(ts.createIdentifier("e"), undefined, ts.createBinary(this.makeReaderCall("reader", rt.ScalarType.INT32), ts.createToken(ts.SyntaxKind.PlusToken), ts.createPropertyAccess(ts.createIdentifier("reader"), ts.createIdentifier("pos"))))], ts.NodeFlags.Let), ts.createBinary(ts.createPropertyAccess(ts.createIdentifier("reader"), ts.createIdentifier("pos")), ts.createToken(ts.SyntaxKind.LessThanToken), ts.createIdentifier("e")), undefined, ts.createExpressionStatement(ts.createCall(ts.createPropertyAccess(fieldPropertyAccess, ts.createIdentifier("push")), undefined, [this.makeReaderCall("reader", type, longType)]))), ts.createExpressionStatement(ts.createCall(ts.createPropertyAccess(fieldPropertyAccess, ts.createIdentifier("push")), undefined, [this.makeReaderCall("reader", type, longType)]))),
];
}
}
// binaryReadMapEntry<field no>(map: ExampleResponse["<field local name>"], reader: IBinaryReader, options: BinaryReadOptions): void
makeMapEntryReadMethod(source, messageDescriptor, field) {
let methodName = this.binaryReadMapEntryMethodName + field.no, fieldDescriptor = messageDescriptor.field.find(fd => fd.number === field.no), MessageInterface = this.imports.type(source, messageDescriptor), IBinaryReader = this.imports.name(source, 'IBinaryReader', this.options.runtimeImportPath, true), BinaryReadOptions = this.imports.name(source, 'BinaryReadOptions', this.options.runtimeImportPath, true), methodStatements = [];
runtime_1.assert(fieldDescriptor !== undefined);
// let len = reader.uint32(), end = reader.pos + len, key: keyof EnumMapMessage["int64EnuField"] | undefined, val: EnumMapMessage["int64EnuField"][any] | undefined;
methodStatements.push(ts.createVariableStatement(undefined, ts.createVariableDeclarationList([
ts.createVariableDeclaration(ts.createIdentifier("len"), undefined, ts.createCall(ts.createPropertyAccess(ts.createIdentifier("reader"), ts.createIdentifier("uint32")), undefined, [])),
ts.createVariableDeclaration(ts.createIdentifier("end"), undefined, ts.createBinary(ts.createPropertyAccess(ts.createIdentifier("reader"), ts.createIdentifier("pos")), ts.createToken(ts.SyntaxKind.PlusToken), ts.createIdentifier("len"))),
ts.createVariableDeclaration(ts.createIdentifier("key"), ts.createUnionTypeNode([
ts.createTypeOperatorNode(ts.createIndexedAccessTypeNode(ts.createTypeReferenceNode(MessageInterface, undefined), ts.createLiteralTypeNode(ts.createStringLiteral(field.localName)))),
ts.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword)
]), undefined),
ts.createVariableDeclaration(ts.createIdentifier("val"), ts.createUnionTypeNode([
ts.createIndexedAccessTypeNode(ts.createIndexedAccessTypeNode(ts.createTypeReferenceNode(MessageInterface, undefined), ts.createLiteralTypeNode(ts.createStringLiteral(field.localName))), ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)),
ts.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword)
]), undefined)
], ts.NodeFlags.Let)));
// reader.string()
let readKeyExpression = this.makeReaderCall("reader", field.K, rt.LongType.STRING);
if (field.K === rt.ScalarType.BOOL) {
readKeyExpression = ts.createCall(ts.createPropertyAccess(readKeyExpression, ts.createIdentifier("toString")), undefined, []);
}
// reader.bytes()
let readValueExpression;
switch (field.V.kind) {
case "scalar":
readValueExpression = this.makeReaderCall("reader", field.V.T, field.V.L);
break;
case "enum":
readValueExpression = this.makeReaderCall("reader", rt.ScalarType.INT32);
break;
case "message":
let valueMessageDescriptor = this.registry.resolveTypeName(field.V.T().typeName);
runtime_1.assert(plugin_framework_1.DescriptorProto.is(valueMessageDescriptor));
readValueExpression = ts.createCall(ts.createPropertyAccess(ts.createIdentifier(this.imports.type(source, valueMessageDescriptor)), ts.createIdentifier("internalBinaryRead")), undefined, [
ts.createIdentifier("reader"),
this.makeReaderCall("reader", rt.ScalarType.UINT32),
ts.createIdentifier("options")
]);
break;
}
// while (reader.pos < end) {
methodStatements.push(ts.createWhile(ts.createBinary(ts.createPropertyAccess(ts.createIdentifier("reader"), ts.createIdentifier("pos")), ts.createToken(ts.SyntaxKind.LessThanToken), ts.createIdentifier("end")), ts.createBlock([
// let [fieldNo, wireType] = reader.tag();
ts.createVariableStatement(undefined, ts.createVariableDeclarationList([ts.createVariableDeclaration(ts.createArrayBindingPattern([
ts.createBindingElement(undefined, undefined, ts.createIdentifier("fieldNo"), undefined),
ts.createBindingElement(undefined, undefined, ts.createIdentifier("wireType"), undefined)
]), undefined, ts.createCall(ts.createPropertyAccess(ts.createIdentifier("reader"), ts.createIdentifier("tag")), undefined, []))], ts.NodeFlags.Let)),
// switch (fieldNo) {
ts.createSwitch(ts.createIdentifier("fieldNo"), ts.createCaseBlock([
// case 1:
ts.createCaseClause(ts.createNumericLiteral("1"), [
// key = reader....
ts.createExpressionStatement(ts.createBinary(ts.createIdentifier("key"), ts.createToken(ts.SyntaxKind.EqualsToken), readKeyExpression)),
ts.createBreak(undefined)
]),
// case 2:
ts.createCaseClause(ts.createNumericLiteral("2"), [
// value = ...
ts.createExpressionStatement(ts.createBinary(ts.createIdentifier("val"), ts.createToken(ts.SyntaxKind.EqualsToken), readValueExpression)),
ts.createBreak(undefined)
]),
ts.createDefaultClause([ts.createThrow(ts.createNew(ts.createPropertyAccess(ts.createIdentifier("globalThis"), ts.createIdentifier("Error")), undefined, [ts.createStringLiteral("unknown map entry field for " + this.registry.formatQualifiedName(fieldDescriptor))]))])
]))
], true)));
// map[key ?? ""] = val ?? 0;
methodStatements.push(ts.createExpressionStatement(ts.createBinary(ts.createElementAccess(ts.createIdentifier("map"), ts.createBinary(ts.createIdentifier("key"), ts.createToken(ts.SyntaxKind.QuestionQuestionToken), this.createMapKeyDefaultValue(field.K))), ts.createToken(ts.SyntaxKind.EqualsToken), ts.createBinary(ts.createIdentifier("val"), ts.createToken(ts.SyntaxKind.QuestionQuestionToken), this.createMapValueDefaultValue(source, field.V)))));
// private binaryReadMapEntry<field no>(map: ExampleResponse["<field local name>"], reader: IBinaryReader, options: BinaryReadOptions): void
return ts.createMethod(undefined, [ts.createModifier(ts.SyntaxKind.PrivateKeyword)], undefined, ts.createIdentifier(methodName), undefined, undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("map"), undefined, ts.createIndexedAccessTypeNode(ts.createTypeReferenceNode(ts.createIdentifier(MessageInterface), undefined), ts.createLiteralTypeNode(ts.createStringLiteral(field.localName))), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("reader"), undefined, ts.createTypeReferenceNode(ts.createIdentifier(IBinaryReader), undefined), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("options"), undefined, ts.createTypeReferenceNode(ts.createIdentifier(BinaryReadOptions), undefined), undefined)
], ts.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword), ts.createBlock(methodStatements, true));
}
createMapKeyDefaultValue(type) {
let value = this.createScalarDefaultValue(type);
runtime_1.assert(value !== undefined);
// javascript object key must be number or string
// noinspection SuspiciousTypeOfGuard
if (typeof value !== "number") {
value = value.toString();
}
return plugin_framework_1.typescriptLiteralFromValue(value);
}
createMapValueDefaultValue(source, V) {
switch (V.kind) {
case "scalar":
return plugin_framework_1.typescriptLiteralFromValue(this.createScalarDefaultValue(V.T, V.L));
case "enum":
return plugin_framework_1.typescriptLiteralFromValue(this.createScalarDefaultValue(rt.ScalarType.INT32));
case "message":
let messageDescriptor = this.registry.resolveTypeName(V.T().typeName);
runtime_1.assert(plugin_framework_1.DescriptorProto.is(messageDescriptor));
let MessageInterface = this.imports.type(source, messageDescriptor);
return ts.createCall(ts.createPropertyAccess(ts.createIdentifier(MessageInterface), ts.createIdentifier("create")), undefined, []);
}
}
// noinspection JSMethodCanBeStatic
createScalarDefaultValue(type, longType) {
let syntheticType = new rt.MessageType("$synthetic.InternalBinaryRead", [{
no: 1, name: "syntheticField", localName: "syntheticField", kind: "scalar", T: type, L: longType
}]);
const value = syntheticType.create().syntheticField;
runtime_1.assert(value !== undefined);
return value;
}
// reader.int32().toString()
// reader.int32().toBigInt()
// reader.int32().toNumber()
makeReaderCall(readerExpressionOrName, type, longType) {
let readerMethodName = plugin_framework_1.StringFormat.formatScalarType(type);
let readerMethodProp = ts.createPropertyAccess(typeof readerExpressionOrName == "string" ? ts.createIdentifier(readerExpressionOrName) : readerExpressionOrName, ts.createIdentifier(readerMethodName));
let readerMethodCall = ts.createCall(readerMethodProp, undefined, []);
if (!interpreter_1.Interpreter.isLongValueType(type)) {
return readerMethodCall;
}
let convertMethodProp;
switch (longType !== null && longType !== void 0 ? longType : rt.LongType.STRING) {
case rt.LongType.STRING:
convertMethodProp = ts.createPropertyAccess(readerMethodCall, ts.createIdentifier('toString'));
break;
case rt.LongType.NUMBER:
convertMethodProp = ts.createPropertyAccess(readerMethodCall, ts.createIdentifier('toNumber'));
break;
case rt.LongType.BIGINT:
convertMethodProp = ts.createPropertyAccess(readerMethodCall, ts.createIdentifier('toBigInt'));
break;
}
return ts.createCall(convertMethodProp, undefined, []);
}
}
exports.InternalBinaryRead = InternalBinaryRead;

View file

@ -0,0 +1,63 @@
import * as ts from "typescript";
import { DescriptorProto, DescriptorRegistry, TypescriptFile, TypeScriptImports } from "@protobuf-ts/plugin-framework";
import * as rt from "@protobuf-ts/runtime";
import { CustomMethodGenerator } from "../code-gen/message-type-generator";
import { Interpreter } from "../interpreter";
/**
* Generates the `internalBinaryWrite` method, which writes a message
* in binary format.
*
* Heads up: The generated code is only very marginally faster than
* the reflection-based one. The gain is less than 3%.
*
*/
export declare class InternalBinaryWrite implements CustomMethodGenerator {
private readonly registry;
private readonly imports;
private readonly interpreter;
private readonly options;
constructor(registry: DescriptorRegistry, imports: TypeScriptImports, interpreter: Interpreter, options: {
oneofKindDiscriminator: string;
runtimeImportPath: string;
});
make(source: TypescriptFile, descriptor: DescriptorProto): ts.MethodDeclaration[];
makeMethod(source: TypescriptFile, descriptor: DescriptorProto, bodyStatements: readonly ts.Statement[]): ts.MethodDeclaration;
makeUnknownFieldsHandler(source: TypescriptFile): ts.Statement[];
makeStatementsForEveryField(source: TypescriptFile, descriptor: DescriptorProto): ts.Statement[];
scalar(source: TypescriptFile, field: rt.FieldInfo & {
kind: "scalar" | "enum";
oneof: undefined;
repeat: undefined | rt.RepeatType.NO;
}, fieldPropertyAccess: ts.PropertyAccessExpression, fieldDeclarationComment: string): ts.Statement[];
scalarRepeated(source: TypescriptFile, field: rt.FieldInfo & {
kind: "scalar" | "enum";
oneof: undefined;
repeat: rt.RepeatType.PACKED | rt.RepeatType.UNPACKED;
}, fieldPropertyAccess: ts.PropertyAccessExpression, fieldDeclarationComment: string): ts.Statement[];
scalarOneof(source: TypescriptFile, field: rt.FieldInfo & {
kind: "scalar" | "enum";
oneof: string;
repeat: undefined | rt.RepeatType.NO;
}, fieldDeclarationComment: string): ts.Statement[];
message(source: TypescriptFile, field: rt.FieldInfo & {
kind: "message";
repeat: undefined | rt.RepeatType.NO;
oneof: undefined;
}, fieldPropertyAccess: ts.PropertyAccessExpression, fieldDeclarationComment: string): ts.Statement[];
messageRepeated(source: TypescriptFile, field: rt.FieldInfo & {
kind: "message";
repeat: rt.RepeatType.PACKED | rt.RepeatType.UNPACKED;
oneof: undefined;
}, fieldPropertyAccess: ts.PropertyAccessExpression, fieldDeclarationComment: string): ts.Statement[];
messageOneof(source: TypescriptFile, field: rt.FieldInfo & {
kind: "message";
repeat: undefined | rt.RepeatType.NO;
oneof: string;
}, fieldDeclarationComment: string): ts.Statement[];
map(source: TypescriptFile, field: rt.FieldInfo & {
kind: "map";
}, fieldPropertyAccess: ts.PropertyAccessExpression, fieldDeclarationComment: string): ts.Statement[];
protected makeWriterCall(writerExpressionOrName: string | ts.Expression, type: rt.ScalarType | 'fork' | 'join', argument?: ts.Expression): ts.Expression;
protected makeWriterTagCall(source: TypescriptFile, writerExpressionOrName: string | ts.Expression, fieldNo: number, wireType: rt.WireType): ts.Expression;
protected wireTypeForSingleScalar(scalarType: rt.ScalarType): rt.WireType;
}

View file

@ -0,0 +1,347 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.InternalBinaryWrite = void 0;
const ts = require("typescript");
const plugin_framework_1 = require("@protobuf-ts/plugin-framework");
const rt = require("@protobuf-ts/runtime");
const runtime_1 = require("@protobuf-ts/runtime");
/**
* Generates the `internalBinaryWrite` method, which writes a message
* in binary format.
*
* Heads up: The generated code is only very marginally faster than
* the reflection-based one. The gain is less than 3%.
*
*/
class InternalBinaryWrite {
constructor(registry, imports, interpreter, options) {
this.registry = registry;
this.imports = imports;
this.interpreter = interpreter;
this.options = options;
}
make(source, descriptor) {
// internalBinaryWrite(message: ScalarValuesMessage, writer: IBinaryWriter, options: BinaryWriteOptions): void {
let internalBinaryWrite = this.makeMethod(source, descriptor, [
...this.makeStatementsForEveryField(source, descriptor),
...this.makeUnknownFieldsHandler(source),
// return writer;
ts.createReturn(ts.createIdentifier("writer"))
]);
return [internalBinaryWrite];
}
makeMethod(source, descriptor, bodyStatements) {
const MessageInterface = this.imports.type(source, descriptor), IBinaryWriter = this.imports.name(source, 'IBinaryWriter', this.options.runtimeImportPath, true), BinaryWriteOptions = this.imports.name(source, 'BinaryWriteOptions', this.options.runtimeImportPath, true);
return ts.createMethod(undefined, undefined, undefined, ts.createIdentifier("internalBinaryWrite"), undefined, undefined, [
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("message"), undefined, ts.createTypeReferenceNode(MessageInterface, undefined), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("writer"), undefined, ts.createTypeReferenceNode(IBinaryWriter, undefined), undefined),
ts.createParameter(undefined, undefined, undefined, ts.createIdentifier("options"), undefined, ts.createTypeReferenceNode(BinaryWriteOptions, undefined), undefined),
], ts.createTypeReferenceNode(IBinaryWriter, undefined), ts.createBlock(bodyStatements, true));
}
makeUnknownFieldsHandler(source) {
let UnknownFieldHandler = this.imports.name(source, 'UnknownFieldHandler', this.options.runtimeImportPath);
return [
ts.createVariableStatement(undefined, ts.createVariableDeclarationList([ts.createVariableDeclaration(ts.createIdentifier("u"), undefined, ts.createPropertyAccess(ts.createIdentifier("options"), ts.createIdentifier("writeUnknownFields")))], ts.NodeFlags.Let)),
ts.createIf(ts.createBinary(ts.createIdentifier("u"), ts.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken), ts.createFalse()), ts.createExpressionStatement(ts.createCall(ts.createParen(ts.createConditional(ts.createBinary(ts.createIdentifier("u"), ts.createToken(ts.SyntaxKind.EqualsEqualsToken), ts.createTrue()), ts.createPropertyAccess(ts.createIdentifier(UnknownFieldHandler), ts.createIdentifier("onWrite")), ts.createIdentifier("u"))), undefined, [
ts.createPropertyAccess(ts.createThis(), ts.createIdentifier("typeName")),
ts.createIdentifier("message"),
ts.createIdentifier("writer")
])), undefined)
];
}
makeStatementsForEveryField(source, descriptor) {
const interpreterType = this.interpreter.getMessageType(descriptor), statements = [];
for (let fieldInfo of interpreterType.fields) {
let fieldDescriptor = descriptor.field.find(fd => fd.number === fieldInfo.no);
runtime_1.assert(fieldDescriptor !== undefined);
let fieldDeclarationComment = this.registry.formatFieldDeclaration(fieldDescriptor);
let fieldPropertyAccess = ts.createPropertyAccess(ts.createIdentifier("message"), fieldInfo.localName);
switch (fieldInfo.kind) {
case "scalar":
case "enum":
if (fieldInfo.repeat) {
statements.push(...this.scalarRepeated(source, fieldInfo, fieldPropertyAccess, fieldDeclarationComment));
}
else if (fieldInfo.oneof !== undefined) {
statements.push(...this.scalarOneof(source, fieldInfo, fieldDeclarationComment));
}
else {
statements.push(...this.scalar(source, fieldInfo, fieldPropertyAccess, fieldDeclarationComment));
}
break;
case "message":
if (fieldInfo.repeat) {
statements.push(...this.messageRepeated(source, fieldInfo, fieldPropertyAccess, fieldDeclarationComment));
}
else if (fieldInfo.oneof !== undefined) {
statements.push(...this.messageOneof(source, fieldInfo, fieldDeclarationComment));
}
else {
statements.push(...this.message(source, fieldInfo, fieldPropertyAccess, fieldDeclarationComment));
}
break;
case "map":
statements.push(...this.map(source, fieldInfo, fieldPropertyAccess, fieldDeclarationComment));
break;
}
}
return statements;
}
scalar(source, field, fieldPropertyAccess, fieldDeclarationComment) {
let type = field.kind == "enum" ? rt.ScalarType.INT32 : field.T;
// we only write scalar fields if they have a non-default value
// this is the condition:
let shouldWriteCondition;
if (field.T === rt.ScalarType.BYTES && field.opt) {
// message.bytes !== undefined
shouldWriteCondition = ts.createBinary(fieldPropertyAccess, ts.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken), ts.createIdentifier("undefined"));
}
else if (field.T === rt.ScalarType.BYTES && !field.opt) {
// message.bytes.length
shouldWriteCondition = ts.createPropertyAccess(fieldPropertyAccess, ts.createIdentifier("length"));
}
else {
// message.field !== <default value>
// get a default value for the scalar field using the MessageType
let defaultValue = new rt.MessageType("$synthetic.InternalBinaryWrite", [field]).create()[field.localName];
let defaultValueExpression = plugin_framework_1.typescriptLiteralFromValue(defaultValue);
shouldWriteCondition = ts.createBinary(fieldPropertyAccess, ts.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken), defaultValueExpression);
}
// if ( <shouldWriteCondition> )
let statement = ts.createIf(shouldWriteCondition,
// writer.tag( <field no>, <wire type> ).string(message.stringField)
ts.createExpressionStatement(this.makeWriterCall(this.makeWriterTagCall(source, 'writer', field.no, this.wireTypeForSingleScalar(type)), type, fieldPropertyAccess)), undefined);
ts.addSyntheticLeadingComment(statement, ts.SyntaxKind.MultiLineCommentTrivia, ' ' + fieldDeclarationComment + ' ', true);
return [statement];
}
scalarRepeated(source, field, fieldPropertyAccess, fieldDeclarationComment) {
let statement;
let type = field.kind == "enum" ? rt.ScalarType.INT32 : field.T;
if (field.repeat === rt.RepeatType.PACKED) {
// if (message.int32Field.length) {
statement = ts.createIf(ts.createPropertyAccess(fieldPropertyAccess, ts.createIdentifier("length")), ts.createBlock([
// writer.tag(3, WireType.LengthDelimited).fork();
ts.createExpressionStatement(this.makeWriterCall(this.makeWriterTagCall(source, 'writer', field.no, rt.WireType.LengthDelimited), 'fork')),
// for (let i = 0; i < message.int32Field.length; i++)
ts.createFor(ts.createVariableDeclarationList([ts.createVariableDeclaration(ts.createIdentifier("i"), undefined, ts.createNumericLiteral("0"))], ts.NodeFlags.Let), ts.createBinary(ts.createIdentifier("i"), ts.createToken(ts.SyntaxKind.LessThanToken), ts.createPropertyAccess(fieldPropertyAccess, ts.createIdentifier("length"))), ts.createPostfix(ts.createIdentifier("i"), ts.SyntaxKind.PlusPlusToken),
// writer.int32(message.int32Field[i]);
ts.createExpressionStatement(this.makeWriterCall('writer', type, ts.createElementAccess(fieldPropertyAccess, ts.createIdentifier("i"))))),
// writer.join();
ts.createExpressionStatement(this.makeWriterCall('writer', 'join')),
], true), undefined);
}
else {
// never packed
// for (let i = 0; i < message.bytesField.length; i++)
statement = ts.createFor(ts.createVariableDeclarationList([ts.createVariableDeclaration(ts.createIdentifier("i"), undefined, ts.createNumericLiteral("0"))], ts.NodeFlags.Let), ts.createBinary(ts.createIdentifier("i"), ts.createToken(ts.SyntaxKind.LessThanToken), ts.createPropertyAccess(fieldPropertyAccess, ts.createIdentifier("length"))), ts.createPostfix(ts.createIdentifier("i"), ts.SyntaxKind.PlusPlusToken),
// writer.tag( <field number>, <wire type> ).bytes( message.bytesField[i] )
ts.createExpressionStatement(this.makeWriterCall(this.makeWriterTagCall(source, "writer", field.no, this.wireTypeForSingleScalar(type)), type, ts.createElementAccess(fieldPropertyAccess, ts.createIdentifier("i")))));
}
ts.addSyntheticLeadingComment(statement, ts.SyntaxKind.MultiLineCommentTrivia, ' ' + fieldDeclarationComment + ' ', true);
return [statement];
}
scalarOneof(source, field, fieldDeclarationComment) {
let type = field.kind == "enum" ? rt.ScalarType.INT32 : field.T;
let groupPropertyAccess = ts.createPropertyAccess(ts.createIdentifier("message"), ts.createIdentifier(field.oneof));
let statement = ts.createIf(
// if (message.result.oneofKind === 'value')
ts.createBinary(ts.createPropertyAccess(groupPropertyAccess, ts.createIdentifier(this.options.oneofKindDiscriminator)), ts.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), ts.createStringLiteral(field.localName)),
// writer.tag( <field no>, <wire type> ).string(message.stringField)
ts.createExpressionStatement(this.makeWriterCall(this.makeWriterTagCall(source, 'writer', field.no, this.wireTypeForSingleScalar(type)), type, ts.createPropertyAccess(groupPropertyAccess, field.localName))), undefined);
ts.addSyntheticLeadingComment(statement, ts.SyntaxKind.MultiLineCommentTrivia, ' ' + fieldDeclarationComment + ' ', true);
return [statement];
}
message(source, field, fieldPropertyAccess, fieldDeclarationComment) {
let messageDescriptor = this.registry.resolveTypeName(field.T().typeName);
runtime_1.assert(plugin_framework_1.DescriptorProto.is(messageDescriptor));
// writer.tag(<field no>, WireType.LengthDelimited).fork();
let writeTagAndFork = this.makeWriterCall(this.makeWriterTagCall(source, 'writer', field.no, rt.WireType.LengthDelimited), 'fork');
// MessageFieldMessage_TestMessage.internalBinaryWrite(message.messageField, <writeTagAndFork>, options);
let binaryWrite = ts.createCall(ts.createPropertyAccess(ts.createIdentifier(this.imports.type(source, messageDescriptor)), ts.createIdentifier("internalBinaryWrite")), undefined, [fieldPropertyAccess, writeTagAndFork, ts.createIdentifier("options")]);
// <...>.join()
let binaryWriteAndJoin = this.makeWriterCall(binaryWrite, 'join');
// if (message.messageField) {
let statement = ts.createIf(fieldPropertyAccess, ts.createExpressionStatement(binaryWriteAndJoin), undefined);
ts.addSyntheticLeadingComment(statement, ts.SyntaxKind.MultiLineCommentTrivia, ' ' + fieldDeclarationComment + ' ', true);
return [statement];
}
messageRepeated(source, field, fieldPropertyAccess, fieldDeclarationComment) {
let messageDescriptor = this.registry.resolveTypeName(field.T().typeName);
runtime_1.assert(plugin_framework_1.DescriptorProto.is(messageDescriptor));
// message.repeatedMessageField[i]
let fieldPropI = ts.createElementAccess(fieldPropertyAccess, ts.createIdentifier("i"));
// writer.tag(<field no>, WireType.LengthDelimited).fork();
let writeTagAndFork = this.makeWriterCall(this.makeWriterTagCall(source, 'writer', field.no, rt.WireType.LengthDelimited), 'fork');
// MessageFieldMessage_TestMessage.internalBinaryWrite(message.repeatedMessageField, <writeTagAndFork>, options);
let binaryWrite = ts.createCall(ts.createPropertyAccess(ts.createIdentifier(this.imports.type(source, messageDescriptor)), ts.createIdentifier("internalBinaryWrite")), undefined, [fieldPropI, writeTagAndFork, ts.createIdentifier("options")]);
// <...>.join()
let binaryWriteAndJoin = this.makeWriterCall(binaryWrite, 'join');
// for (let i = 0; i < message.repeatedMessageField.length; i++) {
let statement = ts.createFor(ts.createVariableDeclarationList([ts.createVariableDeclaration(ts.createIdentifier("i"), undefined, ts.createNumericLiteral("0"))], ts.NodeFlags.Let), ts.createBinary(ts.createIdentifier("i"), ts.createToken(ts.SyntaxKind.LessThanToken), ts.createPropertyAccess(fieldPropertyAccess, ts.createIdentifier("length"))), ts.createPostfix(ts.createIdentifier("i"), ts.SyntaxKind.PlusPlusToken), ts.createExpressionStatement(binaryWriteAndJoin));
ts.addSyntheticLeadingComment(statement, ts.SyntaxKind.MultiLineCommentTrivia, ' ' + fieldDeclarationComment + ' ', true);
return [statement];
}
messageOneof(source, field, fieldDeclarationComment) {
let messageDescriptor = this.registry.resolveTypeName(field.T().typeName);
runtime_1.assert(plugin_framework_1.DescriptorProto.is(messageDescriptor));
// message.<oneof name>
let groupPropertyAccess = ts.createPropertyAccess(ts.createIdentifier("message"), ts.createIdentifier(field.oneof));
// writer.tag(<field no>, WireType.LengthDelimited).fork();
let writeTagAndFork = this.makeWriterCall(this.makeWriterTagCall(source, 'writer', field.no, rt.WireType.LengthDelimited), 'fork');
// MessageFieldMessage_TestMessage.internalBinaryWrite(message.<groupPropertyAccess>.<fieldLocalName>, <writeTagAndFork>, options);
let binaryWrite = ts.createCall(ts.createPropertyAccess(ts.createIdentifier(this.imports.type(source, messageDescriptor)), ts.createIdentifier("internalBinaryWrite")), undefined, [
ts.createPropertyAccess(groupPropertyAccess, field.localName),
writeTagAndFork,
ts.createIdentifier("options")
]);
// <...>.join()
let binaryWriteAndJoin = this.makeWriterCall(binaryWrite, 'join');
// if (message.objects.oneofKind === 'a') {
let statement = ts.createIf(ts.createBinary(ts.createPropertyAccess(groupPropertyAccess, ts.createIdentifier(this.options.oneofKindDiscriminator)), ts.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), ts.createStringLiteral(field.localName)), ts.createExpressionStatement(binaryWriteAndJoin), undefined);
ts.addSyntheticLeadingComment(statement, ts.SyntaxKind.MultiLineCommentTrivia, ' ' + fieldDeclarationComment + ' ', true);
return [statement];
}
map(source, field, fieldPropertyAccess, fieldDeclarationComment) {
// all javascript property keys are strings, need to do some conversion for wire format
let mapEntryKeyRead;
let mapEntryValueRead = ts.createElementAccess(fieldPropertyAccess, ts.createIdentifier("k"));
switch (field.K) {
case rt.ScalarType.BOOL:
// parse bool for writer
mapEntryKeyRead = ts.createBinary(ts.createIdentifier("k"), ts.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), ts.createStringLiteral("true"));
break;
case rt.ScalarType.INT32:
case rt.ScalarType.SINT32:
case rt.ScalarType.UINT32:
case rt.ScalarType.FIXED32:
case rt.ScalarType.SFIXED32:
// parse int for writer
mapEntryKeyRead = ts.createCall(ts.createIdentifier("parseInt"), undefined, [ts.createIdentifier("k")]);
// convince compiler key works for index type
// message.int32KeyedMap[k as any]
// ^^^^^^^^^^
mapEntryValueRead = ts.createElementAccess(fieldPropertyAccess, ts.createAsExpression(ts.createIdentifier("k"), ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)));
break;
default:
// writer method accepts string for all other cases, no need to modify
mapEntryKeyRead = ts.createIdentifier("k");
break;
}
// loop body for every map entry. looks different for messages.
let forBody;
if (field.V.kind == "message") {
let messageDescriptor = this.registry.resolveTypeName(field.V.T().typeName);
runtime_1.assert(plugin_framework_1.DescriptorProto.is(messageDescriptor));
forBody = ts.createBlock([
// same as for scalar maps
ts.createExpressionStatement(this.makeWriterCall(
// this.makeWriterCall(
this.makeWriterTagCall(source, this.makeWriterCall(this.makeWriterTagCall(source, "writer", field.no, rt.WireType.LengthDelimited),
// .fork // start length delimited for the MapEntry
'fork'), 1, this.wireTypeForSingleScalar(field.K)),
// .string(message.strStrField[k]) // MapEntry key value
field.K, mapEntryKeyRead)),
//
ts.createExpressionStatement(this.makeWriterCall(this.makeWriterTagCall(source, 'writer', 2, rt.WireType.LengthDelimited),
// start length delimited for the message
'fork')),
// MessageMapMessage_MyItem.internalBinaryWrite(message.strMsgField[k], writer, options);
ts.createExpressionStatement(ts.createCall(ts.createPropertyAccess(ts.createIdentifier(this.imports.type(source, messageDescriptor)), ts.createIdentifier("internalBinaryWrite")), undefined, [
mapEntryValueRead,
ts.createIdentifier("writer"),
ts.createIdentifier("options")
])),
// end message and end map entry
ts.createExpressionStatement(this.makeWriterCall(this.makeWriterCall('writer', 'join'), 'join')),
], true);
}
else {
// handle enum as INT32
let mapEntryValueScalarType = field.V.kind == "enum" ? rt.ScalarType.INT32 : field.V.T;
// *rolleyes*
forBody = ts.createExpressionStatement(this.makeWriterCall(this.makeWriterCall(this.makeWriterTagCall(source, this.makeWriterCall(this.makeWriterTagCall(source, this.makeWriterCall(this.makeWriterTagCall(source, 'writer',
// tag for our field
field.no, rt.WireType.LengthDelimited),
// .fork // start length delimited for the MapEntry
'fork'),
// MapEntry key field tag
1, this.wireTypeForSingleScalar(field.K)),
// .string(message.strStrField[k]) // MapEntry key value
field.K, mapEntryKeyRead),
// MapEntry value field tag
2, this.wireTypeForSingleScalar(mapEntryValueScalarType)),
// .string(message.strStrField[k]) // MapEntry value value
mapEntryValueScalarType, mapEntryValueRead), 'join'));
}
// for (let k of Object.keys(message.strStrField))
let statement = ts.createForOf(undefined, ts.createVariableDeclarationList([ts.createVariableDeclaration(ts.createIdentifier("k"), undefined, undefined)], ts.NodeFlags.Let), ts.createCall(ts.createPropertyAccess(ts.createPropertyAccess(ts.createIdentifier("globalThis"), ts.createIdentifier("Object")), ts.createIdentifier("keys")), undefined, [fieldPropertyAccess]), forBody);
ts.addSyntheticLeadingComment(statement, ts.SyntaxKind.MultiLineCommentTrivia, ' ' + fieldDeclarationComment + ' ', true);
return [statement];
}
makeWriterCall(writerExpressionOrName, type, argument) {
let methodName = typeof type == "string" ? type : plugin_framework_1.StringFormat.formatScalarType(type);
let writerExpression = typeof writerExpressionOrName == "string" ? ts.createIdentifier(writerExpressionOrName) : writerExpressionOrName;
let methodProp = ts.createPropertyAccess(writerExpression, ts.createIdentifier(methodName));
return ts.createCall(methodProp, undefined, argument ? [argument] : undefined);
}
makeWriterTagCall(source, writerExpressionOrName, fieldNo, wireType) {
let writerExpression = typeof writerExpressionOrName == "string" ? ts.createIdentifier(writerExpressionOrName) : writerExpressionOrName;
let methodProp = ts.createPropertyAccess(writerExpression, ts.createIdentifier("tag"));
let wireTypeName;
switch (wireType) {
case rt.WireType.LengthDelimited:
wireTypeName = "LengthDelimited";
break;
case rt.WireType.Bit64:
wireTypeName = "Bit64";
break;
case rt.WireType.Bit32:
wireTypeName = "Bit32";
break;
case rt.WireType.Varint:
wireTypeName = "Varint";
break;
case rt.WireType.EndGroup:
wireTypeName = "EndGroup";
break;
case rt.WireType.StartGroup:
wireTypeName = "StartGroup";
break;
}
let wireTypeAccess = ts.createPropertyAccess(ts.createIdentifier(this.imports.name(source, 'WireType', this.options.runtimeImportPath)), wireTypeName);
return ts.createCall(methodProp, undefined, [
ts.createNumericLiteral(fieldNo.toString()),
wireTypeAccess
]);
}
wireTypeForSingleScalar(scalarType) {
let wireType;
switch (scalarType) {
case rt.ScalarType.BOOL:
case rt.ScalarType.INT32:
case rt.ScalarType.UINT32:
case rt.ScalarType.SINT32:
case rt.ScalarType.INT64:
case rt.ScalarType.UINT64:
case rt.ScalarType.SINT64:
wireType = rt.WireType.Varint;
break;
case rt.ScalarType.BYTES:
case rt.ScalarType.STRING:
wireType = rt.WireType.LengthDelimited;
break;
case rt.ScalarType.DOUBLE:
case rt.ScalarType.FIXED64:
case rt.ScalarType.SFIXED64:
wireType = rt.WireType.Bit64;
break;
case rt.ScalarType.FLOAT:
case rt.ScalarType.FIXED32:
case rt.ScalarType.SFIXED32:
wireType = rt.WireType.Bit32;
break;
}
return wireType;
}
}
exports.InternalBinaryWrite = InternalBinaryWrite;

View file

@ -0,0 +1,41 @@
import { DescriptorProto, ITypeNameLookup, TypescriptFile, TypeScriptImports } from "@protobuf-ts/plugin-framework";
import * as ts from "typescript";
import { LongType } from "@protobuf-ts/runtime";
import { CustomMethodGenerator } from "../code-gen/message-type-generator";
export declare class WellKnownTypes implements CustomMethodGenerator {
private readonly typeNameLookup;
private readonly imports;
private readonly options;
static readonly protoFilenames: string[];
constructor(typeNameLookup: ITypeNameLookup, imports: TypeScriptImports, options: {
normalLongType: LongType;
runtimeImportPath: string;
useProtoFieldName: boolean;
});
/**
* Create custom methods for the handlers of well known types.
*
* Well known types have a custom JSON representation and we
* also add some convenience methods, for example to convert a
* `google.protobuf.Timestamp` to a javascript Date.
*/
make(source: TypescriptFile, descriptor: DescriptorProto): ts.MethodDeclaration[];
['google.protobuf.Empty'](): void;
['google.protobuf.Any'](source: TypescriptFile, descriptor: DescriptorProto): string[];
['google.protobuf.Timestamp'](source: TypescriptFile, descriptor: DescriptorProto): string[];
['google.protobuf.Duration'](source: TypescriptFile, descriptor: DescriptorProto): string[];
['google.protobuf.FieldMask'](source: TypescriptFile, descriptor: DescriptorProto): string[];
['google.protobuf.Struct'](source: TypescriptFile, descriptor: DescriptorProto): string[];
['google.protobuf.Value'](source: TypescriptFile, descriptor: DescriptorProto): string[];
['google.protobuf.NullValue'](): void;
['google.protobuf.ListValue'](source: TypescriptFile, descriptor: DescriptorProto): string[];
['google.protobuf.BoolValue'](source: TypescriptFile, descriptor: DescriptorProto): string[];
['google.protobuf.StringValue'](source: TypescriptFile, descriptor: DescriptorProto): string[];
['google.protobuf.DoubleValue'](source: TypescriptFile, descriptor: DescriptorProto): string[];
['google.protobuf.FloatValue'](source: TypescriptFile, descriptor: DescriptorProto): string[];
['google.protobuf.Int32Value'](source: TypescriptFile, descriptor: DescriptorProto): string[];
['google.protobuf.UInt32Value'](source: TypescriptFile, descriptor: DescriptorProto): string[];
['google.protobuf.Int64Value'](source: TypescriptFile, descriptor: DescriptorProto): string[];
['google.protobuf.UInt64Value'](source: TypescriptFile, descriptor: DescriptorProto): string[];
['google.protobuf.BytesValue'](source: TypescriptFile, descriptor: DescriptorProto): string[];
}

View file

@ -0,0 +1,681 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WellKnownTypes = void 0;
const plugin_framework_1 = require("@protobuf-ts/plugin-framework");
const runtime_1 = require("@protobuf-ts/runtime");
const field_info_generator_1 = require("../code-gen/field-info-generator");
class WellKnownTypes {
constructor(typeNameLookup, imports, options) {
this.typeNameLookup = typeNameLookup;
this.imports = imports;
this.options = options;
}
/**
* Create custom methods for the handlers of well known types.
*
* Well known types have a custom JSON representation and we
* also add some convenience methods, for example to convert a
* `google.protobuf.Timestamp` to a javascript Date.
*/
make(source, descriptor) {
const typeName = this.typeNameLookup.makeTypeName(descriptor), fn = this[typeName];
if (fn) {
let r = fn.apply(this, [source, descriptor]);
if (typeof r == "string") {
return [plugin_framework_1.typescriptMethodFromText(r)];
}
if (Array.isArray(r)) {
return r.map(txt => plugin_framework_1.typescriptMethodFromText(txt));
}
}
return [];
}
['google.protobuf.Empty']( /*source: TypescriptFile, descriptor: DescriptorProto*/) {
// that´s ok, Empty already has the required JSON representation by default
}
['google.protobuf.Any'](source, descriptor) {
const Any = this.imports.type(source, descriptor), IMessageType = this.imports.name(source, 'IMessageType', this.options.runtimeImportPath, true), BinaryReadOptions = this.imports.name(source, 'BinaryReadOptions', this.options.runtimeImportPath, true), JsonWriteOptions = this.imports.name(source, 'JsonWriteOptions', this.options.runtimeImportPath, true), JsonReadOptions = this.imports.name(source, 'JsonReadOptions', this.options.runtimeImportPath, true), jsonWriteOptions = this.imports.name(source, 'jsonWriteOptions', this.options.runtimeImportPath), JsonValue = this.imports.name(source, 'JsonValue', this.options.runtimeImportPath, true), typeofJsonValue = this.imports.name(source, 'typeofJsonValue', this.options.runtimeImportPath), isJsonObject = this.imports.name(source, 'isJsonObject', this.options.runtimeImportPath), typeUrlField = field_info_generator_1.FieldInfoGenerator.createTypescriptLocalName('type_url', this.options);
return [
`
/**
* Pack the message into a new \`Any\`.
*
* Uses 'type.googleapis.com/full.type.name' as the type URL.
*/
function pack<T extends object>(message: T, type: ${IMessageType}<T>): ${Any} {
return {
${typeUrlField}: this.typeNameToUrl(type.typeName),
value: type.toBinary(message),
};
}
`, `
/**
* Unpack the message from the \`Any\`.
*/
function unpack<T extends object>(any: ${Any}, type: ${IMessageType}<T>, options?: Partial<${BinaryReadOptions}>): T {
if (!this.contains(any, type))
throw new Error("Cannot unpack google.protobuf.Any with ${typeUrlField} '" + any.${typeUrlField} + "' as " + type.typeName + ".");
return type.fromBinary(any.value, options);
}
`, `
/**
* Does the given \`Any\` contain a packed message of the given type?
*/
function contains(any: ${Any}, type: ${IMessageType}<any> | string): boolean {
if (!any.${typeUrlField}.length)
return false;
let wants = typeof type == "string" ? type : type.typeName;
let has = this.typeUrlToName(any.${typeUrlField});
return wants === has;
}
`, `
/**
* Convert the message to canonical JSON value.
*
* You have to provide the \`typeRegistry\` option so that the
* packed message can be converted to JSON.
*
* The \`typeRegistry\` option is also required to read
* \`google.protobuf.Any\` from JSON format.
*/
function internalJsonWrite(any: ${Any}, options: ${JsonWriteOptions}): ${JsonValue} {
if (any.${typeUrlField} === "")
return {};
let typeName = this.typeUrlToName(any.${typeUrlField});
let opt = ${jsonWriteOptions}(options);
let type = opt.typeRegistry?.find(t => t.typeName === typeName);
if (!type)
throw new globalThis.Error("Unable to convert google.protobuf.Any with typeUrl '" + any.${typeUrlField} + "' to JSON. The specified type " + typeName + " is not available in the type registry.");
let value = type.fromBinary(any.value, {readUnknownField: false});
let json = type.internalJsonWrite(value, opt);
if (typeName.startsWith("google.protobuf.") || !${isJsonObject}(json))
json = {value: json};
json['@type'] = any.${typeUrlField};
return json;
}
`, `
function internalJsonRead(json: ${JsonValue}, options: ${JsonReadOptions}, target?: ${Any}): ${Any} {
if (!${isJsonObject}(json))
throw new globalThis.Error("Unable to parse google.protobuf.Any from JSON " + ${typeofJsonValue}(json) + ".");
if (typeof json['@type'] != "string" || json['@type'] == '')
return this.create();
let typeName = this.typeUrlToName(json["@type"]);
let type = options?.typeRegistry?.find(t => t.typeName == typeName);
if (!type)
throw new globalThis.Error("Unable to parse google.protobuf.Any from JSON. The specified type " + typeName + " is not available in the type registry.");
let value;
if (typeName.startsWith('google.protobuf.') && json.hasOwnProperty('value'))
value = type.fromJson(json['value'], options);
else {
let copy = Object.assign({}, json);
delete copy['@type'];
value = type.fromJson(copy, options);
}
if (target === undefined)
target = this.create();
target.${typeUrlField} = json['@type'];
target.value = type.toBinary(value);
return target;
}
`, `
function typeNameToUrl(name: string): string {
if (!name.length)
throw new Error('invalid type name: ' + name);
return "type.googleapis.com/" + name;
}
`, `
function typeUrlToName(url: string): string {
if (!url.length)
throw new Error('invalid type url: ' + url);
let slash = url.lastIndexOf("/");
let name = slash > 0 ? url.substring(slash + 1) : url;
if (!name.length)
throw new Error('invalid type url: ' + url);
return name;
}
`
];
}
['google.protobuf.Timestamp'](source, descriptor) {
const Timestamp = this.imports.type(source, descriptor), PbLong = this.imports.name(source, 'PbLong', this.options.runtimeImportPath), JsonWriteOptions = this.imports.name(source, 'JsonWriteOptions', this.options.runtimeImportPath, true), JsonReadOptions = this.imports.name(source, 'JsonReadOptions', this.options.runtimeImportPath, true), JsonValue = this.imports.name(source, 'JsonValue', this.options.runtimeImportPath, true), typeofJsonValue = this.imports.name(source, 'typeofJsonValue', this.options.runtimeImportPath);
let longConvertMethod = 'toBigInt';
if (this.options.normalLongType === runtime_1.LongType.NUMBER)
longConvertMethod = 'toNumber';
else if (this.options.normalLongType === runtime_1.LongType.STRING)
longConvertMethod = 'toString';
return [
`
/**
* Creates a new \`Timestamp\` for the current time.
*/
function now(): ${Timestamp} {
const msg = this.create();
const ms = Date.now();
msg.seconds = ${PbLong}.from(Math.floor(ms / 1000)).${longConvertMethod}();
msg.nanos = (ms % 1000) * 1000000;
return msg;
}
`, `
/**
* Converts a \`Timestamp\` to a JavaScript Date.
*/
function toDate(message: ${Timestamp}): Date {
return new Date(${PbLong}.from(message.seconds).toNumber() * 1000 + Math.ceil(message.nanos / 1000000));
}
`, `
/**
* Converts a JavaScript Date to a \`Timestamp\`.
*/
function fromDate(date: Date): ${Timestamp} {
const msg = this.create();
const ms = date.getTime();
msg.seconds = PbLong.from(Math.floor(ms / 1000)).${longConvertMethod}();
msg.nanos = (ms % 1000) * 1000000;
return msg;
}
`, `
/**
* In JSON format, the \`Timestamp\` type is encoded as a string
* in the RFC 3339 format.
*/
function internalJsonWrite(message: ${Timestamp}, options: ${JsonWriteOptions}): ${JsonValue} {
let ms = PbLong.from(message.seconds).toNumber() * 1000;
if (ms < Date.parse("0001-01-01T00:00:00Z") || ms > Date.parse("9999-12-31T23:59:59Z"))
throw new Error("Unable to encode Timestamp to JSON. Must be from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive.");
if (message.nanos < 0)
throw new Error("Unable to encode invalid Timestamp to JSON. Nanos must not be negative.");
let z = "Z";
if (message.nanos > 0) {
let nanosStr = (message.nanos + 1000000000).toString().substring(1);
if (nanosStr.substring(3) === "000000")
z = "." + nanosStr.substring(0, 3) + "Z";
else if (nanosStr.substring(6) === "000")
z = "." + nanosStr.substring(0, 6) + "Z";
else
z = "." + nanosStr + "Z";
}
return new Date(ms).toISOString().replace(".000Z", z)
}
`, `
/**
* In JSON format, the \`Timestamp\` type is encoded as a string
* in the RFC 3339 format.
*/
function internalJsonRead(json: ${JsonValue}, options: ${JsonReadOptions}, target?: ${Timestamp}): ${Timestamp} {
if (typeof json !== "string")
throw new Error("Unable to parse Timestamp from JSON " + ${typeofJsonValue}(json) + ".");
// RFC 3339 with "Z" (UTC) or offset like "+08:00" and optional fractions
// match[7] optional fractions, 3 to 9 digits
// match[8] offset like "-08:00", if undefined date is UTC
let matches = json.match(/^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(?:Z|\\.([0-9]{3,9})Z|([+-][0-9][0-9]:[0-9][0-9]))$/);
if (!matches)
throw new Error("Unable to parse Timestamp from JSON. Invalid format.");
let ms = Date.parse(matches[1] + "-" + matches[2] + "-" + matches[3] + "T" + matches[4] + ":" + matches[5] + ":" + matches[6] + (matches[8] ? matches[8] : "Z"));
if (Number.isNaN(ms))
throw new Error("Unable to parse Timestamp from JSON. Invalid value.");
if (ms < Date.parse("0001-01-01T00:00:00Z") || ms > Date.parse("9999-12-31T23:59:59Z"))
throw new globalThis.Error("Unable to parse Timestamp from JSON. Must be from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive.");
if (!target)
target = this.create();
target.seconds = PbLong.from(ms / 1000).${longConvertMethod}();
target.nanos = 0;
if (matches[7])
target.nanos = (parseInt("1" + matches[7] + "0".repeat(9 - matches[7].length)) - 1000000000);
return target;
}
`,
];
}
['google.protobuf.Duration'](source, descriptor) {
const Duration = this.imports.type(source, descriptor), PbLong = this.imports.name(source, 'PbLong', this.options.runtimeImportPath), JsonWriteOptions = this.imports.name(source, 'JsonWriteOptions', this.options.runtimeImportPath, true), JsonReadOptions = this.imports.name(source, 'JsonReadOptions', this.options.runtimeImportPath, true), JsonValue = this.imports.name(source, 'JsonValue', this.options.runtimeImportPath, true), typeofJsonValue = this.imports.name(source, 'typeofJsonValue', this.options.runtimeImportPath);
let longConvertMethod = 'toBigInt';
if (this.options.normalLongType === runtime_1.LongType.NUMBER)
longConvertMethod = 'toNumber';
else if (this.options.normalLongType === runtime_1.LongType.STRING)
longConvertMethod = 'toString';
return [
`
/**
* Encode \`Duration\` to JSON string like "3.000001s".
*/
function internalJsonWrite(message: ${Duration}, options: ${JsonWriteOptions}): ${JsonValue} {
let s = ${PbLong}.from(message.seconds).toNumber();
if (s > 315576000000 || s < -315576000000)
throw new Error("Duration value out of range.");
let text = message.seconds.toString();
if (s === 0 && message.nanos < 0)
text = "-" + text;
if (message.nanos !== 0) {
let nanosStr = Math.abs(message.nanos).toString();
nanosStr = "0".repeat(9 - nanosStr.length) + nanosStr;
if (nanosStr.substring(3) === "000000")
nanosStr = nanosStr.substring(0, 3);
else if (nanosStr.substring(6) === "000")
nanosStr = nanosStr.substring(0, 6);
text += "." + nanosStr;
}
return text + "s";
}
`, `
/**
* Decode \`Duration\` from JSON string like "3.000001s"
*/
function internalJsonRead(json: ${JsonValue}, options: ${JsonReadOptions}, target?: ${Duration}): ${Duration} {
if (typeof json !== "string")
throw new Error("Unable to parse Duration from JSON " + ${typeofJsonValue}(json) + ". Expected string.");
let match = json.match(/^(-?)([0-9]+)(?:\\.([0-9]+))?s/);
if (match === null)
throw new Error("Unable to parse Duration from JSON string. Invalid format.");
if (!target)
target = this.create();
let [, sign, secs, nanos] = match;
let longSeconds = ${PbLong}.from(sign + secs);
if (longSeconds.toNumber() > 315576000000 || longSeconds.toNumber() < -315576000000)
throw new Error("Unable to parse Duration from JSON string. Value out of range.");
target.seconds = longSeconds.${longConvertMethod}();
if (typeof nanos == "string") {
let nanosStr = sign + nanos + "0".repeat(9 - nanos.length);
target.nanos = parseInt(nanosStr);
}
return target;
}
`,
];
}
['google.protobuf.FieldMask'](source, descriptor) {
const FieldMask = this.imports.type(source, descriptor), JsonWriteOptions = this.imports.name(source, 'JsonWriteOptions', this.options.runtimeImportPath, true), JsonReadOptions = this.imports.name(source, 'JsonReadOptions', this.options.runtimeImportPath, true), lowerCamelCase = this.imports.name(source, 'lowerCamelCase', this.options.runtimeImportPath), JsonValue = this.imports.name(source, 'JsonValue', this.options.runtimeImportPath, true), typeofJsonValue = this.imports.name(source, 'typeofJsonValue', this.options.runtimeImportPath);
return [
`
/**
* Encode \`${descriptor.name}\` to JSON object.
*/
function internalJsonWrite(message: ${FieldMask}, options: ${JsonWriteOptions}): ${JsonValue} {
const invalidFieldMaskJsonRegex = /[A-Z]|(_([.0-9_]|$))/g;
return message.paths.map(p => {
if (invalidFieldMaskJsonRegex.test(p))
throw new Error("Unable to encode FieldMask to JSON. lowerCamelCase of path name \\""+p+"\\" is irreversible.");
return ${lowerCamelCase}(p);
}).join(",");
}
`, `
/**
* Decode \`${descriptor.name}\` from JSON object.
*/
function internalJsonRead(json: ${JsonValue}, options: ${JsonReadOptions}, target?: ${FieldMask}): ${FieldMask} {
if (typeof json !== "string")
throw new Error("Unable to parse FieldMask from JSON " + ${typeofJsonValue}(json) + ". Expected string.");
if (!target)
target = this.create();
if (json === "")
return target;
let camelToSnake = (str: string) => {
if (str.includes('_'))
throw new Error("Unable to parse FieldMask from JSON. Path names must be lowerCamelCase.");
let sc = str.replace(/[A-Z]/g, letter => "_" + letter.toLowerCase());
return sc;
};
target.paths = json.split(",").map(camelToSnake);
return target;
}
`,
];
}
['google.protobuf.Struct'](source, descriptor) {
const Struct = this.imports.type(source, descriptor), JsonObject = this.imports.name(source, 'JsonObject', this.options.runtimeImportPath, true), JsonWriteOptions = this.imports.name(source, 'JsonWriteOptions', this.options.runtimeImportPath, true), JsonReadOptions = this.imports.name(source, 'JsonReadOptions', this.options.runtimeImportPath, true), JsonValue = this.imports.name(source, 'JsonValue', this.options.runtimeImportPath, true), typeofJsonValue = this.imports.name(source, 'typeofJsonValue', this.options.runtimeImportPath), isJsonObject = this.imports.name(source, 'isJsonObject', this.options.runtimeImportPath);
return [
`
/**
* Encode \`${descriptor.name}\` to JSON object.
*/
function internalJsonWrite(message: ${Struct}, options: ${JsonWriteOptions}): ${JsonValue} {
let json: ${JsonObject} = {};
for (let [k, v] of Object.entries(message.fields)) {
json[k] = Value.toJson(v);
}
return json;
}
`, `
/**
* Decode \`${descriptor.name}\` from JSON object.
*/
function internalJsonRead(json: ${JsonValue}, options: ${JsonReadOptions}, target?: ${Struct}): ${Struct} {
if (!${isJsonObject}(json))
throw new globalThis.Error("Unable to parse message " + this.typeName + " from JSON " + ${typeofJsonValue}(json) + ".");
if (!target)
target = this.create();
for (let [k, v] of globalThis.Object.entries(json)) {
target.fields[k] = Value.fromJson(v);
}
return target;
}
`,
];
}
['google.protobuf.Value'](source, descriptor) {
const Value = this.imports.type(source, descriptor), JsonWriteOptions = this.imports.name(source, 'JsonWriteOptions', this.options.runtimeImportPath, true), JsonReadOptions = this.imports.name(source, 'JsonReadOptions', this.options.runtimeImportPath, true), JsonValue = this.imports.name(source, 'JsonValue', this.options.runtimeImportPath, true), typeofJsonValue = this.imports.name(source, 'typeofJsonValue', this.options.runtimeImportPath), nullValueField = field_info_generator_1.FieldInfoGenerator.createTypescriptLocalName('null_value', this.options), boolValueField = field_info_generator_1.FieldInfoGenerator.createTypescriptLocalName('bool_value', this.options), numberValueField = field_info_generator_1.FieldInfoGenerator.createTypescriptLocalName('number_value', this.options), stringValueField = field_info_generator_1.FieldInfoGenerator.createTypescriptLocalName('string_value', this.options), listValueField = field_info_generator_1.FieldInfoGenerator.createTypescriptLocalName('list_value', this.options), structValueField = field_info_generator_1.FieldInfoGenerator.createTypescriptLocalName('struct_value', this.options);
return [
`
/**
* Encode \`${descriptor.name}\` to JSON value.
*/
function internalJsonWrite(message: ${Value}, options: ${JsonWriteOptions}): ${JsonValue} {
if (message.kind.oneofKind === undefined) throw new globalThis.Error();
switch (message.kind.oneofKind) {
case undefined:
throw new globalThis.Error();
case "${boolValueField}":
return message.kind.${boolValueField};
case "${nullValueField}":
return null;
case "${numberValueField}":
let numberValue = message.kind.${numberValueField};
if (typeof numberValue == "number" && !Number.isFinite(numberValue)) throw new globalThis.Error();
return numberValue;
case "${stringValueField}":
return message.kind.${stringValueField};
case "${listValueField}":
let listValueField = this.fields.find(f => f.no === 6);
if (listValueField?.kind !== 'message') throw new globalThis.Error();
return listValueField.T().toJson(message.kind.${listValueField});
case "${structValueField}":
let structValueField = this.fields.find(f => f.no === 5);
if (structValueField?.kind !== 'message') throw new globalThis.Error();
return structValueField.T().toJson(message.kind.${structValueField});
}
}
`, `
/**
* Decode \`${descriptor.name}\` from JSON value.
*/
function internalJsonRead(json: ${JsonValue}, options: ${JsonReadOptions}, target?: ${Value}): ${Value} {
if (!target)
target = this.create();
switch (typeof json) {
case "number":
target.kind = {oneofKind: "${numberValueField}", ${numberValueField}: json};
break;
case "string":
target.kind = {oneofKind: "${stringValueField}", ${stringValueField}: json};
break;
case "boolean":
target.kind = {oneofKind: "${boolValueField}", ${boolValueField}: json};
break;
case "object":
if (json === null) {
target.kind = {oneofKind: "${nullValueField}", ${nullValueField}: NullValue.NULL_VALUE};
} else if (globalThis.Array.isArray(json)) {
target.kind = {oneofKind: "${listValueField}", ${listValueField}: ListValue.fromJson(json)};
} else {
target.kind = {oneofKind: "${structValueField}", ${structValueField}: Struct.fromJson(json)};
}
break;
default:
throw new globalThis.Error('Unable to parse ' + this.typeName + ' from JSON ' + ${typeofJsonValue}(json));
}
return target;
}
`,
];
}
['google.protobuf.NullValue']( /*source: TypescriptFile, descriptor: DescriptorProto*/) {
// that´s ok, NullValue is actually an enum, and special JSON representation is handled by the reflection json reader
}
['google.protobuf.ListValue'](source, descriptor) {
const ListValue = this.imports.type(source, descriptor), JsonWriteOptions = this.imports.name(source, 'JsonWriteOptions', this.options.runtimeImportPath, true), JsonReadOptions = this.imports.name(source, 'JsonReadOptions', this.options.runtimeImportPath, true), JsonValue = this.imports.name(source, 'JsonValue', this.options.runtimeImportPath, true), typeofJsonValue = this.imports.name(source, 'typeofJsonValue', this.options.runtimeImportPath);
return [
`
/**
* Encode \`${descriptor.name}\` to JSON array.
*/
function internalJsonWrite(message: ${ListValue}, options: ${JsonWriteOptions}): ${JsonValue} {
return message.values.map(v => Value.toJson(v));
}
`, `
/**
* Decode \`${descriptor.name}\` from JSON array.
*/
function internalJsonRead(json: ${JsonValue}, options: ${JsonReadOptions}, target?: ${ListValue}): ${ListValue} {
if (! globalThis.Array.isArray(json)) throw new globalThis.Error('Unable to parse ' + this.typeName + ' from JSON ' + ${typeofJsonValue}(json));
if (!target)
target = this.create();
let values = json.map(v => Value.fromJson(v));
target.values.push(...values);
return target;
}
`,
];
}
['google.protobuf.BoolValue'](source, descriptor) {
const BoolValue = this.imports.type(source, descriptor), JsonWriteOptions = this.imports.name(source, 'JsonWriteOptions', this.options.runtimeImportPath, true), JsonReadOptions = this.imports.name(source, 'JsonReadOptions', this.options.runtimeImportPath, true), JsonValue = this.imports.name(source, 'JsonValue', this.options.runtimeImportPath, true);
return [
`
/**
* Encode \`${descriptor.name}\` to JSON bool.
*/
function internalJsonWrite(message: ${BoolValue}, options: ${JsonWriteOptions}): ${JsonValue} {
return message.value;
}
`, `
/**
* Decode \`${descriptor.name}\` from JSON bool.
*/
function internalJsonRead(json: ${JsonValue}, options: ${JsonReadOptions}, target?: ${BoolValue}): ${BoolValue} {
if (!target)
target = this.create();
target.value = this.refJsonReader.scalar(json, ${plugin_framework_1.FieldDescriptorProto_Type.BOOL}, undefined, "value") as boolean;
return target;
}
`,
];
}
['google.protobuf.StringValue'](source, descriptor) {
const StringValue = this.imports.type(source, descriptor), JsonWriteOptions = this.imports.name(source, 'JsonWriteOptions', this.options.runtimeImportPath, true), JsonReadOptions = this.imports.name(source, 'JsonReadOptions', this.options.runtimeImportPath, true), JsonValue = this.imports.name(source, 'JsonValue', this.options.runtimeImportPath, true);
return [
`
/**
* Encode \`${descriptor.name}\` to JSON string.
*/
function internalJsonWrite(message: ${StringValue}, options: ${JsonWriteOptions}): ${JsonValue} {
return message.value;
}
`, `
/**
* Decode \`${descriptor.name}\` from JSON string.
*/
function internalJsonRead(json: ${JsonValue}, options: ${JsonReadOptions}, target?: ${StringValue}): ${StringValue} {
if (!target)
target = this.create();
target.value = this.refJsonReader.scalar(json, ${plugin_framework_1.FieldDescriptorProto_Type.STRING}, undefined, "value") as string;
return target;
}
`,
];
}
['google.protobuf.DoubleValue'](source, descriptor) {
const DoubleValue = this.imports.type(source, descriptor), JsonWriteOptions = this.imports.name(source, 'JsonWriteOptions', this.options.runtimeImportPath, true), JsonReadOptions = this.imports.name(source, 'JsonReadOptions', this.options.runtimeImportPath, true), JsonValue = this.imports.name(source, 'JsonValue', this.options.runtimeImportPath, true);
return [
`
/**
* Encode \`${descriptor.name}\` to JSON number.
*/
function internalJsonWrite(message: ${DoubleValue}, options: ${JsonWriteOptions}): ${JsonValue} {
return this.refJsonWriter.scalar(${plugin_framework_1.FieldDescriptorProto_Type.FLOAT}, message.value, "value", false, true);
}
`, `
/**
* Decode \`${descriptor.name}\` from JSON number.
*/
function internalJsonRead(json: ${JsonValue}, options: ${JsonReadOptions}, target?: ${DoubleValue}): ${DoubleValue} {
if (!target)
target = this.create();
target.value = this.refJsonReader.scalar(json, ${plugin_framework_1.FieldDescriptorProto_Type.DOUBLE}, undefined, "value") as number;
return target;
}
`,
];
}
['google.protobuf.FloatValue'](source, descriptor) {
const FloatValue = this.imports.type(source, descriptor), JsonWriteOptions = this.imports.name(source, 'JsonWriteOptions', this.options.runtimeImportPath, true), JsonReadOptions = this.imports.name(source, 'JsonReadOptions', this.options.runtimeImportPath, true), JsonValue = this.imports.name(source, 'JsonValue', this.options.runtimeImportPath, true);
return [
`
/**
* Encode \`${descriptor.name}\` to JSON number.
*/
function internalJsonWrite(message: ${FloatValue}, options: ${JsonWriteOptions}): ${JsonValue} {
return this.refJsonWriter.scalar(${plugin_framework_1.FieldDescriptorProto_Type.DOUBLE}, message.value, "value", false, true);
}
`, `
/**
* Decode \`${descriptor.name}\` from JSON number.
*/
function internalJsonRead(json: ${JsonValue}, options: ${JsonReadOptions}, target?: ${FloatValue}): ${FloatValue} {
if (!target)
target = this.create();
target.value = this.refJsonReader.scalar(json, ${plugin_framework_1.FieldDescriptorProto_Type.DOUBLE}, undefined, "value") as number;
return target;
}
`,
];
}
['google.protobuf.Int32Value'](source, descriptor) {
const Int32Value = this.imports.type(source, descriptor), JsonWriteOptions = this.imports.name(source, 'JsonWriteOptions', this.options.runtimeImportPath, true), JsonReadOptions = this.imports.name(source, 'JsonReadOptions', this.options.runtimeImportPath, true), JsonValue = this.imports.name(source, 'JsonValue', this.options.runtimeImportPath, true);
return [
`
/**
* Encode \`${descriptor.name}\` to JSON string.
*/
function internalJsonWrite(message: ${Int32Value}, options: ${JsonWriteOptions}): ${JsonValue} {
return this.refJsonWriter.scalar(${plugin_framework_1.FieldDescriptorProto_Type.INT32}, message.value, "value", false, true);
}
`, `
/**
* Decode \`${descriptor.name}\` from JSON string.
*/
function internalJsonRead(json: ${JsonValue}, options: ${JsonReadOptions}, target?: ${Int32Value}): ${Int32Value} {
if (!target)
target = this.create();
target.value = this.refJsonReader.scalar(json, ${plugin_framework_1.FieldDescriptorProto_Type.INT32}, undefined, "value") as number;
return target;
}
`,
];
}
['google.protobuf.UInt32Value'](source, descriptor) {
const UInt32Value = this.imports.type(source, descriptor), JsonWriteOptions = this.imports.name(source, 'JsonWriteOptions', this.options.runtimeImportPath, true), JsonReadOptions = this.imports.name(source, 'JsonReadOptions', this.options.runtimeImportPath, true), JsonValue = this.imports.name(source, 'JsonValue', this.options.runtimeImportPath, true);
return [
`
/**
* Encode \`${descriptor.name}\` to JSON string.
*/
function internalJsonWrite(message: ${UInt32Value}, options: ${JsonWriteOptions}): ${JsonValue} {
return this.refJsonWriter.scalar(${plugin_framework_1.FieldDescriptorProto_Type.UINT32}, message.value, "value", false, true);
}
`, `
/**
* Decode \`${descriptor.name}\` from JSON string.
*/
function internalJsonRead(json: ${JsonValue}, options: ${JsonReadOptions}, target?: ${UInt32Value}): ${UInt32Value} {
if (!target)
target = this.create();
target.value = this.refJsonReader.scalar(json, ${plugin_framework_1.FieldDescriptorProto_Type.UINT32}, undefined, "value") as number;
return target;
}
`,
];
}
['google.protobuf.Int64Value'](source, descriptor) {
const Int64Value = this.imports.type(source, descriptor), iLongType = this.imports.name(source, 'LongType', this.options.runtimeImportPath), JsonWriteOptions = this.imports.name(source, 'JsonWriteOptions', this.options.runtimeImportPath, true), JsonReadOptions = this.imports.name(source, 'JsonReadOptions', this.options.runtimeImportPath, true), JsonValue = this.imports.name(source, 'JsonValue', this.options.runtimeImportPath, true), ScalarType = this.imports.name(source, 'ScalarType', this.options.runtimeImportPath);
let longTypeEnumValue = 'BIGINT';
if (this.options.normalLongType === runtime_1.LongType.NUMBER)
longTypeEnumValue = 'NUMBER';
else if (this.options.normalLongType === runtime_1.LongType.STRING)
longTypeEnumValue = 'STRING';
return [
`
/**
* Encode \`${descriptor.name}\` to JSON string.
*/
function internalJsonWrite(message: ${Int64Value}, options: ${JsonWriteOptions}): ${JsonValue} {
return this.refJsonWriter.scalar(${ScalarType}.INT64, message.value, "value", false, true);
}
`, `
/**
* Decode \`${descriptor.name}\` from JSON string.
*/
function internalJsonRead(json: ${JsonValue}, options: ${JsonReadOptions}, target?: ${Int64Value}): ${Int64Value} {
if (!target)
target = this.create();
target.value = this.refJsonReader.scalar(json, ${ScalarType}.INT64, ${iLongType}.${longTypeEnumValue}, "value") as any;
return target;
}
`,
];
}
['google.protobuf.UInt64Value'](source, descriptor) {
const UInt64Value = this.imports.type(source, descriptor), iLongType = this.imports.name(source, 'LongType', this.options.runtimeImportPath), JsonWriteOptions = this.imports.name(source, 'JsonWriteOptions', this.options.runtimeImportPath, true), JsonReadOptions = this.imports.name(source, 'JsonReadOptions', this.options.runtimeImportPath, true), JsonValue = this.imports.name(source, 'JsonValue', this.options.runtimeImportPath, true), ScalarType = this.imports.name(source, 'ScalarType', this.options.runtimeImportPath);
let longTypeEnumValue = 'BIGINT';
if (this.options.normalLongType === runtime_1.LongType.NUMBER)
longTypeEnumValue = 'NUMBER';
else if (this.options.normalLongType === runtime_1.LongType.STRING)
longTypeEnumValue = 'STRING';
return [
`
/**
* Encode \`${descriptor.name}\` to JSON string.
*/
function internalJsonWrite(message: ${UInt64Value}, options: ${JsonWriteOptions}): ${JsonValue} {
return this.refJsonWriter.scalar(${ScalarType}.UINT64, message.value, "value", false, true);
}
`, `
/**
* Decode \`${descriptor.name}\` from JSON string.
*/
function internalJsonRead(json: ${JsonValue}, options: ${JsonReadOptions}, target?: ${UInt64Value}): ${UInt64Value} {
if (!target)
target = this.create();
target.value = this.refJsonReader.scalar(json, ${ScalarType}.UINT64, ${iLongType}.${longTypeEnumValue}, "value") as any;
return target;
}
`,
];
}
['google.protobuf.BytesValue'](source, descriptor) {
const BytesValue = this.imports.type(source, descriptor), JsonWriteOptions = this.imports.name(source, 'JsonWriteOptions', this.options.runtimeImportPath, true), JsonReadOptions = this.imports.name(source, 'JsonReadOptions', this.options.runtimeImportPath, true), JsonValue = this.imports.name(source, 'JsonValue', this.options.runtimeImportPath, true);
return [
`
/**
* Encode \`${descriptor.name}\` to JSON string.
*/
function internalJsonWrite(message: ${BytesValue}, options: ${JsonWriteOptions}): ${JsonValue} {
return this.refJsonWriter.scalar(${plugin_framework_1.FieldDescriptorProto_Type.BYTES}, message.value, "value", false, true);
}
`, `
/**
* Decode \`${descriptor.name}\` from JSON string.
*/
function internalJsonRead(json: ${JsonValue}, options: ${JsonReadOptions}, target?: ${BytesValue}): ${BytesValue} {
if (!target)
target = this.create();
target.value = this.refJsonReader.scalar(json, ${plugin_framework_1.FieldDescriptorProto_Type.BYTES}, undefined, "value") as Uint8Array;
return target;
}
`,
];
}
}
exports.WellKnownTypes = WellKnownTypes;
WellKnownTypes.protoFilenames = [
"google/protobuf/any.proto",
"google/protobuf/api.proto",
"google/protobuf/descriptor.proto",
"google/protobuf/duration.proto",
"google/protobuf/empty.proto",
"google/protobuf/field_mask.proto",
"google/protobuf/source_context.proto",
"google/protobuf/struct.proto",
"google/protobuf/timestamp.proto",
"google/protobuf/type.proto",
"google/protobuf/wrappers.proto",
"google/protobuf/compiler/plugin.proto",
"google/protobuf/any.proto",
];

View file

@ -0,0 +1,159 @@
/**
* Custom file options interpreted by @protobuf-ts/plugin
*/
import * as rt from "@protobuf-ts/runtime";
import { FileDescriptorProto, FileOptions_OptimizeMode as OptimizeMode, IStringFormat, ServiceDescriptorProto } from "@protobuf-ts/plugin-framework";
import { Interpreter } from "./interpreter";
import * as ts from "typescript";
/**
* Custom file options interpreted by @protobuf-ts/plugin
* The extensions are declared in protobuf-ts.proto
*/
export interface OurFileOptions {
/**
* Exclude field or method options from being emitted in reflection data.
*
* For example, to stop the data of the "google.api.http" method option
* from being exported in the reflection information, set the following
* file option:
*
* ```proto
* option (ts.exclude_options) = "google.api.http";
* ```
*
* The option can be set multiple times.
* `*` serves as a wildcard and will greedily match anything.
*/
readonly ["ts.exclude_options"]: readonly string[];
}
/**
* Custom service options interpreted by @protobuf-ts/plugin
*/
export interface OurServiceOptions {
/**
* Generate a client for this service with this style.
* Can be set multiple times to generate several styles.
*/
readonly ["ts.client"]: ClientStyle[];
/**
* Generate a server for this service with this style.
* Can be set multiple times to generate several styles.
*/
readonly ["ts.server"]: ServerStyle[];
}
/**
* Read the custom file options declared in protobuf-ts.proto
*/
export declare function readOurFileOptions(file: FileDescriptorProto): OurFileOptions;
/**
* Read the custom service options declared in protobuf-ts.proto
*/
export declare function readOurServiceOptions(service: ServiceDescriptorProto): OurServiceOptions;
/**
* The available client styles from @protobuf-ts/plugin
* The extensions are declared in protobuf-ts.proto
*/
export declare enum ClientStyle {
/**
* Do not emit a client for this service.
*/
NO_CLIENT = 0,
/**
* Use the call implementations of @protobuf-ts/runtime-rpc.
* This is the default behaviour.
*/
GENERIC_CLIENT = 1,
/**
* Generate a client using @grpc/grpc-js (major version 1).
*/
GRPC1_CLIENT = 4
}
/**
* The available server styles from @protobuf-ts/plugin
* The extensions are declared in protobuf-ts.proto
*/
export declare enum ServerStyle {
/**
* Do not emit a server for this service.
* This is the default behaviour.
*/
NO_SERVER = 0,
/**
* Generate a generic server interface.
* Adapters be used to serve the service, for example @protobuf-ts/grpc-backend
* for gRPC.
*/
GENERIC_SERVER = 1,
/**
* Generate a server for @grpc/grpc-js (major version 1).
*/
GRPC1_SERVER = 2
}
/**
* Internal settings for the file generation.
*/
export interface InternalOptions {
readonly generateDependencies: boolean;
readonly pluginCredit?: string;
readonly normalLongType: rt.LongType;
readonly normalOptimizeMode: OptimizeMode;
readonly forcedOptimizeMode: OptimizeMode | undefined;
readonly normalServerStyle: ServerStyle;
readonly forcedServerStyle: ServerStyle | undefined;
readonly normalClientStyle: ClientStyle;
readonly forcedClientStyle: ClientStyle | undefined;
readonly synthesizeEnumZeroValue: string | false;
readonly oneofKindDiscriminator: string;
readonly runtimeRpcImportPath: string;
readonly runtimeImportPath: string;
readonly forceExcludeAllOptions: boolean;
readonly keepEnumPrefix: boolean;
readonly useProtoFieldName: boolean;
readonly tsNoCheck: boolean;
readonly esLintDisable: boolean;
readonly transpileTarget: ts.ScriptTarget | undefined;
readonly transpileModule: ts.ModuleKind;
readonly forceDisableServices: boolean;
readonly addPbSuffix: boolean;
}
export declare function makeInternalOptions(params?: {
generate_dependencies: boolean;
long_type_string: boolean;
long_type_number: boolean;
force_exclude_all_options: boolean;
keep_enum_prefix: boolean;
use_proto_field_name: boolean;
ts_nocheck: boolean;
eslint_disable: boolean;
force_optimize_code_size: boolean;
force_optimize_speed: boolean;
optimize_code_size: boolean;
force_server_none: boolean;
server_none: boolean;
server_generic: boolean;
server_grpc1: boolean;
force_client_none: boolean;
client_generic: boolean;
client_none: boolean;
client_grpc1: boolean;
add_pb_suffix: boolean;
force_disable_services: boolean;
output_typescript: boolean;
output_javascript: boolean;
output_javascript_es2015: boolean;
output_javascript_es2016: boolean;
output_javascript_es2017: boolean;
output_javascript_es2018: boolean;
output_javascript_es2019: boolean;
output_javascript_es2020: boolean;
output_legacy_commonjs: boolean;
}, pluginCredit?: string): InternalOptions;
export declare class OptionResolver {
private readonly interpreter;
private readonly stringFormat;
private readonly options;
constructor(interpreter: Interpreter, stringFormat: IStringFormat, options: InternalOptions);
getOptimizeMode(file: FileDescriptorProto): OptimizeMode;
getClientStyles(descriptor: ServiceDescriptorProto): ClientStyle[];
getServerStyles(descriptor: ServiceDescriptorProto): ServerStyle[];
}

View file

@ -0,0 +1,276 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.OptionResolver = exports.makeInternalOptions = exports.ServerStyle = exports.ClientStyle = exports.readOurServiceOptions = exports.readOurFileOptions = void 0;
/**
* Custom file options interpreted by @protobuf-ts/plugin
*/
const rt = require("@protobuf-ts/runtime");
const plugin_framework_1 = require("@protobuf-ts/plugin-framework");
const ts = require("typescript");
/**
* Read the custom file options declared in protobuf-ts.proto
*/
function readOurFileOptions(file) {
return read(file.options, emptyFileOptions, OurFileOptions);
}
exports.readOurFileOptions = readOurFileOptions;
/**
* Read the custom service options declared in protobuf-ts.proto
*/
function readOurServiceOptions(service) {
return read(service.options, emptyServiceOptions, OurServiceOptions);
}
exports.readOurServiceOptions = readOurServiceOptions;
function read(options, defaults, type) {
if (!options) {
return defaults;
}
let unknownFields = rt.UnknownFieldHandler.list(options);
if (!unknownFields.length) {
return defaults;
}
// concat all unknown field data
let unknownWriter = new rt.BinaryWriter();
for (let { no, wireType, data } of unknownFields)
unknownWriter.tag(no, wireType).raw(data);
let unknownBytes = unknownWriter.finish();
return type.fromBinary(unknownBytes, { readUnknownField: false });
}
const OurFileOptions = new rt.MessageType("$synthetic.OurFileOptions", [
{
no: 777701,
name: "ts.exclude_options", localName: "ts.exclude_options", jsonName: "ts.exclude_options",
kind: "scalar",
T: rt.ScalarType.STRING,
repeat: rt.RepeatType.PACKED
}
]);
const OurServiceOptions = new rt.MessageType("$synthetic.OurServiceOptions", [
{
no: 777701,
name: "ts.client", localName: "ts.client", jsonName: "ts.client",
kind: "enum",
T: () => ["ts.ClientStyle", ClientStyle],
repeat: rt.RepeatType.UNPACKED,
},
{
no: 777702,
name: "ts.server", localName: "ts.server", jsonName: "ts.server",
kind: "enum",
T: () => ["ts.ServerStyle", ServerStyle],
repeat: rt.RepeatType.UNPACKED,
}
]);
/**
* The available client styles from @protobuf-ts/plugin
* The extensions are declared in protobuf-ts.proto
*/
var ClientStyle;
(function (ClientStyle) {
/**
* Do not emit a client for this service.
*/
ClientStyle[ClientStyle["NO_CLIENT"] = 0] = "NO_CLIENT";
/**
* Use the call implementations of @protobuf-ts/runtime-rpc.
* This is the default behaviour.
*/
ClientStyle[ClientStyle["GENERIC_CLIENT"] = 1] = "GENERIC_CLIENT";
/**
* Generate a client using @grpc/grpc-js (major version 1).
*/
ClientStyle[ClientStyle["GRPC1_CLIENT"] = 4] = "GRPC1_CLIENT";
})(ClientStyle = exports.ClientStyle || (exports.ClientStyle = {}));
/**
* The available server styles from @protobuf-ts/plugin
* The extensions are declared in protobuf-ts.proto
*/
var ServerStyle;
(function (ServerStyle) {
/**
* Do not emit a server for this service.
* This is the default behaviour.
*/
ServerStyle[ServerStyle["NO_SERVER"] = 0] = "NO_SERVER";
/**
* Generate a generic server interface.
* Adapters be used to serve the service, for example @protobuf-ts/grpc-backend
* for gRPC.
*/
ServerStyle[ServerStyle["GENERIC_SERVER"] = 1] = "GENERIC_SERVER";
/**
* Generate a server for @grpc/grpc-js (major version 1).
*/
ServerStyle[ServerStyle["GRPC1_SERVER"] = 2] = "GRPC1_SERVER";
})(ServerStyle = exports.ServerStyle || (exports.ServerStyle = {}));
const emptyFileOptions = OurFileOptions.create();
const emptyServiceOptions = OurServiceOptions.create();
function makeInternalOptions(params, pluginCredit) {
const o = Object.assign({}, {
generateDependencies: false,
normalLongType: rt.LongType.BIGINT,
normalOptimizeMode: plugin_framework_1.FileOptions_OptimizeMode.SPEED,
forcedOptimizeMode: undefined,
normalClientStyle: ClientStyle.GENERIC_CLIENT,
forcedClientStyle: undefined,
normalServerStyle: ServerStyle.NO_SERVER,
forcedServerStyle: undefined,
synthesizeEnumZeroValue: 'UNSPECIFIED$',
oneofKindDiscriminator: 'oneofKind',
runtimeRpcImportPath: '@protobuf-ts/runtime-rpc',
runtimeImportPath: '@protobuf-ts/runtime',
forceExcludeAllOptions: false,
keepEnumPrefix: false,
useProtoFieldName: false,
tsNoCheck: false,
esLintDisable: false,
transpileTarget: undefined,
transpileModule: ts.ModuleKind.ES2015,
forceDisableServices: false,
addPbSuffix: false,
});
if (pluginCredit) {
o.pluginCredit = pluginCredit;
}
if (params === null || params === void 0 ? void 0 : params.generate_dependencies) {
o.generateDependencies = true;
}
if (params === null || params === void 0 ? void 0 : params.force_exclude_all_options) {
o.forceExcludeAllOptions = true;
}
if (params === null || params === void 0 ? void 0 : params.keep_enum_prefix) {
o.keepEnumPrefix = true;
}
if (params === null || params === void 0 ? void 0 : params.use_proto_field_name) {
o.useProtoFieldName = true;
}
if (params === null || params === void 0 ? void 0 : params.ts_nocheck) {
o.tsNoCheck = true;
}
if (params === null || params === void 0 ? void 0 : params.eslint_disable) {
o.esLintDisable = true;
}
if (params === null || params === void 0 ? void 0 : params.long_type_string) {
o.normalLongType = rt.LongType.STRING;
}
if (params === null || params === void 0 ? void 0 : params.long_type_number) {
o.normalLongType = rt.LongType.NUMBER;
}
if (params === null || params === void 0 ? void 0 : params.optimize_code_size) {
o.normalOptimizeMode = plugin_framework_1.FileOptions_OptimizeMode.CODE_SIZE;
}
if (params === null || params === void 0 ? void 0 : params.force_optimize_speed) {
o.forcedOptimizeMode = plugin_framework_1.FileOptions_OptimizeMode.SPEED;
}
if (params === null || params === void 0 ? void 0 : params.force_optimize_code_size) {
o.forcedOptimizeMode = plugin_framework_1.FileOptions_OptimizeMode.CODE_SIZE;
}
if (params === null || params === void 0 ? void 0 : params.client_none) {
o.normalClientStyle = ClientStyle.NO_CLIENT;
}
if (params === null || params === void 0 ? void 0 : params.client_grpc1) {
o.normalClientStyle = ClientStyle.GRPC1_CLIENT;
}
if (params === null || params === void 0 ? void 0 : params.force_client_none) {
o.forcedClientStyle = ClientStyle.NO_CLIENT;
}
if (params === null || params === void 0 ? void 0 : params.server_generic) {
o.normalServerStyle = ServerStyle.GENERIC_SERVER;
}
if (params === null || params === void 0 ? void 0 : params.server_grpc1) {
o.normalServerStyle = ServerStyle.GRPC1_SERVER;
}
if (params === null || params === void 0 ? void 0 : params.force_server_none) {
o.forcedServerStyle = ServerStyle.NO_SERVER;
}
if (params === null || params === void 0 ? void 0 : params.add_pb_suffix) {
o.addPbSuffix = true;
}
if (params === null || params === void 0 ? void 0 : params.force_disable_services) {
o.forceDisableServices = true;
}
if (params === null || params === void 0 ? void 0 : params.output_javascript) {
o.transpileTarget = ts.ScriptTarget.ES2020;
}
if (params === null || params === void 0 ? void 0 : params.output_javascript_es2015) {
o.transpileTarget = ts.ScriptTarget.ES2015;
}
if (params === null || params === void 0 ? void 0 : params.output_javascript_es2016) {
o.transpileTarget = ts.ScriptTarget.ES2016;
}
if (params === null || params === void 0 ? void 0 : params.output_javascript_es2017) {
o.transpileTarget = ts.ScriptTarget.ES2017;
}
if (params === null || params === void 0 ? void 0 : params.output_javascript_es2018) {
o.transpileTarget = ts.ScriptTarget.ES2018;
}
if (params === null || params === void 0 ? void 0 : params.output_javascript_es2019) {
o.transpileTarget = ts.ScriptTarget.ES2019;
}
if (params === null || params === void 0 ? void 0 : params.output_javascript_es2020) {
o.transpileTarget = ts.ScriptTarget.ES2020;
}
if (params === null || params === void 0 ? void 0 : params.output_legacy_commonjs) {
o.transpileModule = ts.ModuleKind.CommonJS;
}
return o;
}
exports.makeInternalOptions = makeInternalOptions;
class OptionResolver {
constructor(interpreter, stringFormat, options) {
this.interpreter = interpreter;
this.stringFormat = stringFormat;
this.options = options;
}
getOptimizeMode(file) {
var _a;
if (this.options.forcedOptimizeMode !== undefined) {
return this.options.forcedOptimizeMode;
}
if (((_a = file.options) === null || _a === void 0 ? void 0 : _a.optimizeFor) !== undefined) {
return file.options.optimizeFor;
}
return this.options.normalOptimizeMode;
}
getClientStyles(descriptor) {
const opt = this.interpreter.readOurServiceOptions(descriptor)["ts.client"];
// always check service options valid
if (opt.includes(ClientStyle.NO_CLIENT) && opt.some(s => s !== ClientStyle.NO_CLIENT)) {
let err = new Error(`You provided invalid options for ${this.stringFormat.formatQualifiedName(descriptor, true)}. If you set (ts.client) = NO_CLIENT, you cannot set additional client styles.`);
err.name = `PluginMessageError`;
throw err;
}
if (this.options.forcedClientStyle !== undefined) {
return [this.options.forcedClientStyle];
}
// look for service options
if (opt.length) {
return opt
.filter(s => s !== ClientStyle.NO_CLIENT)
.filter((value, index, array) => array.indexOf(value) === index);
}
// fall back to normal style set by option
return [this.options.normalClientStyle];
}
getServerStyles(descriptor) {
const opt = this.interpreter.readOurServiceOptions(descriptor)["ts.server"];
// always check service options valid
if (opt.includes(ServerStyle.NO_SERVER) && opt.some(s => s !== ServerStyle.NO_SERVER)) {
let err = new Error(`You provided invalid options for ${this.stringFormat.formatQualifiedName(descriptor, true)}. If you set (ts.server) = NO_SERVER, you cannot set additional server styles.`);
err.name = `PluginMessageError`;
throw err;
}
if (this.options.forcedServerStyle !== undefined) {
return [this.options.forcedServerStyle];
}
// look for service options
if (opt.length) {
return opt
.filter(s => s !== ServerStyle.NO_SERVER)
.filter((value, index, array) => array.indexOf(value) === index);
}
// fall back to normal style set by option
return [this.options.normalServerStyle];
}
}
exports.OptionResolver = OptionResolver;

View file

@ -0,0 +1,15 @@
import { DescriptorRegistry, FileDescriptorProto, GeneratedFile, TypescriptFile } from "@protobuf-ts/plugin-framework";
import { InternalOptions } from "./our-options";
/**
* A protobuf-ts output file.
*/
export declare class OutFile extends TypescriptFile implements GeneratedFile {
readonly fileDescriptor: FileDescriptorProto;
private readonly registry;
private readonly options;
private header;
constructor(name: string, fileDescriptor: FileDescriptorProto, registry: DescriptorRegistry, options: InternalOptions);
getContent(): string;
getHeader(): string;
private makeHeader;
}

View file

@ -0,0 +1,63 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.OutFile = void 0;
const plugin_framework_1 = require("@protobuf-ts/plugin-framework");
/**
* A protobuf-ts output file.
*/
class OutFile extends plugin_framework_1.TypescriptFile {
constructor(name, fileDescriptor, registry, options) {
super(name);
this.fileDescriptor = fileDescriptor;
this.registry = registry;
this.options = options;
}
getContent() {
if (this.isEmpty()) {
return "";
}
return this.getHeader() + super.getContent();
}
getHeader() {
if (this.isEmpty()) {
return "";
}
if (!this.header) {
this.header = this.makeHeader();
}
return this.header;
}
makeHeader() {
var _a;
let props = [];
if (this.fileDescriptor.package) {
props.push('package "' + this.fileDescriptor.package + '"');
}
props.push('syntax ' + ((_a = this.fileDescriptor.syntax) !== null && _a !== void 0 ? _a : 'proto2'));
const header = [];
if (this.options.esLintDisable) {
header.push(`/* eslint-disable */`);
}
header.push(...[
`// @generated ${this.options.pluginCredit}`,
`// @generated from protobuf file "${this.fileDescriptor.name}" (${props.join(', ')})`,
`// tslint:disable`
]);
if (this.options.tsNoCheck) {
header.push(`// @ts-nocheck`);
}
if (this.registry.isExplicitlyDeclaredDeprecated(this.fileDescriptor)) {
header.push('// @deprecated');
}
[
...this.registry.sourceCodeComments(this.fileDescriptor, plugin_framework_1.FileDescriptorProtoFields.syntax).leadingDetached,
...this.registry.sourceCodeComments(this.fileDescriptor, plugin_framework_1.FileDescriptorProtoFields.package).leadingDetached
].every(block => header.push('//', ...block.split('\n').map(l => '//' + l), '//'));
let head = header.join('\n');
if (head.length > 0 && !head.endsWith('\n')) {
head += '\n';
}
return head;
}
}
exports.OutFile = OutFile;

View file

@ -0,0 +1,142 @@
import { CodeGeneratorRequest, CodeGeneratorResponse_Feature, GeneratedFile, PluginBase } from "@protobuf-ts/plugin-framework";
import { OutFile } from "./out-file";
import { InternalOptions } from "./our-options";
export declare class ProtobuftsPlugin extends PluginBase {
private readonly version;
parameters: {
long_type_string: {
description: string;
excludes: string[];
};
long_type_number: {
description: string;
excludes: string[];
};
long_type_bigint: {
description: string;
excludes: string[];
};
generate_dependencies: {
description: string;
};
force_exclude_all_options: {
description: string;
};
keep_enum_prefix: {
description: string;
};
use_proto_field_name: {
description: string;
};
ts_nocheck: {
description: string;
excludes: string[];
};
disable_ts_nocheck: {
description: string;
excludes: string[];
};
eslint_disable: {
description: string;
excludes: string[];
};
no_eslint_disable: {
description: string;
excludes: string[];
};
add_pb_suffix: {
description: string;
};
output_typescript: {
description: string;
excludes: string[];
};
output_javascript: {
description: string;
excludes: string[];
};
output_javascript_es2015: {
description: string;
excludes: string[];
};
output_javascript_es2016: {
description: string;
excludes: string[];
};
output_javascript_es2017: {
description: string;
excludes: string[];
};
output_javascript_es2018: {
description: string;
excludes: string[];
};
output_javascript_es2019: {
description: string;
excludes: string[];
};
output_javascript_es2020: {
description: string;
excludes: string[];
};
output_legacy_commonjs: {
description: string;
excludes: string[];
};
client_none: {
description: string;
excludes: string[];
};
client_generic: {
description: string;
excludes: string[];
};
client_grpc1: {
description: string;
excludes: string[];
};
force_client_none: {
description: string;
excludes: string[];
};
server_none: {
description: string;
excludes: string[];
};
server_generic: {
description: string;
excludes: string[];
};
server_grpc1: {
description: string;
excludes: string[];
};
force_server_none: {
description: string;
};
force_disable_services: {
description: string;
excludes: string[];
};
optimize_speed: {
description: string;
excludes: string[];
};
optimize_code_size: {
description: string;
excludes: string[];
};
force_optimize_code_size: {
description: string;
excludes: string[];
};
force_optimize_speed: {
description: string;
excludes: string[];
};
};
constructor(version: string);
generate(request: CodeGeneratorRequest): GeneratedFile[];
protected transpile(tsFiles: OutFile[], options: InternalOptions): GeneratedFile[];
protected getSupportedFeatures: () => CodeGeneratorResponse_Feature[];
}

View file

@ -0,0 +1,357 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProtobuftsPlugin = void 0;
const plugin_framework_1 = require("@protobuf-ts/plugin-framework");
const out_file_1 = require("./out-file");
const local_type_name_1 = require("./code-gen/local-type-name");
const interpreter_1 = require("./interpreter");
const our_options_1 = require("./our-options");
const service_server_generator_grpc_1 = require("./code-gen/service-server-generator-grpc");
const comment_generator_1 = require("./code-gen/comment-generator");
const message_interface_generator_1 = require("./code-gen/message-interface-generator");
const message_type_generator_1 = require("./code-gen/message-type-generator");
const enum_generator_1 = require("./code-gen/enum-generator");
const service_type_generator_1 = require("./code-gen/service-type-generator");
const service_client_generator_generic_1 = require("./code-gen/service-client-generator-generic");
const file_table_1 = require("./file-table");
const service_server_generator_generic_1 = require("./code-gen/service-server-generator-generic");
const service_client_generator_grpc_1 = require("./code-gen/service-client-generator-grpc");
const ts = require("typescript");
const runtime_1 = require("@protobuf-ts/runtime");
const well_known_types_1 = require("./message-type-extensions/well-known-types");
class ProtobuftsPlugin extends plugin_framework_1.PluginBase {
constructor(version) {
super();
this.version = version;
this.parameters = {
// @formatter:off
// long type
long_type_string: {
description: "Sets jstype = JS_STRING for message fields with 64 bit integral values. \n" +
"The default behaviour is to use native `bigint`. \n" +
"Only applies to fields that do *not* use the option `jstype`.",
excludes: ["long_type_number", "long_type_bigint"],
},
long_type_number: {
description: "Sets jstype = JS_NUMBER for message fields with 64 bit integral values. \n" +
"The default behaviour is to use native `bigint`. \n" +
"Only applies to fields that do *not* use the option `jstype`.",
excludes: ["long_type_string", "long_type_bigint"],
},
long_type_bigint: {
description: "Sets jstype = JS_NORMAL for message fields with 64 bit integral values. \n" +
"This is the default behavior. \n" +
"Only applies to fields that do *not* use the option `jstype`.",
excludes: ["long_type_string", "long_type_number"],
},
// misc
generate_dependencies: {
description: "By default, only the PROTO_FILES passed as input to protoc are generated, \n" +
"not the files they import (with the exception of well-known types, which are \n" +
"always generated when imported). \n" +
"Set this option to generate code for dependencies too.",
},
force_exclude_all_options: {
description: "By default, custom options are included in the metadata and can be blacklisted \n" +
"with our option (ts.exclude_options). Set this option if you are certain you \n" +
"do not want to include any options at all.",
},
keep_enum_prefix: {
description: "By default, if all enum values share a prefix that corresponds with the enum's \n" +
"name, the prefix is dropped from the value names. Set this option to disable \n" +
"this behavior.",
},
use_proto_field_name: {
description: "By default interface fields use lowerCamelCase names by transforming proto field\n" +
"names to follow common style convention for TypeScript. Set this option to preserve\n" +
"original proto field names in generated interfaces.",
},
ts_nocheck: {
description: "Generate a @ts-nocheck annotation at the top of each file. This will become the \n" +
"default behaviour in the next major release.",
excludes: ['disable_ts_nocheck'],
},
disable_ts_nocheck: {
description: "Do not generate a @ts-nocheck annotation at the top of each file. Since this is \n" +
"the default behaviour, this option has no effect.",
excludes: ['ts_nocheck'],
},
eslint_disable: {
description: "Generate a eslint-disable comment at the top of each file. This will become the \n" +
"default behaviour in the next major release.",
excludes: ['no_eslint_disable'],
},
no_eslint_disable: {
description: "Do not generate a eslint-disable comment at the top of each file. Since this is \n" +
"the default behaviour, this option has no effect.",
excludes: ['eslint_disable'],
},
add_pb_suffix: {
description: "Adds the suffix `_pb` to the names of all generated files. This will become the \n" +
"default behaviour in the next major release.",
},
// output types
output_typescript: {
description: "Output TypeScript files. This is the default behavior.",
excludes: ["output_javascript", "output_javascript_es2015", "output_javascript_es2016", "output_javascript_es2017", "output_javascript_es2018", "output_javascript_es2019", "output_javascript_es2020"]
},
output_javascript: {
description: "Output JavaScript for the currently recommended target ES2020. The target may \n" +
"change with a major release of protobuf-ts. \n" +
"Along with JavaScript files, this always outputs TypeScript declaration files.",
excludes: ["output_typescript", "output_javascript_es2015", "output_javascript_es2016", "output_javascript_es2017", "output_javascript_es2018", "output_javascript_es2019", "output_javascript_es2020"]
},
output_javascript_es2015: {
description: "Output JavaScript for the ES2015 target.",
excludes: ["output_typescript", "output_javascript_es2016", "output_javascript_es2017", "output_javascript_es2018", "output_javascript_es2019", "output_javascript_es2020"]
},
output_javascript_es2016: {
description: "Output JavaScript for the ES2016 target.",
excludes: ["output_typescript", "output_javascript_es2015", "output_javascript_es2017", "output_javascript_es2018", "output_javascript_es2019", "output_javascript_es2020"]
},
output_javascript_es2017: {
description: "Output JavaScript for the ES2017 target.",
excludes: ["output_typescript", "output_javascript_es2015", "output_javascript_es2016", "output_javascript_es2018", "output_javascript_es2019", "output_javascript_es2020"]
},
output_javascript_es2018: {
description: "Output JavaScript for the ES2018 target.",
excludes: ["output_typescript", "output_javascript_es2015", "output_javascript_es2016", "output_javascript_es2017", "output_javascript_es2019", "output_javascript_es2020"]
},
output_javascript_es2019: {
description: "Output JavaScript for the ES2019 target.",
excludes: ["output_typescript", "output_javascript_es2015", "output_javascript_es2016", "output_javascript_es2017", "output_javascript_es2018", "output_javascript_es2020"]
},
output_javascript_es2020: {
description: "Output JavaScript for the ES2020 target.",
excludes: ["output_typescript", "output_javascript_es2015", "output_javascript_es2016", "output_javascript_es2017", "output_javascript_es2018", "output_javascript_es2019"]
},
output_legacy_commonjs: {
description: "Use CommonJS instead of the default ECMAScript module system.",
excludes: ["output_typescript"]
},
// client
client_none: {
description: "Do not generate rpc clients. \n" +
"Only applies to services that do *not* use the option `ts.client`. \n" +
"If you do not want rpc clients at all, use `force_client_none`.",
excludes: ['client_generic', 'client_grpc1'],
},
client_generic: {
description: "Only applies to services that do *not* use the option `ts.client`. \n" +
"Since GENERIC_CLIENT is the default, this option has no effect.",
excludes: ['client_none', 'client_grpc1', 'force_client_none', 'force_disable_services'],
},
client_grpc1: {
description: "Generate a client using @grpc/grpc-js (major version 1). \n" +
"Only applies to services that do *not* use the option `ts.client`.",
excludes: ['client_none', 'client_generic', 'force_client_none', 'force_disable_services'],
},
force_client_none: {
description: "Do not generate rpc clients, ignore options in proto files.",
excludes: ['client_none', 'client_generic', 'client_grpc1'],
},
// server
server_none: {
description: "Do not generate rpc servers. \n" +
"This is the default behaviour, but only applies to services that do \n" +
"*not* use the option `ts.server`. \n" +
"If you do not want servers at all, use `force_server_none`.",
excludes: ['server_grpc1'],
},
server_generic: {
description: "Generate a generic server interface. Adapters are used to serve the service, \n" +
"for example @protobuf-ts/grpc-backend for gRPC. \n" +
"Note that this is an experimental feature and may change with a minor release. \n" +
"Only applies to services that do *not* use the option `ts.server`.",
excludes: ['server_none', 'force_server_none', 'force_disable_services'],
},
server_grpc1: {
description: "Generate a server interface and definition for use with @grpc/grpc-js \n" +
"(major version 1). \n" +
"Only applies to services that do *not* use the option `ts.server`.",
excludes: ['server_none', 'force_server_none', 'force_disable_services'],
},
force_server_none: {
description: "Do not generate rpc servers, ignore options in proto files.",
},
force_disable_services: {
description: "Do not generate anything for service definitions, and ignore options in proto \n" +
"files. This is the same as setting both options `force_server_none` and \n" +
"`force_client_none`, but also stops generating service metadata.",
excludes: ['client_generic', 'client_grpc1', 'server_generic', 'server_grpc1']
},
// optimization
optimize_speed: {
description: "Sets optimize_for = SPEED for proto files that have no file option \n" +
"'option optimize_for'. Since SPEED is the default, this option has no effect.",
excludes: ['force_optimize_speed'],
},
optimize_code_size: {
description: "Sets optimize_for = CODE_SIZE for proto files that have no file option \n" +
"'option optimize_for'.",
excludes: ['force_optimize_speed'],
},
force_optimize_code_size: {
description: "Forces optimize_for = CODE_SIZE for all proto files, ignore file options.",
excludes: ['optimize_code_size', 'force_optimize_speed']
},
force_optimize_speed: {
description: "Forces optimize_for = SPEED for all proto files, ignore file options.",
excludes: ['optimize_code_size', 'force_optimize_code_size']
},
};
// we support proto3-optionals, so we let protoc know
this.getSupportedFeatures = () => [plugin_framework_1.CodeGeneratorResponse_Feature.PROTO3_OPTIONAL];
this.version = version;
}
generate(request) {
const options = our_options_1.makeInternalOptions(this.parseOptions(this.parameters, request.parameter), `by protobuf-ts ${this.version}` + (request.parameter ? ` with parameter ${request.parameter}` : '')), registry = plugin_framework_1.DescriptorRegistry.createFrom(request), symbols = new plugin_framework_1.SymbolTable(), fileTable = new file_table_1.FileTable(), imports = new plugin_framework_1.TypeScriptImports(symbols), comments = new comment_generator_1.CommentGenerator(registry), interpreter = new interpreter_1.Interpreter(registry, options), optionResolver = new our_options_1.OptionResolver(interpreter, registry, options), genMessageInterface = new message_interface_generator_1.MessageInterfaceGenerator(symbols, registry, imports, comments, interpreter, options), genEnum = new enum_generator_1.EnumGenerator(symbols, registry, imports, comments, interpreter, options), genMessageType = new message_type_generator_1.MessageTypeGenerator(symbols, registry, imports, comments, interpreter, options), genServiceType = new service_type_generator_1.ServiceTypeGenerator(symbols, registry, imports, comments, interpreter, options), genServerGeneric = new service_server_generator_generic_1.ServiceServerGeneratorGeneric(symbols, registry, imports, comments, interpreter, options), genServerGrpc = new service_server_generator_grpc_1.ServiceServerGeneratorGrpc(symbols, registry, imports, comments, interpreter, options), genClientGeneric = new service_client_generator_generic_1.ServiceClientGeneratorGeneric(symbols, registry, imports, comments, interpreter, options), genClientGrpc = new service_client_generator_grpc_1.ServiceClientGeneratorGrpc(symbols, registry, imports, comments, interpreter, options);
let tsFiles = [];
// ensure unique file names
for (let fileDescriptor of registry.allFiles()) {
const base = fileDescriptor.name.replace('.proto', '') + (options.addPbSuffix ? "_pb" : "");
fileTable.register(base + '.ts', fileDescriptor);
}
for (let fileDescriptor of registry.allFiles()) {
const base = fileDescriptor.name.replace('.proto', '') + (options.addPbSuffix ? "_pb" : "");
fileTable.register(base + '.server.ts', fileDescriptor, 'generic-server');
fileTable.register(base + '.grpc-server.ts', fileDescriptor, 'grpc1-server');
fileTable.register(base + '.client.ts', fileDescriptor, 'client');
fileTable.register(base + '.promise-client.ts', fileDescriptor, 'promise-client');
fileTable.register(base + '.rx-client.ts', fileDescriptor, 'rx-client');
fileTable.register(base + '.grpc-client.ts', fileDescriptor, 'grpc1-client');
}
for (let fileDescriptor of registry.allFiles()) {
const outMain = new out_file_1.OutFile(fileTable.get(fileDescriptor).name, fileDescriptor, registry, options), outServerGeneric = new out_file_1.OutFile(fileTable.get(fileDescriptor, 'generic-server').name, fileDescriptor, registry, options), outServerGrpc = new out_file_1.OutFile(fileTable.get(fileDescriptor, 'grpc1-server').name, fileDescriptor, registry, options), outClientCall = new out_file_1.OutFile(fileTable.get(fileDescriptor, 'client').name, fileDescriptor, registry, options), outClientPromise = new out_file_1.OutFile(fileTable.get(fileDescriptor, 'promise-client').name, fileDescriptor, registry, options), outClientRx = new out_file_1.OutFile(fileTable.get(fileDescriptor, 'rx-client').name, fileDescriptor, registry, options), outClientGrpc = new out_file_1.OutFile(fileTable.get(fileDescriptor, 'grpc1-client').name, fileDescriptor, registry, options);
tsFiles.push(outMain, outServerGeneric, outServerGrpc, outClientCall, outClientPromise, outClientRx, outClientGrpc);
registry.visitTypes(fileDescriptor, descriptor => {
// we are not interested in synthetic types like map entry messages
if (registry.isSyntheticElement(descriptor))
return;
// register all symbols, regardless whether they are going to be used - we want stable behaviour
symbols.register(local_type_name_1.createLocalTypeName(descriptor, registry), descriptor, outMain);
if (plugin_framework_1.ServiceDescriptorProto.is(descriptor)) {
genClientGeneric.registerSymbols(outClientCall, descriptor);
genClientGrpc.registerSymbols(outClientGrpc, descriptor);
genServerGeneric.registerSymbols(outServerGeneric, descriptor);
genServerGrpc.registerSymbols(outServerGrpc, descriptor);
}
});
registry.visitTypes(fileDescriptor, descriptor => {
// we are not interested in synthetic types like map entry messages
if (registry.isSyntheticElement(descriptor))
return;
if (plugin_framework_1.DescriptorProto.is(descriptor)) {
genMessageInterface.generateMessageInterface(outMain, descriptor);
}
if (plugin_framework_1.EnumDescriptorProto.is(descriptor)) {
genEnum.generateEnum(outMain, descriptor);
}
});
registry.visitTypes(fileDescriptor, descriptor => {
// still not interested in synthetic types like map entry messages
if (registry.isSyntheticElement(descriptor))
return;
if (plugin_framework_1.DescriptorProto.is(descriptor)) {
genMessageType.generateMessageType(outMain, descriptor, optionResolver.getOptimizeMode(fileDescriptor));
}
if (!options.forceDisableServices) {
if (plugin_framework_1.ServiceDescriptorProto.is(descriptor)) {
// service type
genServiceType.generateServiceType(outMain, descriptor);
// clients
const clientStyles = optionResolver.getClientStyles(descriptor);
if (clientStyles.includes(our_options_1.ClientStyle.GENERIC_CLIENT)) {
genClientGeneric.generateInterface(outClientCall, descriptor);
genClientGeneric.generateImplementationClass(outClientCall, descriptor);
}
if (clientStyles.includes(our_options_1.ClientStyle.GRPC1_CLIENT)) {
genClientGrpc.generateInterface(outClientGrpc, descriptor);
genClientGrpc.generateImplementationClass(outClientGrpc, descriptor);
}
// servers
const serverStyles = optionResolver.getServerStyles(descriptor);
if (serverStyles.includes(our_options_1.ServerStyle.GENERIC_SERVER)) {
genServerGeneric.generateInterface(outServerGeneric, descriptor);
}
if (serverStyles.includes(our_options_1.ServerStyle.GRPC1_SERVER)) {
genServerGrpc.generateInterface(outServerGrpc, descriptor);
genServerGrpc.generateDefinition(outServerGrpc, descriptor);
}
}
}
});
}
// plugins should only return files requested to generate
// unless our option "generate_dependencies" is set.
// We always return well-known types, because we do not
// maintain them in a package - they are always generated
// on demand.
if (!options.generateDependencies) {
tsFiles = tsFiles.filter(file => {
const protoFilename = file.fileDescriptor.name;
runtime_1.assert(protoFilename);
if (request.fileToGenerate.includes(protoFilename)) {
return true;
}
if (well_known_types_1.WellKnownTypes.protoFilenames.includes(protoFilename)) {
return true;
}
return false;
});
}
// if a proto file is imported to use custom options, or if a proto file declares custom options,
// we do not to emit it. unless it was explicitly requested.
const outFileDescriptors = tsFiles.map(of => of.fileDescriptor);
tsFiles = tsFiles.filter(of => request.fileToGenerate.includes(of.fileDescriptor.name)
|| registry.isFileUsed(of.fileDescriptor, outFileDescriptors));
return this.transpile(tsFiles, options);
}
transpile(tsFiles, options) {
if (options.transpileTarget === undefined) {
return tsFiles;
}
const opt = {
moduleResolution: ts.ModuleResolutionKind.NodeJs,
skipLibCheck: true,
declaration: true,
module: options.transpileModule,
target: options.transpileTarget,
};
const [program,] = plugin_framework_1.setupCompiler(opt, tsFiles, tsFiles.map(f => f.getFilename()));
const results = [];
let err;
program.emit(undefined, (fileName, data, writeByteOrderMark, onError, sourceFiles) => {
// We have to go through some hoops here because the header we add to each file
// is not part of the AST. So we find the TypeScript file we generated for each
// emitted file and add the header to each output ourselves.
if (!sourceFiles) {
err = new Error(`unable to map emitted file "${fileName}" to a source file: missing source files`);
return;
}
if (sourceFiles.length !== 1) {
err = new Error(`unable to map emitted file "${fileName}" to a source file: expected 1 source file, got ${sourceFiles.length}`);
return;
}
const tsFile = tsFiles.find(x => sourceFiles[0].fileName === x.getFilename());
if (!tsFile) {
err = new Error(`unable to map emitted file "${fileName}" to a source file: not found`);
return;
}
const content = tsFile.getHeader() + data;
results.push({
getFilename() {
return fileName;
},
getContent() {
return content;
}
});
});
if (err) {
throw err;
}
return results;
}
}
exports.ProtobuftsPlugin = ProtobuftsPlugin;