troto

1.3.1 • Public • Published

Troto

Troto is a replacement for the protobuf compiler that can generate .proto files from TypeScript types as well as invoke any plugins without even needing protoc installed. This leads to a much better experience as TypeScript is much more powerful than the .proto language and allows automatic service XxxRequest/XxxResponse generation, generics, and much more.

Usage

To use Troto, you need add it in your project using your favorite package manager. For example, with pnpm:

pnpm install troto

After this step, you will have the trotoc command available in your project. Next, you will need to create a compatible tsconfig.json file.

You can use the following example as a starting point:

{
	"compilerOptions": {
		"strict": true,
		"outDir": "./dist/proto",
		"types": ["troto/types"]
	}
}

All available options are:

{
	// TypeScript options
	"compilerOptions": {
		"strict": true,
		// Output directory for the generated .proto files
		"outDir": "./dist/proto",
		// Required to use the troto builtin types
		"types": ["troto/types"]
	},

	// Troto options
	"troto": {
		// Project-wide configuration of default file options
		"options": {
			// This will add `option objc_class_prefix = "TRO";`
			// to all generated .proto files
			"objc_class_prefix": "TRO"
		},
		// Language-specific package prefixes
		"packages": {
			"go": "github.com/yourname/yourproject"
		},

		// If set generates optional fields without the single-item oneof wrapper. Proto 3.15+.
		"optionals315": false,

		// Whether to generate .proto files, or just call the plugins
		"proto": true,

		// Ignore list for the packages you would like to skip emitting
		"ignore": ["google.protobuf"],

		// Plugins configuration
		"plugins": {
			// Invoke protoc-gen-gogofaster with:
			"gogofaster": {
				// Output directory for the generated .pb.go files
				"outDir": ".",
				// Plugin-specific options
				"paths": "source_relative",
				"plugins": "grpc"
			}
		}
	}
}

Once this is done, you can simply run npx trotoc . in your project directory to run the compiler.

Lets create an example to demonstrate how Troto works.

// Non-exported types are not emitted
interface Vector3<T> {
	x$1: T; // $ suffix sets the field number
	y$2: T;
	z: T; // Default field number is the field order
}

// Exported interfaces with no methods are emitted as messages
export interface Vector3f extends Vector3<float> {}
export interface Vector3d extends Vector3<double> {}
export interface Vector3i extends Vector3<int32> {}

// Importing works as expected, google types are provided by troto
import { Struct } from 'troto/types/google/protobuf/struct';

// You can also force an import by prefixing with '?'
import '?my/file.proto';

// This will add `option a = "b";` to the generated definition of ComplexType
/*** @option a=b */
export interface ComplexType {
	opt1?: Vector3f; // Optional fields are emitted as optional
	opt2: Opt<Vector3d>; // Opt<> can also be used to make fields optional

	map1: Map<string, Vector3i>; // Map<> can be used to define map fields

	rep1: Rep<Vector3f>; // Rep<> can be used to define repeated fields
	rep2: Vector3f[]; // This is equivalent to Rep<Vector3f>

	ext1: Ext<Vector3f, { y: 4 }>; // Ext<> can be used to define options for fields

	union: { f: Vector3f } | { d: Vector3d }; // Unions are emitted as oneof

	// All primitive types are supported with their own names
	b1: bool;
	b2: double;
	b3: bytes;
	b4: float;
	b5: int32;
	b6: int64;
	b7: uint32;
	b8: uint64;
	b9: sint32;
	b10: sint64;
	b11: fixed32;
	b12: fixed64;
	b13: sfixed32;
	b14: sfixed64;
	str: Struct;

	// Javascript types map to protobuf types as well.
	date: Date; // google.protobuf.Timestamp
	any: any; // google.protobuf.Any
	arr: Array<any>; // repeated google.protobuf.Any
	bytes: ArrayBuffer; // bytes
	u32: Uint32Array; // repeated uint32
}

// Type aliases are not emitted either, exported or not. They are for your
// internal use across different files
export type Test = {
	z: 4;
};

// You can also set file options via the FileOpt function
FileOpt('csharp_namespace', 'Example.Test');

// Exported interfaces with methods are emitted as services
export interface VectorService {
	// If the method has multiple arguments, they are wrapped in a request message
	// If the arguments / return types are empty, it will use google.protobuf.Empty
	MulF32(v: Vector3f, f: float): Vector3f;
	MulF64(v: Vector3d, d: double): Vector3d;
	MulI32(v: Vector3i, i: int32): Stream<Vector3i>; // Stream<> can be used to define streaming methods
}

This will generate the following .proto file:

// Code generated by Troto. DO NOT EDIT.
syntax = "proto3";
package test;
option java_multiple_files = true;
option cc_enable_arenas = true;
option optimize_for = 1;
option csharp_namespace = "Example.Test";
option java_package = "com.test";
option php_namespace = "Test";
option ruby_package = "Test";
option java_outer_classname = "SampleProto";
option php_metadata_namespace = "Test\PBMetadata";
option go_package = "test";
import "my/file.proto";
import "google/protobuf/struct.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/any.proto";
message Vector3f {
	float x = 1;
	float y = 2;
	float z = 3;
}
message Vector3d {
	double x = 1;
	double y = 2;
	double z = 3;
}
message Vector3i {
	int32 x = 1;
	int32 y = 2;
	int32 z = 3;
}
message ComplexType {
	option a = "b";
	optional Vector3f opt1 = 1;
	optional Vector3d opt2 = 2;
	oneof union {
		Vector3f f = 7;
		Vector3d d = 8;
	}
	map<string, Vector3i> map1 = 3;
	repeated Vector3f rep1 = 4;
	repeated Vector3f rep2 = 5;
	Vector3f ext1 = 6 [y=4];
	bool b1 = 9;
	double b2 = 10;
	bytes b3 = 11;
	float b4 = 12;
	int32 b5 = 13;
	int64 b6 = 14;
	uint32 b7 = 15;
	uint64 b8 = 16;
	sint32 b9 = 17;
	sint64 b10 = 18;
	fixed32 b11 = 19;
	fixed64 b12 = 20;
	sfixed32 b13 = 21;
	sfixed64 b14 = 22;
	google.protobuf.Struct str = 23;
	google.protobuf.Timestamp date = 24;
	google.protobuf.Any any = 25;
	repeated google.protobuf.Any arr = 26;
	bytes bytes = 27;
	repeated uint32 u32 = 28;
}
service VectorService {
	rpc MulF32 (MulF32Request) returns (Vector3f);
	rpc MulF64 (MulF64Request) returns (Vector3d);
	rpc MulI32 (MulI32Request) returns (stream Vector3i);
}
message MulF32Request {
	Vector3f v = 1;
	float f = 2;
}
message MulF64Request {
	Vector3d v = 1;
	double d = 2;
}
message MulI32Request {
	Vector3i v = 1;
	int32 i = 2;
}

Neat, right? Now we can invoke code-generation plugins compatible with protoc protocol to generate code without emitting .proto files at all.

Let's modify the tsconfig.json file:

{
	"compilerOptions": {
		"strict": true,
		"skipLibCheck": true,
		"outDir": "./dist/proto",
		"types": ["troto/types"]
	},
	"troto": {
		"proto": false,
		"plugins": {
			"go": {
				"outDir": "./gen",
				"paths": "source_relative"
			}
		}
	}
}

After running the command below, we will have the .go files under the ./gen directory.

npx trotoc .

You are now ready to use Troto in your project.

Enjoy generics, sum types, and much more in your project without losing any of the benefits of protobuf!

Package Sidebar

Install

npm i troto

Weekly Downloads

69

Version

1.3.1

License

MIT

Unpacked Size

824 kB

Total Files

46

Last publish

Collaborators

  • can1357