Skip to content

Commit

Permalink
Improve DescriptorRegistry (#77)
Browse files Browse the repository at this point in the history
  • Loading branch information
timostamm authored May 6, 2022
1 parent adeff25 commit 8d8d28d
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 19 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ for the [generated code](docs/generated_code.md), and the documentation for the

### How does this compare to protoc's JavaScript generator?

[`js_generator.cc`](https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/compiler/js/js_generator.cc)
[`js_generator.cc`](https://github.com/protocolbuffers/protobuf-javascript/blob/main/generator/js_generator.cc)
is rarely updated, and has fallen behind the quickly moving world of JavaScript.

For example:
Expand Down
5 changes: 3 additions & 2 deletions docs/generated_code.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,9 @@ Note that all map fields will have an empty object as a default value.

While it is not a perfectly clear-cut case, we chose to represent map fields
as plain objects instead of [ECMAScript map objects](https://tc39.es/ecma262/multipage/keyed-collections.html#sec-map-objects).
While `Map` has some benefits like better behavior around keys, they don't
have a literal representation and do not support the spread operator.
While `Map` has better behavior around keys, they do not have a literal
representation, do not support the spread operator and type narrowing in
TypeScript.


### Oneof groups
Expand Down
10 changes: 4 additions & 6 deletions docs/runtime_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -275,10 +275,8 @@ buf generate --output image.bin
```
```typescript
const fds = FileDescriptorSet.fromBinary(readFileSync("image.bin"));
const dr = new DescriptorRegistry();
for (const fd of fds.file) {
dr.add(fd);
}
const Example = dr.findMessage("doc.Example");
const registry = new DescriptorRegistry.fromFileDescriptorSet(
readFileSync("image.bin")
);
const Example = registry.findMessage("doc.Example");
```
27 changes: 18 additions & 9 deletions packages/protobuf-test/src/descriptor-registry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import {
import { TestAllTypes } from "./gen/ts/google/protobuf/unittest_proto3_pb.js";
import { assertMessageTypeEquals } from "./helpers.js";

const fds = FileDescriptorSet.fromBinary(readFileSync("./descriptorset.bin"));
const fdsBytes = readFileSync("./descriptorset.bin");
const fds = FileDescriptorSet.fromBinary(fdsBytes);

describe("DescriptorSet", () => {
test("add() does not crash", () => {
Expand All @@ -39,14 +40,22 @@ describe("DescriptorRegistry", () => {
expect(dr.findMessage("foo.Foo")).toBeUndefined();
expect(dr.findEnum("foo.Foo")).toBeUndefined();
});
test("dynamic type equals generated type", () => {
const dr = new DescriptorRegistry();
dr.add(...fds.file);
const messageTypeDyn = dr.findMessage(TestAllTypes.typeName);
expect(messageTypeDyn).toBeDefined();
if (!messageTypeDyn) {
return;
test("fromDescriptorSet with instance", () => {
const dr = DescriptorRegistry.fromFileDescriptorSet(fds);
expect(dr.findEnum("foo.Foo")).toBeUndefined();
const mt = dr.findMessage(TestAllTypes.typeName);
expect(mt).toBeDefined();
if (mt) {
assertMessageTypeEquals(mt, TestAllTypes);
}
});
test("fromDescriptorSet with bytes", () => {
const dr = DescriptorRegistry.fromFileDescriptorSet(fdsBytes);
expect(dr.findEnum("foo.Foo")).toBeUndefined();
const mt = dr.findMessage(TestAllTypes.typeName);
expect(mt).toBeDefined();
if (mt) {
assertMessageTypeEquals(mt, TestAllTypes);
}
assertMessageTypeEquals(messageTypeDyn, TestAllTypes);
});
});
21 changes: 21 additions & 0 deletions packages/protobuf/src/descriptor-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ import {
UInt32Value,
UInt64Value,
} from "./google/protobuf/wrappers_pb.js";
import { FileDescriptorSet } from "./google/protobuf/descriptor_pb.js";
import type { PartialMessage } from "./message.js";

// well-known message types with specialized JSON representation
const wkMessages = [
Expand Down Expand Up @@ -106,6 +108,25 @@ export class DescriptorRegistry
}
}

/**
* Conveniently create a DescriptorRegistry from a FileDescriptorSet
* instance or a FileDescriptorSet in binary format.
*/
static fromFileDescriptorSet(
bytesOrSet:
| Uint8Array
| FileDescriptorSet
| PartialMessage<FileDescriptorSet>
): DescriptorRegistry {
const set =
bytesOrSet instanceof Uint8Array
? FileDescriptorSet.fromBinary(bytesOrSet)
: new FileDescriptorSet(bytesOrSet);
const dr = new DescriptorRegistry();
dr.add(...set.file);
return dr;
}

/**
* May raise an error on invalid descriptors.
*/
Expand Down
2 changes: 1 addition & 1 deletion private/protoplugin/protoplugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ type Message struct {
File *File
Proto *descriptorpb.DescriptorProto
TypeName string // fully qualified name, for example `foo.MyMessage`
Symbol *Symbol // importable of this message in ECMAScript
Symbol *Symbol // importable name of this message in ECMAScript
Members []*Member
Fields []*Field
Oneofs []*Oneof // excluding synthetic oneofs for proto3 optional
Expand Down

0 comments on commit 8d8d28d

Please sign in to comment.