Skip to content

Commit

Permalink
Support the jstype field option (#592)
Browse files Browse the repository at this point in the history
  • Loading branch information
timostamm authored Oct 24, 2023
1 parent 12974f6 commit 31371a4
Show file tree
Hide file tree
Showing 31 changed files with 2,238 additions and 57 deletions.
19 changes: 13 additions & 6 deletions docs/generated_code.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,13 +162,20 @@ as `optional`. Protobuf types map to ECMAScript types as follows:

### 64-bit integral types

We use the `bigint` primitive to represent 64-bit integral types. If bigint
is unavailable, we fall back to a string representation, which means that
all values typed as `bigint` will actually be strings.
We use the `BigInt` primitive to represent 64-bit integral types. `BigInt` has
been available in all major runtimes since 2020.

For presentation purposes, it is always safe to simply call `toString()` on
the field value. For more detailed information, see the conversion utility
[`protoInt64`][src-proto-int64] provided by [@bufbuild/protobuf][pkg-protobuf].
If you prefer to avoid `BigInt` in generated code, you can set the field option
`jstype = JS_STRING` to generate `String` instead:

```protobuf
int64 my_field = 1 [jstype = JS_STRING]; // will generate `myField: string`
```

If `BigInt` is unavailable in your environment, Protobuf-ES falls back to the
string representation. This means all values typed as `bigint` will be a `string`
at runtime. For detailed information on how to handle both variants, see the
conversion utility [`protoInt64`][src-proto-int64] provided by [@bufbuild/protobuf][pkg-protobuf].


### Message fields
Expand Down
2 changes: 1 addition & 1 deletion packages/protobuf-bench/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ server would usually do.

| code generator | bundle size | minified | compressed |
|---------------------|------------------------:|-----------------------:|-------------------:|
| protobuf-es | 89,648 b | 37,862 b | 9,699 b |
| protobuf-es | 90,449 b | 38,223 b | 9,818 b |
| protobuf-javascript | 394,384 b | 288,653 b | 45,140 b |
68 changes: 68 additions & 0 deletions packages/protobuf-test/extra/jstype-proto2.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright 2021-2023 Buf Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto2";
package spec;

message JSTypeProto2OmittedMessage {
optional fixed64 fixed64_field = 1;
optional int64 int64_field = 3;
optional sfixed64 sfixed64_field = 4;
optional sint64 sint64_field = 5;
optional uint64 uint64_field = 6;
repeated fixed64 repeated_fixed64_field = 11;
repeated int64 repeated_int64_field = 12;
repeated sfixed64 repeated_sfixed64_field = 13;
repeated sint64 repeated_sint64_field = 14;
repeated uint64 repeated_uint64_field = 15;
}

message JSTypeProto2NormalMessage {
optional fixed64 fixed64_field = 1 [jstype = JS_NORMAL];
optional int64 int64_field = 3 [jstype = JS_NORMAL];
optional sfixed64 sfixed64_field = 4 [jstype = JS_NORMAL];
optional sint64 sint64_field = 5 [jstype = JS_NORMAL];
optional uint64 uint64_field = 6 [jstype = JS_NORMAL];
repeated fixed64 repeated_fixed64_field = 11 [jstype = JS_NORMAL];
repeated int64 repeated_int64_field = 12 [jstype = JS_NORMAL];
repeated sfixed64 repeated_sfixed64_field = 13 [jstype = JS_NORMAL];
repeated sint64 repeated_sint64_field = 14 [jstype = JS_NORMAL];
repeated uint64 repeated_uint64_field = 15 [jstype = JS_NORMAL];
}

message JSTypeProto2StringMessage {
optional fixed64 fixed64_field = 1 [jstype = JS_STRING];
optional int64 int64_field = 3 [jstype = JS_STRING];
optional sfixed64 sfixed64_field = 4 [jstype = JS_STRING];
optional sint64 sint64_field = 5 [jstype = JS_STRING];
optional uint64 uint64_field = 6 [jstype = JS_STRING];
repeated fixed64 repeated_fixed64_field = 11 [jstype = JS_STRING];
repeated int64 repeated_int64_field = 12 [jstype = JS_STRING];
repeated sfixed64 repeated_sfixed64_field = 13 [jstype = JS_STRING];
repeated sint64 repeated_sint64_field = 14 [jstype = JS_STRING];
repeated uint64 repeated_uint64_field = 15 [jstype = JS_STRING];
}

message JSTypeProto2NumberMessage {
optional fixed64 fixed64_field = 1 [jstype = JS_NUMBER];
optional int64 int64_field = 3 [jstype = JS_NUMBER];
optional sfixed64 sfixed64_field = 4 [jstype = JS_NUMBER];
optional sint64 sint64_field = 5 [jstype = JS_NUMBER];
optional uint64 uint64_field = 6 [jstype = JS_NUMBER];
repeated fixed64 repeated_fixed64_field = 11 [jstype = JS_NUMBER];
repeated int64 repeated_int64_field = 12 [jstype = JS_NUMBER];
repeated sfixed64 repeated_sfixed64_field = 13 [jstype = JS_NUMBER];
repeated sint64 repeated_sint64_field = 14 [jstype = JS_NUMBER];
repeated uint64 repeated_uint64_field = 15 [jstype = JS_NUMBER];
}
68 changes: 68 additions & 0 deletions packages/protobuf-test/extra/jstype.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright 2021-2023 Buf Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto3";
package spec;

message JSTypeOmittedMessage {
fixed64 fixed64_field = 1;
int64 int64_field = 3;
sfixed64 sfixed64_field = 4;
sint64 sint64_field = 5;
uint64 uint64_field = 6;
repeated fixed64 repeated_fixed64_field = 11;
repeated int64 repeated_int64_field = 12;
repeated sfixed64 repeated_sfixed64_field = 13;
repeated sint64 repeated_sint64_field = 14;
repeated uint64 repeated_uint64_field = 15;
}

message JSTypeNormalMessage {
fixed64 fixed64_field = 1 [jstype = JS_NORMAL];
int64 int64_field = 3 [jstype = JS_NORMAL];
sfixed64 sfixed64_field = 4 [jstype = JS_NORMAL];
sint64 sint64_field = 5 [jstype = JS_NORMAL];
uint64 uint64_field = 6 [jstype = JS_NORMAL];
repeated fixed64 repeated_fixed64_field = 11 [jstype = JS_NORMAL];
repeated int64 repeated_int64_field = 12 [jstype = JS_NORMAL];
repeated sfixed64 repeated_sfixed64_field = 13 [jstype = JS_NORMAL];
repeated sint64 repeated_sint64_field = 14 [jstype = JS_NORMAL];
repeated uint64 repeated_uint64_field = 15 [jstype = JS_NORMAL];
}

message JSTypeStringMessage {
fixed64 fixed64_field = 1 [jstype = JS_STRING];
int64 int64_field = 3 [jstype = JS_STRING];
sfixed64 sfixed64_field = 4 [jstype = JS_STRING];
sint64 sint64_field = 5 [jstype = JS_STRING];
uint64 uint64_field = 6 [jstype = JS_STRING];
repeated fixed64 repeated_fixed64_field = 11 [jstype = JS_STRING];
repeated int64 repeated_int64_field = 12 [jstype = JS_STRING];
repeated sfixed64 repeated_sfixed64_field = 13 [jstype = JS_STRING];
repeated sint64 repeated_sint64_field = 14 [jstype = JS_STRING];
repeated uint64 repeated_uint64_field = 15 [jstype = JS_STRING];
}

message JSTypeNumberMessage {
fixed64 fixed64_field = 1 [jstype = JS_NUMBER];
int64 int64_field = 3 [jstype = JS_NUMBER];
sfixed64 sfixed64_field = 4 [jstype = JS_NUMBER];
sint64 sint64_field = 5 [jstype = JS_NUMBER];
uint64 uint64_field = 6 [jstype = JS_NUMBER];
repeated fixed64 repeated_fixed64_field = 11 [jstype = JS_NUMBER];
repeated int64 repeated_int64_field = 12 [jstype = JS_NUMBER];
repeated sfixed64 repeated_sfixed64_field = 13 [jstype = JS_NUMBER];
repeated sint64 repeated_sint64_field = 14 [jstype = JS_NUMBER];
repeated uint64 repeated_uint64_field = 15 [jstype = JS_NUMBER];
}
25 changes: 25 additions & 0 deletions packages/protobuf-test/src/constructor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,33 @@ import {
TestAllTypesProto3_NestedMessage as TS_TestAllTypesProto3_NestedMessage,
} from "./gen/ts/google/protobuf/test_messages_proto3_pb.js";
import { TestAllTypesProto3 as JS_TestAllTypesProto3 } from "./gen/js/google/protobuf/test_messages_proto3_pb.js";
import { JSTypeStringMessage as TS_JSTypeStringMessage } from "./gen/ts/extra/jstype_pb.js";
import { JSTypeStringMessage as JS_JSTypeStringMessage } from "./gen/js/extra/jstype_pb.js";
import { testMT } from "./helpers.js";

describe("constructor initializes jstype=JS_STRING with string", function () {
testMT(
{ ts: TS_JSTypeStringMessage, js: JS_JSTypeStringMessage },
(messageType) => {
const m = new messageType({
fixed64Field: "123",
repeatedFixed64Field: ["123"],
});
expect(m.fixed64Field).toBe("123");
expect(m.int64Field).toBe("0");
expect(m.sfixed64Field).toBe("0");
expect(m.sint64Field).toBe("0");
expect(m.uint64Field).toBe("0");
expect(m.repeatedFixed64Field.length).toBe(1);
expect(m.repeatedFixed64Field).toStrictEqual(["123"]);
expect(m.repeatedInt64Field.length).toBe(0);
expect(m.repeatedSfixed64Field.length).toBe(0);
expect(m.repeatedSint64Field.length).toBe(0);
expect(m.repeatedUint64Field.length).toBe(0);
},
);
});

describe("constructor takes message partial for message field", function () {
testMT(
{ ts: TS_TestAllTypesProto3, js: JS_TestAllTypesProto3 },
Expand Down
90 changes: 90 additions & 0 deletions packages/protobuf-test/src/equals.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,99 @@ import { ScalarValuesMessage as TS_ScalarValuesMessage } from "./gen/ts/extra/ms
import { ScalarValuesMessage as JS_ScalarValuesMessage } from "./gen/js/extra/msg-scalar_pb.js";
import { OneofMessage as TS_OneofMessage } from "./gen/ts/extra/msg-oneof_pb.js";
import { OneofMessage as JS_OneofMessage } from "./gen/js/extra/msg-oneof_pb.js";
import { JSTypeStringMessage as TS_JSTypeStringMessage } from "./gen/ts/extra/jstype_pb.js";
import { JSTypeStringMessage as JS_JSTypeStringMessage } from "./gen/js/extra/jstype_pb.js";
import { JSTypeProto2StringMessage as TS_JSTypeProto2StringMessage } from "./gen/ts/extra/jstype-proto2_pb.js";
import { JSTypeProto2StringMessage as JS_JSTypeProto2StringMessage } from "./gen/js/extra/jstype-proto2_pb.js";
import { describeMT } from "./helpers.js";

describe("equals", function () {
describeMT(
{ ts: TS_JSTypeProto2StringMessage, js: JS_JSTypeProto2StringMessage },
(messageType) => {
let a: TS_JSTypeProto2StringMessage | JS_JSTypeProto2StringMessage,
b: TS_JSTypeProto2StringMessage | JS_JSTypeProto2StringMessage;
beforeEach(() => {
a = new messageType({
fixed64Field: "123",
int64Field: "123",
sfixed64Field: "123",
sint64Field: "123",
uint64Field: "123",
repeatedFixed64Field: ["123"],
repeatedInt64Field: ["123"],
repeatedSfixed64Field: ["123"],
repeatedSint64Field: ["123"],
repeatedUint64Field: ["123"],
});
b = new messageType({
fixed64Field: "123",
int64Field: "123",
sfixed64Field: "123",
sint64Field: "123",
uint64Field: "123",
repeatedFixed64Field: ["123"],
repeatedInt64Field: ["123"],
repeatedSfixed64Field: ["123"],
repeatedSint64Field: ["123"],
repeatedUint64Field: ["123"],
});
});
test("same are equal", () => {
expect(a).toStrictEqual(b);
expect(a.equals(b)).toBeTruthy();
});
test("changed are not equal", () => {
a.int64Field = undefined;
expect(a).not.toStrictEqual(b);
expect(a.equals(b)).toBeFalsy();
});
},
);

describeMT(
{ ts: TS_JSTypeStringMessage, js: JS_JSTypeStringMessage },
(messageType) => {
let a: TS_JSTypeStringMessage | JS_JSTypeStringMessage,
b: TS_JSTypeStringMessage | JS_JSTypeStringMessage;
beforeEach(() => {
a = new messageType({
fixed64Field: "123",
int64Field: "123",
sfixed64Field: "123",
sint64Field: "123",
uint64Field: "123",
repeatedFixed64Field: ["123"],
repeatedInt64Field: ["123"],
repeatedSfixed64Field: ["123"],
repeatedSint64Field: ["123"],
repeatedUint64Field: ["123"],
});
b = new messageType({
fixed64Field: "123",
int64Field: "123",
sfixed64Field: "123",
sint64Field: "123",
uint64Field: "123",
repeatedFixed64Field: ["123"],
repeatedInt64Field: ["123"],
repeatedSfixed64Field: ["123"],
repeatedSint64Field: ["123"],
repeatedUint64Field: ["123"],
});
});
test("same are equal", () => {
expect(a).toStrictEqual(b);
expect(a.equals(b)).toBeTruthy();
});
test("changed are not equal", () => {
a.int64Field = "456";
expect(a).not.toStrictEqual(b);
expect(a.equals(b)).toBeFalsy();
});
},
);

describeMT({ ts: TS_OneofMessage, js: JS_OneofMessage }, (messageType) => {
test("oneof scalars are equal", () => {
const a = new messageType({ scalar: { case: "value", value: 1 } });
Expand Down
Loading

0 comments on commit 31371a4

Please sign in to comment.