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,174 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.

View file

@ -0,0 +1,37 @@
@protobuf-ts/plugin
===================
The protocol buffer compiler plugin for TypeScript: [protobuf-ts](https://github.com/timostamm/protobuf-ts)
Installation:
```shell script
# with npm:
npm install -D @protobuf-ts/plugin
# with yarn:
yarn add --dev @protobuf-ts/plugin
```
This will install the plugin as a development dependency.
Basic usage:
```shell script
npx protoc --ts_out . --proto_path protos protos/my.proto
```
With some options:
```shell script
npx protoc \
--ts_out . \
--ts_opt long_type_string \
--ts_opt optimize_code_size \
--proto_path protos \
protos/my.proto
```
`protoc` is the protocol buffer compiler. [protobuf-ts](https://github.com/timostamm/protobuf-ts)
installs it automatically.
Plugin parameters are documented in the [MANUAL](https://github.com/timostamm/protobuf-ts/blob/master/MANUAL.md#the-protoc-plugin).
For a quick overview of [protobuf-ts](https://github.com/timostamm/protobuf-ts), check the repository [README](https://github.com/timostamm/protobuf-ts/blob/master/README.md).

View file

@ -0,0 +1,8 @@
#!/usr/bin/env node
const {DumpPlugin} = require("../build/dump-plugin");
new DumpPlugin().run().catch(_ => {
process.stderr.write('failed to run plugin');
process.exit(1);
});

View file

@ -0,0 +1,9 @@
#!/usr/bin/env node
const {ProtobuftsPlugin} = require("../build/protobufts-plugin");
const pkg = require("../package.json");
new ProtobuftsPlugin(pkg.version).run().catch(_ => {
process.stderr.write('failed to run plugin');
process.exit(1);
});

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;

View file

@ -0,0 +1,48 @@
{
"name": "@protobuf-ts/plugin",
"version": "2.9.3",
"description": "The protocol buffer compiler plugin \"protobuf-ts\" generates TypeScript, gRPC-web, Twirp, and more.",
"bin": {
"protoc-gen-ts": "./bin/protoc-gen-ts",
"protoc-gen-dump": "./bin/protoc-gen-dump"
},
"license": "Apache-2.0",
"author": "Timo Stamm <ts@timostamm.com>",
"homepage": "https://github.com/timostamm/protobuf-ts",
"keywords": [
"Protocol Buffers",
"protobuf",
"TypeScript",
"protoc",
"bigint",
"gRPC-web",
"gRPC",
"Twirp",
"Reflection"
],
"repository": {
"type": "git",
"url": "https://github.com/timostamm/protobuf-ts.git",
"directory": "packages/plugin"
},
"publishConfig": {
"access": "public"
},
"devDependencies": {
"@types/jasmine": "^3.5.10",
"@types/node": "^14.0.13",
"jasmine": "^3.5.0",
"jasmine-spec-reporter": "^5.0.2",
"ts-node": "^8.10.2",
"tsconfig-paths": "^3.9.0",
"tslib": ">=1.6.1"
},
"dependencies": {
"@protobuf-ts/plugin-framework": "^2.9.3",
"@protobuf-ts/protoc": "^2.9.3",
"@protobuf-ts/runtime": "^2.9.3",
"@protobuf-ts/runtime-rpc": "^2.9.3",
"typescript": "^3.9"
},
"gitHead": "bbda0c30ecc8e826c07a41105e93e05dbb9638b2"
}

View file

@ -0,0 +1,83 @@
// This proto file is part of protobuf-ts. It defines custom options
// that are interpreted by @protobuf-ts/plugin.
//
// To use the options, add an import to this file:
//
// import "protobuf-ts.proto";
//
// If you use @protobuf-ts/plugin, it should not be necessary to add a proto
// path to the file. @protobuf-ts/protoc automatically adds
// `--proto_path ./node_modules/@protobuf-ts/plugin` to your commands.
syntax = "proto3";
package ts;
import "google/protobuf/descriptor.proto";
// Custom file options interpreted by @protobuf-ts/plugin
extend google.protobuf.FileOptions {
// 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.
repeated string exclude_options = 777701;
}
// Custom service options interpreted by @protobuf-ts/plugin
extend google.protobuf.ServiceOptions {
// Generate a client for this service with this style.
// Can be set multiple times to generate several styles.
repeated ClientStyle client = 777701;
// Generate a server for this service with this style.
// Can be set multiple times to generate several styles.
repeated ServerStyle server = 777702;
}
// The available client styles that can be generated by @protobuf-ts/plugin
enum ClientStyle {
// Do not emit a client for this service.
NO_CLIENT = 0;
// Use the generic 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 that can be generated by @protobuf-ts/plugin
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;
}