0
# Schema Compilation
1
2
Compile protocol buffer schema files (.proto) into optimized JavaScript read/write functions. The compilation system generates highly optimized code for encoding and decoding specific protobuf schemas.
3
4
## Compilation Functions
5
6
### Runtime Compilation
7
8
Compile a parsed protocol buffer schema into executable functions:
9
10
```typescript { .api }
11
/**
12
* Compile a protobuf schema into read/write functions
13
* @param proto - Parsed protobuf schema object
14
* @returns Object containing generated read and write functions
15
*/
16
function compile(proto: object): Record<string, Function>;
17
```
18
19
### Code Generation
20
21
Generate raw JavaScript/TypeScript code from a protobuf schema:
22
23
```typescript { .api }
24
interface CompileOptions {
25
dev?: boolean; // Include development features and comments
26
legacy?: boolean; // Generate CommonJS instead of ESM
27
noRead?: boolean; // Skip generating read functions
28
noWrite?: boolean; // Skip generating write functions
29
}
30
31
/**
32
* Compile a protobuf schema into raw JavaScript code
33
* @param proto - Parsed protobuf schema object
34
* @param options - Compilation options
35
* @returns Generated JavaScript code as string
36
*/
37
function compileRaw(proto: object, options?: CompileOptions): string;
38
```
39
40
## Schema Input Format
41
42
The compilation functions expect a parsed protobuf schema object (typically from `resolve-protobuf-schema`):
43
44
```typescript { .api }
45
interface ProtoSchema {
46
name?: string;
47
syntax?: number; // Protocol buffer version (2 or 3)
48
fields?: ProtoField[];
49
messages?: ProtoSchema[];
50
enums?: ProtoEnum[];
51
extensions?: any;
52
}
53
54
interface ProtoField {
55
name: string;
56
type: string;
57
tag: number;
58
repeated?: boolean;
59
required?: boolean;
60
optional?: boolean;
61
oneof?: string;
62
map?: {
63
from: string; // Key type
64
to: string; // Value type
65
};
66
options?: {
67
packed?: string; // "true" or "false"
68
default?: string;
69
jstype?: string; // "JS_STRING" for string representation
70
};
71
}
72
73
interface ProtoEnum {
74
name: string;
75
values: Record<string, { value: number }>;
76
}
77
```
78
79
## Generated Function Pattern
80
81
For each message type in the schema, the compiler generates corresponding read and write functions:
82
83
```typescript { .api }
84
// Generated read function signature
85
type ReadFunction<T> = (pbf: Pbf, end?: number) => T;
86
87
// Generated write function signature
88
type WriteFunction<T> = (obj: T, pbf: Pbf) => void;
89
90
// Example generated functions for a "Person" message
91
interface GeneratedFunctions {
92
readPerson: ReadFunction<Person>;
93
writePerson: WriteFunction<Person>;
94
}
95
```
96
97
## Usage Examples
98
99
### Basic Schema Compilation
100
101
```typescript
102
import { compile } from "pbf/compile";
103
import schema from "protocol-buffers-schema";
104
import fs from "fs";
105
106
// Parse .proto file
107
const proto = schema.parse(fs.readFileSync("person.proto"));
108
109
// Compile to functions
110
const { readPerson, writePerson } = compile(proto);
111
112
// Use generated functions
113
const person = readPerson(new Pbf(buffer));
114
const pbf = new Pbf();
115
writePerson(person, pbf);
116
const encoded = pbf.finish();
117
```
118
119
### Code Generation
120
121
```typescript
122
import { compileRaw } from "pbf/compile";
123
import schema from "protocol-buffers-schema";
124
import fs from "fs";
125
126
// Parse .proto file
127
const proto = schema.parse(fs.readFileSync("person.proto"));
128
129
// Generate JavaScript code
130
const jsCode = compileRaw(proto);
131
132
// Write to file
133
fs.writeFileSync("person.js", jsCode);
134
```
135
136
### Compilation Options
137
138
```typescript
139
import { compileRaw } from "pbf/compile";
140
141
// Generate CommonJS module with only write functions
142
const code = compileRaw(proto, {
143
legacy: true, // Use CommonJS exports
144
noRead: true, // Skip read functions
145
dev: false // Production mode
146
});
147
148
// Generate ESM module with development features
149
const devCode = compileRaw(proto, {
150
legacy: false, // Use ESM exports
151
dev: true // Include comments and debug info
152
});
153
```
154
155
### Working with Complex Types
156
157
```typescript
158
// Schema with nested messages and enums
159
const proto = {
160
name: "AddressBook",
161
fields: [
162
{ name: "people", type: "Person", tag: 1, repeated: true }
163
],
164
messages: [
165
{
166
name: "Person",
167
fields: [
168
{ name: "name", type: "string", tag: 1 },
169
{ name: "id", type: "int32", tag: 2 },
170
{ name: "email", type: "string", tag: 3 },
171
{ name: "phones", type: "PhoneNumber", tag: 4, repeated: true },
172
{ name: "type", type: "PhoneType", tag: 5 }
173
]
174
},
175
{
176
name: "PhoneNumber",
177
fields: [
178
{ name: "number", type: "string", tag: 1 },
179
{ name: "type", type: "PhoneType", tag: 2 }
180
]
181
}
182
],
183
enums: [
184
{
185
name: "PhoneType",
186
values: {
187
MOBILE: { value: 0 },
188
HOME: { value: 1 },
189
WORK: { value: 2 }
190
}
191
}
192
]
193
};
194
195
const { readAddressBook, writeAddressBook, readPerson, writePerson, PhoneType } = compile(proto);
196
```
197
198
### Map Field Support
199
200
```typescript
201
// Schema with map fields
202
const proto = {
203
name: "MapExample",
204
fields: [
205
{
206
name: "attributes",
207
type: "map",
208
tag: 1,
209
map: { from: "string", to: "string" }
210
},
211
{
212
name: "counters",
213
type: "map",
214
tag: 2,
215
map: { from: "string", to: "int32" }
216
}
217
]
218
};
219
220
const { readMapExample, writeMapExample } = compile(proto);
221
222
// Usage
223
const data = {
224
attributes: { "color": "red", "size": "large" },
225
counters: { "views": 100, "likes": 25 }
226
};
227
```
228
229
### Packed Fields and Proto3
230
231
```typescript
232
// Proto3 schema with packed repeated fields
233
const proto = {
234
name: "PackedExample",
235
syntax: 3, // Proto3
236
fields: [
237
{
238
name: "numbers",
239
type: "int32",
240
tag: 1,
241
repeated: true,
242
options: { packed: "true" }
243
},
244
{
245
name: "flags",
246
type: "bool",
247
tag: 2,
248
repeated: true,
249
options: { packed: "true" }
250
}
251
]
252
};
253
254
const { readPackedExample, writePackedExample } = compile(proto);
255
```
256
257
### OneOf Fields
258
259
```typescript
260
// Schema with oneof fields
261
const proto = {
262
name: "OneOfExample",
263
fields: [
264
{ name: "name", type: "string", tag: 1 },
265
{ name: "email", type: "string", tag: 2, oneof: "contact" },
266
{ name: "phone", type: "string", tag: 3, oneof: "contact" }
267
]
268
};
269
270
const { readOneOfExample, writeOneOfExample } = compile(proto);
271
272
// Usage - only one field in the oneof group will be set
273
const data1 = { name: "John", email: "john@example.com", contact: "email" };
274
const data2 = { name: "Jane", phone: "555-1234", contact: "phone" };
275
```
276
277
## Error Handling
278
279
The compilation functions may throw errors for invalid schemas:
280
281
```typescript
282
try {
283
const functions = compile(proto);
284
} catch (error) {
285
// Handle compilation errors:
286
// - "Unexpected type: [type]" for unsupported types
287
// - Schema validation errors
288
// - Missing field type definitions
289
console.error("Compilation failed:", error.message);
290
}
291
```