diff --git a/README.md b/README.md index ee18b957c..a6a5a5288 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/docs/generated_code.md b/docs/generated_code.md index ed8932ae6..547162f86 100644 --- a/docs/generated_code.md +++ b/docs/generated_code.md @@ -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 diff --git a/docs/runtime_api.md b/docs/runtime_api.md index 4c9d80b54..63fd90dca 100644 --- a/docs/runtime_api.md +++ b/docs/runtime_api.md @@ -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"); ``` diff --git a/packages/protobuf-test/src/descriptor-registry.test.ts b/packages/protobuf-test/src/descriptor-registry.test.ts index 626a966d2..482a351ab 100644 --- a/packages/protobuf-test/src/descriptor-registry.test.ts +++ b/packages/protobuf-test/src/descriptor-registry.test.ts @@ -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", () => { @@ -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); }); }); diff --git a/packages/protobuf/src/descriptor-registry.ts b/packages/protobuf/src/descriptor-registry.ts index fe06e0828..4bfeb9091 100644 --- a/packages/protobuf/src/descriptor-registry.ts +++ b/packages/protobuf/src/descriptor-registry.ts @@ -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 = [ @@ -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 + ): 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. */ diff --git a/private/protoplugin/protoplugin.go b/private/protoplugin/protoplugin.go index 8c5b8b621..33ce2c375 100644 --- a/private/protoplugin/protoplugin.go +++ b/private/protoplugin/protoplugin.go @@ -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