Protocol Buffer code generator for TypeScript with esbuild and Vite plugins
npm install @nativewrappers/protogenA Protocol Buffer code generator for TypeScript. Generates static encode/decode functions from .proto files as an esbuild plugin.
``bash`
pnpm add @nativewrappers/protogen protobufjs
`typescript
import esbuild from "esbuild";
import { protogenPlugin } from "@nativewrappers/protogen";
await esbuild.build({
entryPoints: ["src/*/.ts"],
outdir: "dist",
plugins: [
protogenPlugin({
protoDirs: ["./src/protos"],
watch: true, // Enable watching .proto files
}),
],
});
`
`typescript
import { defineConfig } from "vite";
import { protogenVitePlugin } from "@nativewrappers/protogen/vite";
export default defineConfig({
plugins: [
protogenVitePlugin({
protoDirs: ["./src/protos"],
}),
],
});
`
Both plugins generate _pb.ts files from .proto files at build start. In watch/dev mode, they automatically regenerate when proto files change.
For proto files:
`protobuf
message ProtoVec3 {
float x = 1;
float y = 2;
float z = 3;
}
message ProtoNetVehicle {
int32 net_id = 1;
}
`
The generator produces:
`typescript
import { Reader, Writer } from "protobufjs/minimal.js";
export type Vec3 = {
x: number;
y: number;
z: number;
};
export const ProtoVec3 = {
name: "ProtoVec3" as const,
encode: (msg: Vec3): Uint8Array => { ... },
decode: (bytes: Uint8Array): Vec3 => { ... },
create: (data?: Partial
};
export type NetVehicle = {
net_id: number;
};
export const ProtoNetVehicle = {
name: "ProtoNetVehicle" as const,
encode: (msg: NetVehicle): Uint8Array => { ... },
decode: (bytes: Uint8Array): NetVehicle => { ... },
create: (data?: Partial
};
`
`typescript
import { ProtoVec3, ProtoNetVehicle } from "./protos/Types_pb";
const bytes = ProtoVec3.encode({ x: 1.0, y: 2.5, z: 3.0 });
const position = ProtoVec3.decode(bytes);
`
`typescript
protogenPlugin({
// Directories containing .proto files
protoDirs: ["./src/protos"],
// Output file suffix (default: "_pb.ts")
suffix: "_pb.ts",
// Include 'name' field in output (default: true)
generateName: true,
// Include 'create' method (default: true)
generateCreate: true,
// Field naming: "snake" or "camel" (default: "snake")
fieldCase: "snake",
// Custom decode transforms
transforms: {},
});
`
`typescript`
protogenPlugin({
protoDirs: ["./src/protos"],
generateName: false,
generateCreate: false,
});
Generates only encode and decode:
`typescript`
export const ProtoVec3 = {
encode: (msg: Vec3): Uint8Array => { ... },
decode: (bytes: Uint8Array): Vec3 => { ... },
};
Wrap decoded messages in custom classes:
`typescriptnew Vector3(${msg}.x, ${msg}.y, ${msg}.z)
protogenPlugin({
protoDirs: ["./src/protos"],
transforms: {
ProtoVec3: {
import: "@nativewrappers/common",
importName: "Vector3",
returnType: "Vector3",
transform: (msg) => ,Vehicle.fromNetId(${msg}.net_id)
},
ProtoNetVehicle: {
import: "@nativewrappers/redm",
importName: "Vehicle",
returnType: "Vehicle | null",
transform: (msg) => ,`
},
},
});
With transforms, the generated code changes to:
`typescript
import { Reader, Writer } from "protobufjs/minimal.js";
import { Vector3 } from "@nativewrappers/common";
import { Vehicle } from "@nativewrappers/redm";
export const ProtoVec3 = {
name: "ProtoVec3" as const,
encode: (msg: Vec3): Uint8Array => { ... },
decode: (bytes: Uint8Array): Vector3 => {
// ... decodes to raw Vec3, then:
return new Vector3(message.x, message.y, message.z);
},
};
export const ProtoNetVehicle = {
name: "ProtoNetVehicle" as const,
encode: (msg: NetVehicle): Uint8Array => { ... },
decode: (bytes: Uint8Array): Vehicle | null => {
// ... decodes to raw NetVehicle, then:
return Vehicle.fromNetId(message.net_id);
},
};
`
For projects needing .d.ts declarations:
`json``
{
"scripts": {
"build": "node build.js && tsc --emitDeclarationOnly"
}
}
| Proto Type | TypeScript Type |
|------------|-----------------|
| double, float | number |
| int32, uint32, sint32, fixed32, sfixed32 | number |
| int64, uint64, sint64, fixed64, sfixed64 | bigint |
| bool | boolean |
| string | string |
| bytes | Uint8Array |
| enum | enum |
| message | type |
| repeated T | T[] |
This is free and unencumbered software released into the public domain. See LICENSE for details.