diff --git a/packages/protobuf-test/src/descriptor-set.test.ts b/packages/protobuf-test/src/descriptor-set.test.ts index 4974baf31..717d16f5e 100644 --- a/packages/protobuf-test/src/descriptor-set.test.ts +++ b/packages/protobuf-test/src/descriptor-set.test.ts @@ -29,6 +29,7 @@ import { TestAllExtensions, TestNestedExtension, } from "./gen/ts/google/protobuf/unittest_pb.js"; +import { UpstreamProtobuf } from "upstream-protobuf"; const fdsBytes = readFileSync("./descriptorset.bin"); @@ -54,6 +55,50 @@ describe("DescriptorSet", () => { expect(descFile?.syntax).toBe("editions"); expect(descFile?.edition).toBe(Edition.EDITION_2023); }); + describe("repeated field packing", () => { + test("proto2 is unpacked by default", async () => { + const bin = await new UpstreamProtobuf().compileToDescriptorSet(` + syntax="proto2"; + message M { + optional int32 not_packable = 1; + repeated bytes also_not_packable = 2; + repeated int32 default = 3; + repeated int32 explicitly_packed = 4 [packed = true]; + repeated int32 explicitly_expanded = 5 [packed = false]; + } + `); + const fields = createDescriptorSet(bin).messages.get("M")?.fields; + expect(fields?.[0].packedByDefault).toBe(false); + expect(fields?.[1].packedByDefault).toBe(false); + expect(fields?.[2].packedByDefault).toBe(false); + expect(fields?.[0].packed).toBe(false); + expect(fields?.[1].packed).toBe(false); + expect(fields?.[2].packed).toBe(false); + expect(fields?.[3].packed).toBe(true); + expect(fields?.[4].packed).toBe(false); + }); + test("proto3 is packed by default", async () => { + const bin = await new UpstreamProtobuf().compileToDescriptorSet(` + syntax="proto3"; + message M { + int32 not_packable = 1; + repeated bytes also_not_packable = 2; + repeated int32 default = 3; + repeated int32 explicitly_packed = 4 [packed = true]; + repeated int32 explicitly_expanded = 5 [packed = false]; + } + `); + const fields = createDescriptorSet(bin).messages.get("M")?.fields; + expect(fields?.[0].packedByDefault).toBe(true); + expect(fields?.[1].packedByDefault).toBe(false); + expect(fields?.[2].packedByDefault).toBe(true); + expect(fields?.[0].packed).toBe(true); + expect(fields?.[1].packed).toBe(false); + expect(fields?.[2].packed).toBe(true); + expect(fields?.[3].packed).toBe(true); + expect(fields?.[4].packed).toBe(false); + }); + }); test("knows extension", () => { const ext = set.extensions.get( "protobuf_unittest.optional_int32_extension", diff --git a/packages/upstream-protobuf/index.d.ts b/packages/upstream-protobuf/index.d.ts index 0d0e71577..861cdef2d 100644 --- a/packages/upstream-protobuf/index.d.ts +++ b/packages/upstream-protobuf/index.d.ts @@ -22,5 +22,16 @@ export declare class UpstreamProtobuf { maximumEdition?: string, ): Promise; - compileToDescriptorSet(files: Record): Promise; + compileToDescriptorSet( + fileContent: string, + opt?: CompileToDescriptorSetOptions, + ): Promise; + compileToDescriptorSet( + files: Record, + opt?: CompileToDescriptorSetOptions, + ): Promise; +} + +interface CompileToDescriptorSetOptions { + includeSourceInfo?: boolean; } diff --git a/packages/upstream-protobuf/index.mjs b/packages/upstream-protobuf/index.mjs index 6b21f66e9..212ceaafd 100644 --- a/packages/upstream-protobuf/index.mjs +++ b/packages/upstream-protobuf/index.mjs @@ -121,31 +121,40 @@ export class UpstreamProtobuf { } /** - * @param {Record} files + * @typedef CompileToDescriptorSetOptions + * @property {boolean} [includeSourceInfo] + */ + /** + * @param {Record|string} filesOrFileContent + * @param {CompileToDescriptorSetOptions} [opt] * @return {Promise} */ - async compileToDescriptorSet(files) { + async compileToDescriptorSet(filesOrFileContent, opt) { const protocPath = await this.getProtocPath(); const tempDir = mkdtempSync( joinPath(this.#temp, "compile-descriptor-set-"), ); + const files = + typeof filesOrFileContent == "string" + ? { "input.proto": filesOrFileContent } + : filesOrFileContent; try { writeTree(Object.entries(files), tempDir); const outPath = joinPath(tempDir, "desc.bin"); - execFileSync( - protocPath, - [ - "--descriptor_set_out", - outPath, - "--proto_path", - tempDir, - ...Object.keys(files), - ], - { - shell: false, - stdio: "ignore", - }, - ); + const args = [ + "--experimental_editions", + "--descriptor_set_out", + outPath, + "--proto_path", + tempDir, + ...Object.keys(files), + ]; + if (opt?.includeSourceInfo) { + args.unshift("--include_source_info"); + } + execFileSync(protocPath, args, { + shell: false, + }); return readFileSync(outPath); } finally { rmSync(tempDir, { recursive: true });