@bufbuild/protoc-gen-es 1.7.1 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -118,7 +118,6 @@ Possible values:
118
118
  the default behavior.
119
119
  - `js_import_style=legacy_commonjs` generate CommonJS `require()` calls.
120
120
 
121
-
122
121
  ### `keep_empty_files=true`
123
122
 
124
123
  By default, [protoc-gen-es](https://www.npmjs.com/package/@bufbuild/protoc-gen-es)
@@ -127,3 +126,14 @@ omit empty files from the plugin output. This option disables pruning of
127
126
  empty files, to allow for smooth interoperation with Bazel and similar
128
127
  tooling that requires all output files to be declared ahead of time.
129
128
  Unless you use Bazel, it is very unlikely that you need this option.
129
+
130
+ ### `ts_nocheck=false`
131
+
132
+ By default, [protoc-gen-es](https://www.npmjs.com/package/@bufbuild/protoc-gen-es)
133
+ (and all other plugins based on [@bufbuild/protoplugin](https://www.npmjs.com/package/@bufbuild/protoplugin))
134
+ generate an annotation at the top of each file: `// @ts-nocheck`.
135
+
136
+ We generate the annotation to support a wide range of compiler configurations and
137
+ future changes to the language. But there can be situations where the annotation
138
+ shadows an underlying problem, for example an unresolvable import. To remove
139
+ the annotation and to enable type checks, set the plugin option `ts_nocheck=false`.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bufbuild/protoc-gen-es",
3
- "version": "1.7.1",
3
+ "version": "1.8.0",
4
4
  "description": "Protocol Buffers code generator for ECMAScript",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -20,11 +20,11 @@
20
20
  },
21
21
  "preferUnplugged": true,
22
22
  "dependencies": {
23
- "@bufbuild/protobuf": "^1.7.1",
24
- "@bufbuild/protoplugin": "1.7.1"
23
+ "@bufbuild/protobuf": "^1.8.0",
24
+ "@bufbuild/protoplugin": "1.8.0"
25
25
  },
26
26
  "peerDependencies": {
27
- "@bufbuild/protobuf": "1.7.1"
27
+ "@bufbuild/protobuf": "1.8.0"
28
28
  },
29
29
  "peerDependenciesMeta": {
30
30
  "@bufbuild/protobuf": {
@@ -16,6 +16,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
16
16
  exports.generateDts = void 0;
17
17
  const ecmascript_1 = require("@bufbuild/protoplugin/ecmascript");
18
18
  const editions_js_1 = require("./editions.js");
19
+ const util_js_1 = require("./util.js");
19
20
  function generateDts(schema) {
20
21
  for (const file of schema.files) {
21
22
  const f = schema.generateFile(file.name + "_pb.d.ts");
@@ -102,29 +103,28 @@ function generateOneof(schema, f, oneof) {
102
103
  f.print(` } | {`);
103
104
  }
104
105
  f.print(f.jsDoc(field, " "));
105
- const { typing } = (0, ecmascript_1.getFieldTyping)(field, f);
106
+ const { typing } = (0, util_js_1.getFieldTypeInfo)(field);
106
107
  f.print(` value: `, typing, `;`);
107
108
  f.print(` case: "`, (0, ecmascript_1.localName)(field), `";`);
108
109
  }
109
110
  f.print(` } | { case: undefined; value?: undefined };`);
110
111
  }
112
+ // prettier-ignore
111
113
  function generateField(schema, f, field) {
112
114
  f.print(f.jsDoc(field, " "));
113
115
  const e = [];
114
- e.push(" ", (0, ecmascript_1.localName)(field));
115
- const { typing, optional } = (0, ecmascript_1.getFieldTyping)(field, f);
116
- if (optional) {
117
- e.push("?: ", typing);
116
+ const { typing, optional } = (0, util_js_1.getFieldTypeInfo)(field);
117
+ if (!optional) {
118
+ e.push(" ", (0, ecmascript_1.localName)(field), ": ", typing, ";");
118
119
  }
119
120
  else {
120
- e.push(": ", typing);
121
+ e.push(" ", (0, ecmascript_1.localName)(field), "?: ", typing, ";");
121
122
  }
122
- e.push(";");
123
123
  f.print(e);
124
124
  }
125
125
  // prettier-ignore
126
126
  function generateExtension(schema, f, ext) {
127
- const { typing } = (0, ecmascript_1.getFieldTyping)(ext, f);
127
+ const { typing } = (0, util_js_1.getFieldTypeInfo)(ext);
128
128
  f.print(f.jsDoc(ext));
129
129
  f.print(f.exportDecl("declare const", ext), ": ", schema.runtime.Extension, "<", ext.extendee, ", ", typing, ">;");
130
130
  f.print();
@@ -198,7 +198,7 @@ function generateWktStaticMethods(schema, f, message) {
198
198
  case "google.protobuf.BoolValue":
199
199
  case "google.protobuf.StringValue":
200
200
  case "google.protobuf.BytesValue": {
201
- const { typing } = (0, ecmascript_1.getFieldTyping)(ref.value, f);
201
+ const { typing } = (0, util_js_1.getFieldTypeInfo)(ref.value);
202
202
  f.print(" static readonly fieldWrapper: {");
203
203
  f.print(" wrapField(value: ", typing, "): ", message, ",");
204
204
  f.print(" unwrapField(value: ", message, "): ", typing, ",");
@@ -15,8 +15,10 @@
15
15
  Object.defineProperty(exports, "__esModule", { value: true });
16
16
  exports.getFieldInfoLiteral = exports.generateFieldInfo = exports.generateJs = void 0;
17
17
  const protobuf_1 = require("@bufbuild/protobuf");
18
+ const protobuf_2 = require("@bufbuild/protobuf");
18
19
  const ecmascript_1 = require("@bufbuild/protoplugin/ecmascript");
19
20
  const editions_js_1 = require("./editions.js");
21
+ const util_js_1 = require("./util.js");
20
22
  function generateJs(schema) {
21
23
  for (const file of schema.files) {
22
24
  const f = schema.generateFile(file.name + "_pb.js");
@@ -38,7 +40,7 @@ exports.generateJs = generateJs;
38
40
  function generateEnum(schema, f, enumeration) {
39
41
  const protoN = (0, editions_js_1.getNonEditionRuntime)(schema, enumeration.file);
40
42
  f.print(f.jsDoc(enumeration));
41
- f.print(f.exportDecl("const", enumeration), " = ", protoN, ".makeEnum(");
43
+ f.print(f.exportDecl("const", enumeration), " = /*@__PURE__*/ ", protoN, ".makeEnum(");
42
44
  f.print(` "`, enumeration.typeName, `",`);
43
45
  f.print(` [`);
44
46
  if (enumeration.sharedPrefix === undefined) {
@@ -60,7 +62,7 @@ function generateEnum(schema, f, enumeration) {
60
62
  function generateMessage(schema, f, message) {
61
63
  const protoN = (0, editions_js_1.getNonEditionRuntime)(schema, message.file);
62
64
  f.print(f.jsDoc(message));
63
- f.print(f.exportDecl("const", message), " = ", protoN, ".makeMessageType(");
65
+ f.print(f.exportDecl("const", message), " = /*@__PURE__*/ ", protoN, ".makeMessageType(");
64
66
  f.print(` `, f.string(message.typeName), `,`);
65
67
  if (message.fields.length == 0) {
66
68
  f.print(" [],");
@@ -73,7 +75,7 @@ function generateMessage(schema, f, message) {
73
75
  f.print(" ],");
74
76
  }
75
77
  const needsLocalName = (0, ecmascript_1.localName)(message) !==
76
- (message.file.syntax == "proto3" ? protobuf_1.proto2 : protobuf_1.proto3)
78
+ (message.file.syntax == "proto3" ? protobuf_2.proto2 : protobuf_2.proto3)
77
79
  .makeMessageType(message.typeName, []).name;
78
80
  if (needsLocalName) {
79
81
  // local name is not inferrable from the type name, we need to provide it
@@ -111,16 +113,16 @@ function getFieldInfoLiteral(schema, field) {
111
113
  }
112
114
  switch (field.fieldKind) {
113
115
  case "scalar":
114
- e.push(`kind: "scalar", T: `, field.scalar, ` /* ScalarType.`, protobuf_1.ScalarType[field.scalar], ` */, `);
115
- if (field.longType != protobuf_1.LongType.BIGINT) {
116
- e.push(`L: `, field.longType, ` /* LongType.`, protobuf_1.LongType[field.longType], ` */, `);
116
+ e.push(`kind: "scalar", T: `, field.scalar, ` /* ScalarType.`, protobuf_2.ScalarType[field.scalar], ` */, `);
117
+ if (field.longType != protobuf_2.LongType.BIGINT) {
118
+ e.push(`L: `, field.longType, ` /* LongType.`, protobuf_2.LongType[field.longType], ` */, `);
117
119
  }
118
120
  break;
119
121
  case "map":
120
- e.push(`kind: "map", K: `, field.mapKey, ` /* ScalarType.`, protobuf_1.ScalarType[field.mapKey], ` */, `);
122
+ e.push(`kind: "map", K: `, field.mapKey, ` /* ScalarType.`, protobuf_2.ScalarType[field.mapKey], ` */, `);
121
123
  switch (field.mapValue.kind) {
122
124
  case "scalar":
123
- e.push(`V: {kind: "scalar", T: `, field.mapValue.scalar, ` /* ScalarType.`, protobuf_1.ScalarType[field.mapValue.scalar], ` */}, `);
125
+ e.push(`V: {kind: "scalar", T: `, field.mapValue.scalar, ` /* ScalarType.`, protobuf_2.ScalarType[field.mapValue.scalar], ` */}, `);
124
126
  break;
125
127
  case "message":
126
128
  e.push(`V: {kind: "message", T: `, field.mapValue.message, `}, `);
@@ -132,7 +134,7 @@ function getFieldInfoLiteral(schema, field) {
132
134
  break;
133
135
  case "message":
134
136
  e.push(`kind: "message", T: `, field.message, `, `);
135
- if (field.proto.type === protobuf_1.FieldDescriptorProto_Type.GROUP) {
137
+ if (field.proto.type === protobuf_2.FieldDescriptorProto_Type.GROUP) {
136
138
  e.push(`delimited: true, `);
137
139
  }
138
140
  break;
@@ -149,7 +151,10 @@ function getFieldInfoLiteral(schema, field) {
149
151
  if (field.optional) {
150
152
  e.push(`opt: true, `);
151
153
  }
152
- const defaultValue = (0, ecmascript_1.getFieldExplicitDefaultValue)(field, schema.runtime.protoInt64);
154
+ else if (field.proto.label === protobuf_1.FieldDescriptorProto_Label.REQUIRED) {
155
+ e.push(`req: true, `);
156
+ }
157
+ const defaultValue = (0, util_js_1.getFieldDefaultValueExpression)(field);
153
158
  if (defaultValue !== undefined) {
154
159
  e.push(`default: `, defaultValue, `, `);
155
160
  }
@@ -544,12 +549,12 @@ function generateWktMethods(schema, f, message) {
544
549
  case "google.protobuf.StringValue":
545
550
  case "google.protobuf.BytesValue":
546
551
  f.print(message, ".prototype.toJson = function toJson(options) {");
547
- f.print(" return proto3.json.writeScalar(", rtScalarType, ".", protobuf_1.ScalarType[ref.value.scalar], ", this.value, true);");
552
+ f.print(" return proto3.json.writeScalar(", rtScalarType, ".", protobuf_2.ScalarType[ref.value.scalar], ", this.value, true);");
548
553
  f.print("}");
549
554
  f.print();
550
555
  f.print(message, ".prototype.fromJson = function fromJson(json, options) {");
551
556
  f.print(" try {");
552
- f.print(" this.value = ", protoN, ".json.readScalar(", rtScalarType, ".", protobuf_1.ScalarType[ref.value.scalar], ", json);");
557
+ f.print(" this.value = ", protoN, ".json.readScalar(", rtScalarType, ".", protobuf_2.ScalarType[ref.value.scalar], ", json);");
553
558
  f.print(" } catch (e) {");
554
559
  f.print(" let m = `cannot decode message ", message.typeName, " from JSON\"`;");
555
560
  f.print(" if (e instanceof Error && e.message.length > 0) {");
@@ -18,6 +18,7 @@ const protobuf_1 = require("@bufbuild/protobuf");
18
18
  const ecmascript_1 = require("@bufbuild/protoplugin/ecmascript");
19
19
  const javascript_js_1 = require("./javascript.js");
20
20
  const editions_js_1 = require("./editions.js");
21
+ const util_js_1 = require("./util.js");
21
22
  function generateTs(schema) {
22
23
  for (const file of schema.files) {
23
24
  const f = schema.generateFile(file.name + "_pb.ts");
@@ -126,34 +127,39 @@ function generateOneof(schema, f, oneof) {
126
127
  f.print(` } | {`);
127
128
  }
128
129
  f.print(f.jsDoc(field, " "));
129
- const { typing } = (0, ecmascript_1.getFieldTyping)(field, f);
130
+ const { typing } = (0, util_js_1.getFieldTypeInfo)(field);
130
131
  f.print(` value: `, typing, `;`);
131
132
  f.print(` case: "`, (0, ecmascript_1.localName)(field), `";`);
132
133
  }
133
134
  f.print(` } | { case: undefined; value?: undefined } = { case: undefined };`);
134
135
  }
136
+ // prettier-ignore
135
137
  function generateField(schema, f, field) {
136
138
  f.print(f.jsDoc(field, " "));
137
139
  const e = [];
138
- e.push(" ", (0, ecmascript_1.localName)(field));
139
- const { defaultValue, typingInferrable } = (0, ecmascript_1.getFieldIntrinsicDefaultValue)(field);
140
- const { typing, optional } = (0, ecmascript_1.getFieldTyping)(field, f);
141
- if (optional || defaultValue === undefined) {
142
- e.push("?: ", typing);
143
- }
144
- else if (!typingInferrable) {
145
- e.push(": ", typing);
140
+ const { typing, optional, typingInferrableFromZeroValue } = (0, util_js_1.getFieldTypeInfo)(field);
141
+ if (optional) {
142
+ e.push(" ", (0, ecmascript_1.localName)(field), "?: ", typing, ";");
146
143
  }
147
- if (defaultValue !== undefined) {
148
- e.push(" = ", defaultValue);
144
+ else {
145
+ if (typingInferrableFromZeroValue) {
146
+ e.push(" ", (0, ecmascript_1.localName)(field));
147
+ }
148
+ else {
149
+ e.push(" ", (0, ecmascript_1.localName)(field), ": ", typing);
150
+ }
151
+ const zeroValue = (0, util_js_1.getFieldZeroValueExpression)(field);
152
+ if (zeroValue !== undefined) {
153
+ e.push(" = ", zeroValue);
154
+ }
155
+ e.push(";");
149
156
  }
150
- e.push(";");
151
157
  f.print(e);
152
158
  }
153
159
  // prettier-ignore
154
160
  function generateExtension(schema, f, ext) {
155
161
  const protoN = (0, editions_js_1.getNonEditionRuntime)(schema, ext.file);
156
- const { typing } = (0, ecmascript_1.getFieldTyping)(ext, f);
162
+ const { typing } = (0, util_js_1.getFieldTypeInfo)(ext);
157
163
  f.print(f.jsDoc(ext));
158
164
  f.print(f.exportDecl("const", ext), " = ", protoN, ".makeExtension<", ext.extendee, ", ", typing, ">(");
159
165
  f.print(" ", f.string(ext.typeName), ", ");
@@ -611,7 +617,7 @@ function generateWktStaticMethods(schema, f, message) {
611
617
  case "google.protobuf.BoolValue":
612
618
  case "google.protobuf.StringValue":
613
619
  case "google.protobuf.BytesValue": {
614
- const { typing } = (0, ecmascript_1.getFieldTyping)(ref.value, f);
620
+ const { typing } = (0, util_js_1.getFieldTypeInfo)(ref.value);
615
621
  f.print(" static readonly fieldWrapper = {");
616
622
  f.print(" wrapField(value: ", typing, "): ", message, " {");
617
623
  f.print(" return new ", message, "({value});");
@@ -0,0 +1,260 @@
1
+ "use strict";
2
+ // Copyright 2021-2024 Buf Technologies, Inc.
3
+ //
4
+ // Licensed under the Apache License, Version 2.0 (the "License");
5
+ // you may not use this file except in compliance with the License.
6
+ // You may obtain a copy of the License at
7
+ //
8
+ // http://www.apache.org/licenses/LICENSE-2.0
9
+ //
10
+ // Unless required by applicable law or agreed to in writing, software
11
+ // distributed under the License is distributed on an "AS IS" BASIS,
12
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ // See the License for the specific language governing permissions and
14
+ // limitations under the License.
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.getFieldZeroValueExpression = exports.getFieldDefaultValueExpression = exports.getFieldTypeInfo = void 0;
17
+ const protobuf_1 = require("@bufbuild/protobuf");
18
+ const ecmascript_1 = require("@bufbuild/protoplugin/ecmascript");
19
+ function getFieldTypeInfo(field) {
20
+ const typing = [];
21
+ let typingInferrableFromZeroValue;
22
+ let optional = false;
23
+ switch (field.fieldKind) {
24
+ case "scalar":
25
+ typing.push(scalarTypeScriptType(field.scalar, field.longType));
26
+ optional =
27
+ field.optional ||
28
+ field.proto.label === protobuf_1.FieldDescriptorProto_Label.REQUIRED;
29
+ typingInferrableFromZeroValue = true;
30
+ break;
31
+ case "message": {
32
+ const baseType = protobuf_1.codegenInfo.getUnwrappedFieldType(field);
33
+ if (baseType !== undefined) {
34
+ typing.push(scalarTypeScriptType(baseType, protobuf_1.LongType.BIGINT));
35
+ }
36
+ else {
37
+ typing.push({
38
+ kind: "es_ref_message",
39
+ type: field.message,
40
+ typeOnly: true,
41
+ });
42
+ }
43
+ optional = true;
44
+ typingInferrableFromZeroValue = true;
45
+ break;
46
+ }
47
+ case "enum":
48
+ typing.push({
49
+ kind: "es_ref_enum",
50
+ type: field.enum,
51
+ typeOnly: true,
52
+ });
53
+ optional =
54
+ field.optional ||
55
+ field.proto.label === protobuf_1.FieldDescriptorProto_Label.REQUIRED;
56
+ typingInferrableFromZeroValue = true;
57
+ break;
58
+ case "map": {
59
+ let keyType;
60
+ switch (field.mapKey) {
61
+ case protobuf_1.ScalarType.INT32:
62
+ case protobuf_1.ScalarType.FIXED32:
63
+ case protobuf_1.ScalarType.UINT32:
64
+ case protobuf_1.ScalarType.SFIXED32:
65
+ case protobuf_1.ScalarType.SINT32:
66
+ keyType = "number";
67
+ break;
68
+ default:
69
+ keyType = "string";
70
+ break;
71
+ }
72
+ let valueType;
73
+ switch (field.mapValue.kind) {
74
+ case "scalar":
75
+ valueType = scalarTypeScriptType(field.mapValue.scalar, protobuf_1.LongType.BIGINT);
76
+ break;
77
+ case "message":
78
+ valueType = {
79
+ kind: "es_ref_message",
80
+ type: field.mapValue.message,
81
+ typeOnly: true,
82
+ };
83
+ break;
84
+ case "enum":
85
+ valueType = {
86
+ kind: "es_ref_enum",
87
+ type: field.mapValue.enum,
88
+ typeOnly: true,
89
+ };
90
+ break;
91
+ }
92
+ typing.push("{ [key: ", keyType, "]: ", valueType, " }");
93
+ typingInferrableFromZeroValue = false;
94
+ optional = false;
95
+ break;
96
+ }
97
+ }
98
+ if (field.repeated) {
99
+ typing.push("[]");
100
+ optional = false;
101
+ typingInferrableFromZeroValue = false;
102
+ }
103
+ return { typing, optional, typingInferrableFromZeroValue };
104
+ }
105
+ exports.getFieldTypeInfo = getFieldTypeInfo;
106
+ /**
107
+ * Return a printable expression for the default value of a field.
108
+ * Only applicable for singular scalar and enum fields.
109
+ */
110
+ function getFieldDefaultValueExpression(field, enumAs = "enum_value_as_is") {
111
+ if (field.repeated) {
112
+ return undefined;
113
+ }
114
+ if (field.fieldKind !== "enum" && field.fieldKind !== "scalar") {
115
+ return undefined;
116
+ }
117
+ const defaultValue = field.getDefaultValue();
118
+ if (defaultValue === undefined) {
119
+ return undefined;
120
+ }
121
+ switch (field.fieldKind) {
122
+ case "enum": {
123
+ const enumValue = field.enum.values.find((value) => value.number === defaultValue);
124
+ if (enumValue === undefined) {
125
+ throw new Error(`invalid enum default value: ${String(defaultValue)} for ${enumValue}`);
126
+ }
127
+ return literalEnumValue(enumValue, enumAs);
128
+ }
129
+ case "scalar":
130
+ return literalScalarValue(defaultValue, field);
131
+ }
132
+ }
133
+ exports.getFieldDefaultValueExpression = getFieldDefaultValueExpression;
134
+ /**
135
+ * Return a printable expression for the zero value of a field.
136
+ *
137
+ * Returns either:
138
+ * - empty array literal for repeated fields
139
+ * - empty object literal for maps
140
+ * - undefined for message fields
141
+ * - an enums first value
142
+ * - scalar zero value
143
+ */
144
+ function getFieldZeroValueExpression(field, enumAs = "enum_value_as_is") {
145
+ if (field.repeated) {
146
+ return "[]";
147
+ }
148
+ switch (field.fieldKind) {
149
+ case "message":
150
+ return undefined;
151
+ case "map":
152
+ return "{}";
153
+ case "enum": {
154
+ // In proto3, the first enum value must be zero.
155
+ // In proto2, protobuf-go returns the first value as the default.
156
+ if (field.enum.values.length < 1) {
157
+ throw new Error("invalid enum: missing at least one value");
158
+ }
159
+ const zeroValue = field.enum.values[0];
160
+ return literalEnumValue(zeroValue, enumAs);
161
+ }
162
+ case "scalar": {
163
+ const defaultValue = protobuf_1.codegenInfo.scalarZeroValue(field.scalar, field.longType);
164
+ return literalScalarValue(defaultValue, field);
165
+ }
166
+ }
167
+ }
168
+ exports.getFieldZeroValueExpression = getFieldZeroValueExpression;
169
+ function literalScalarValue(value, field) {
170
+ switch (field.scalar) {
171
+ case protobuf_1.ScalarType.DOUBLE:
172
+ case protobuf_1.ScalarType.FLOAT:
173
+ case protobuf_1.ScalarType.INT32:
174
+ case protobuf_1.ScalarType.FIXED32:
175
+ case protobuf_1.ScalarType.UINT32:
176
+ case protobuf_1.ScalarType.SFIXED32:
177
+ case protobuf_1.ScalarType.SINT32:
178
+ if (typeof value != "number") {
179
+ throw new Error(`Unexpected value for ${protobuf_1.ScalarType[field.scalar]} ${field.toString()}: ${String(value)}`);
180
+ }
181
+ return value;
182
+ case protobuf_1.ScalarType.BOOL:
183
+ if (typeof value != "boolean") {
184
+ throw new Error(`Unexpected value for ${protobuf_1.ScalarType[field.scalar]} ${field.toString()}: ${String(value)}`);
185
+ }
186
+ return value;
187
+ case protobuf_1.ScalarType.STRING:
188
+ if (typeof value != "string") {
189
+ throw new Error(`Unexpected value for ${protobuf_1.ScalarType[field.scalar]} ${field.toString()}: ${String(value)}`);
190
+ }
191
+ return { kind: "es_string", value };
192
+ case protobuf_1.ScalarType.BYTES:
193
+ if (!(value instanceof Uint8Array)) {
194
+ throw new Error(`Unexpected value for ${protobuf_1.ScalarType[field.scalar]} ${field.toString()}: ${String(value)}`);
195
+ }
196
+ return value;
197
+ case protobuf_1.ScalarType.INT64:
198
+ case protobuf_1.ScalarType.SINT64:
199
+ case protobuf_1.ScalarType.SFIXED64:
200
+ case protobuf_1.ScalarType.UINT64:
201
+ case protobuf_1.ScalarType.FIXED64:
202
+ if (typeof value != "bigint" && typeof value != "string") {
203
+ throw new Error(`Unexpected value for ${protobuf_1.ScalarType[field.scalar]} ${field.toString()}: ${String(value)}`);
204
+ }
205
+ return {
206
+ kind: "es_proto_int64",
207
+ type: field.scalar,
208
+ longType: field.longType,
209
+ value,
210
+ };
211
+ }
212
+ }
213
+ function literalEnumValue(value, enumAs) {
214
+ switch (enumAs) {
215
+ case "enum_value_as_is":
216
+ return [
217
+ { kind: "es_ref_enum", type: value.parent, typeOnly: false },
218
+ ".",
219
+ (0, ecmascript_1.localName)(value),
220
+ ];
221
+ case "enum_value_as_integer":
222
+ return [
223
+ value.number,
224
+ " /* ",
225
+ value.parent.typeName,
226
+ ".",
227
+ value.name,
228
+ " */",
229
+ ];
230
+ case "enum_value_as_cast_integer":
231
+ return [
232
+ value.number,
233
+ " as ",
234
+ { kind: "es_ref_enum", type: value.parent, typeOnly: true },
235
+ ".",
236
+ (0, ecmascript_1.localName)(value),
237
+ ];
238
+ }
239
+ }
240
+ function scalarTypeScriptType(type, longType) {
241
+ switch (type) {
242
+ case protobuf_1.ScalarType.STRING:
243
+ return "string";
244
+ case protobuf_1.ScalarType.BOOL:
245
+ return "boolean";
246
+ case protobuf_1.ScalarType.UINT64:
247
+ case protobuf_1.ScalarType.SFIXED64:
248
+ case protobuf_1.ScalarType.FIXED64:
249
+ case protobuf_1.ScalarType.SINT64:
250
+ case protobuf_1.ScalarType.INT64:
251
+ if (longType === protobuf_1.LongType.STRING) {
252
+ return "string";
253
+ }
254
+ return "bigint";
255
+ case protobuf_1.ScalarType.BYTES:
256
+ return "Uint8Array";
257
+ default:
258
+ return "number";
259
+ }
260
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bufbuild/protoc-gen-es",
3
- "version": "1.7.1",
3
+ "version": "1.8.0",
4
4
  "description": "Protocol Buffers code generator for ECMAScript",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -20,11 +20,11 @@
20
20
  },
21
21
  "preferUnplugged": true,
22
22
  "dependencies": {
23
- "@bufbuild/protobuf": "^1.7.1",
24
- "@bufbuild/protoplugin": "1.7.1"
23
+ "@bufbuild/protobuf": "^1.8.0",
24
+ "@bufbuild/protoplugin": "1.8.0"
25
25
  },
26
26
  "peerDependencies": {
27
- "@bufbuild/protobuf": "1.7.1"
27
+ "@bufbuild/protobuf": "1.8.0"
28
28
  },
29
29
  "peerDependenciesMeta": {
30
30
  "@bufbuild/protobuf": {
@@ -24,12 +24,9 @@ import type {
24
24
  Printable,
25
25
  Schema,
26
26
  } from "@bufbuild/protoplugin/ecmascript";
27
- import {
28
- getFieldTyping,
29
- localName,
30
- reifyWkt,
31
- } from "@bufbuild/protoplugin/ecmascript";
27
+ import { localName, reifyWkt } from "@bufbuild/protoplugin/ecmascript";
32
28
  import { getNonEditionRuntime } from "./editions.js";
29
+ import { getFieldTypeInfo } from "./util.js";
33
30
 
34
31
  export function generateDts(schema: Schema) {
35
32
  for (const file of schema.files) {
@@ -127,25 +124,24 @@ function generateOneof(schema: Schema, f: GeneratedFile, oneof: DescOneof) {
127
124
  f.print(` } | {`);
128
125
  }
129
126
  f.print(f.jsDoc(field, " "));
130
- const { typing } = getFieldTyping(field, f);
127
+ const { typing } = getFieldTypeInfo(field);
131
128
  f.print(` value: `, typing, `;`);
132
129
  f.print(` case: "`, localName(field), `";`);
133
130
  }
134
131
  f.print(` } | { case: undefined; value?: undefined };`);
135
132
  }
136
133
 
134
+ // prettier-ignore
137
135
  function generateField(schema: Schema, f: GeneratedFile, field: DescField) {
138
- f.print(f.jsDoc(field, " "));
139
- const e: Printable = [];
140
- e.push(" ", localName(field));
141
- const { typing, optional } = getFieldTyping(field, f);
142
- if (optional) {
143
- e.push("?: ", typing);
144
- } else {
145
- e.push(": ", typing);
146
- }
147
- e.push(";");
148
- f.print(e);
136
+ f.print(f.jsDoc(field, " "));
137
+ const e: Printable = [];
138
+ const { typing, optional } = getFieldTypeInfo(field);
139
+ if (!optional) {
140
+ e.push(" ", localName(field), ": ", typing, ";");
141
+ } else {
142
+ e.push(" ", localName(field), "?: ", typing, ";");
143
+ }
144
+ f.print(e);
149
145
  }
150
146
 
151
147
  // prettier-ignore
@@ -154,7 +150,7 @@ function generateExtension(
154
150
  f: GeneratedFile,
155
151
  ext: DescExtension,
156
152
  ) {
157
- const { typing } = getFieldTyping(ext, f);
153
+ const { typing } = getFieldTypeInfo(ext);
158
154
  f.print(f.jsDoc(ext));
159
155
  f.print(f.exportDecl("declare const", ext), ": ", schema.runtime.Extension, "<", ext.extendee, ", ", typing, ">;");
160
156
  f.print();
@@ -234,7 +230,7 @@ function generateWktStaticMethods(schema: Schema, f: GeneratedFile, message: Des
234
230
  case "google.protobuf.BoolValue":
235
231
  case "google.protobuf.StringValue":
236
232
  case "google.protobuf.BytesValue": {
237
- const {typing} = getFieldTyping(ref.value, f);
233
+ const {typing} = getFieldTypeInfo(ref.value);
238
234
  f.print(" static readonly fieldWrapper: {")
239
235
  f.print(" wrapField(value: ", typing, "): ", message, ",")
240
236
  f.print(" unwrapField(value: ", message, "): ", typing, ",")
package/src/javascript.ts CHANGED
@@ -12,11 +12,12 @@
12
12
  // See the License for the specific language governing permissions and
13
13
  // limitations under the License.
14
14
 
15
- import type {
15
+ import {
16
16
  DescEnum,
17
17
  DescExtension,
18
18
  DescField,
19
19
  DescMessage,
20
+ FieldDescriptorProto_Label,
20
21
  } from "@bufbuild/protobuf";
21
22
  import {
22
23
  FieldDescriptorProto_Type,
@@ -30,12 +31,9 @@ import type {
30
31
  Printable,
31
32
  Schema,
32
33
  } from "@bufbuild/protoplugin/ecmascript";
33
- import {
34
- getFieldExplicitDefaultValue,
35
- localName,
36
- reifyWkt,
37
- } from "@bufbuild/protoplugin/ecmascript";
34
+ import { localName, reifyWkt } from "@bufbuild/protoplugin/ecmascript";
38
35
  import { getNonEditionRuntime } from "./editions.js";
36
+ import { getFieldDefaultValueExpression } from "./util.js";
39
37
 
40
38
  export function generateJs(schema: Schema) {
41
39
  for (const file of schema.files) {
@@ -58,7 +56,7 @@ export function generateJs(schema: Schema) {
58
56
  function generateEnum(schema: Schema, f: GeneratedFile, enumeration: DescEnum) {
59
57
  const protoN = getNonEditionRuntime(schema, enumeration.file);
60
58
  f.print(f.jsDoc(enumeration));
61
- f.print(f.exportDecl("const", enumeration), " = ", protoN, ".makeEnum(")
59
+ f.print(f.exportDecl("const", enumeration), " = /*@__PURE__*/ ", protoN, ".makeEnum(")
62
60
  f.print(` "`, enumeration.typeName, `",`)
63
61
  f.print(` [`)
64
62
  if (enumeration.sharedPrefix === undefined) {
@@ -80,7 +78,7 @@ function generateEnum(schema: Schema, f: GeneratedFile, enumeration: DescEnum) {
80
78
  function generateMessage(schema: Schema, f: GeneratedFile, message: DescMessage) {
81
79
  const protoN = getNonEditionRuntime(schema, message.file);
82
80
  f.print(f.jsDoc(message));
83
- f.print(f.exportDecl("const", message), " = ", protoN, ".makeMessageType(")
81
+ f.print(f.exportDecl("const", message), " = /*@__PURE__*/ ", protoN, ".makeMessageType(")
84
82
  f.print(` `, f.string(message.typeName), `,`)
85
83
  if (message.fields.length == 0) {
86
84
  f.print(" [],")
@@ -168,8 +166,10 @@ export function getFieldInfoLiteral(schema: Schema, field: DescField | DescExten
168
166
  }
169
167
  if (field.optional) {
170
168
  e.push(`opt: true, `);
169
+ } else if (field.proto.label === FieldDescriptorProto_Label.REQUIRED) {
170
+ e.push(`req: true, `);
171
171
  }
172
- const defaultValue = getFieldExplicitDefaultValue(field, schema.runtime.protoInt64);
172
+ const defaultValue = getFieldDefaultValueExpression(field);
173
173
  if (defaultValue !== undefined) {
174
174
  e.push(`default: `, defaultValue, `, `);
175
175
  }
package/src/typescript.ts CHANGED
@@ -25,14 +25,10 @@ import type {
25
25
  Printable,
26
26
  Schema,
27
27
  } from "@bufbuild/protoplugin/ecmascript";
28
- import {
29
- getFieldIntrinsicDefaultValue,
30
- getFieldTyping,
31
- localName,
32
- reifyWkt,
33
- } from "@bufbuild/protoplugin/ecmascript";
28
+ import { localName, reifyWkt } from "@bufbuild/protoplugin/ecmascript";
34
29
  import { generateFieldInfo, getFieldInfoLiteral } from "./javascript.js";
35
30
  import { getNonEditionRuntime } from "./editions.js";
31
+ import { getFieldTypeInfo, getFieldZeroValueExpression } from "./util.js";
36
32
 
37
33
  export function generateTs(schema: Schema) {
38
34
  for (const file of schema.files) {
@@ -152,29 +148,32 @@ function generateOneof(schema: Schema, f: GeneratedFile, oneof: DescOneof) {
152
148
  f.print(` } | {`);
153
149
  }
154
150
  f.print(f.jsDoc(field, " "));
155
- const { typing } = getFieldTyping(field, f);
151
+ const { typing } = getFieldTypeInfo(field);
156
152
  f.print(` value: `, typing, `;`);
157
153
  f.print(` case: "`, localName(field), `";`);
158
154
  }
159
155
  f.print(` } | { case: undefined; value?: undefined } = { case: undefined };`);
160
156
  }
161
157
 
158
+ // prettier-ignore
162
159
  function generateField(schema: Schema, f: GeneratedFile, field: DescField) {
163
160
  f.print(f.jsDoc(field, " "));
164
161
  const e: Printable = [];
165
- e.push(" ", localName(field));
166
- const { defaultValue, typingInferrable } =
167
- getFieldIntrinsicDefaultValue(field);
168
- const { typing, optional } = getFieldTyping(field, f);
169
- if (optional || defaultValue === undefined) {
170
- e.push("?: ", typing);
171
- } else if (!typingInferrable) {
172
- e.push(": ", typing);
173
- }
174
- if (defaultValue !== undefined) {
175
- e.push(" = ", defaultValue);
162
+ const { typing, optional, typingInferrableFromZeroValue } = getFieldTypeInfo(field);
163
+ if (optional) {
164
+ e.push(" ", localName(field), "?: ", typing, ";");
165
+ } else {
166
+ if (typingInferrableFromZeroValue) {
167
+ e.push(" ", localName(field));
168
+ } else {
169
+ e.push(" ", localName(field), ": ", typing);
170
+ }
171
+ const zeroValue = getFieldZeroValueExpression(field);
172
+ if (zeroValue !== undefined) {
173
+ e.push(" = ", zeroValue);
174
+ }
175
+ e.push(";");
176
176
  }
177
- e.push(";");
178
177
  f.print(e);
179
178
  }
180
179
 
@@ -185,7 +184,7 @@ function generateExtension(
185
184
  ext: DescExtension,
186
185
  ) {
187
186
  const protoN = getNonEditionRuntime(schema, ext.file);
188
- const { typing } = getFieldTyping(ext, f);
187
+ const { typing } = getFieldTypeInfo(ext);
189
188
  f.print(f.jsDoc(ext));
190
189
  f.print(f.exportDecl("const", ext), " = ", protoN, ".makeExtension<", ext.extendee, ", ", typing, ">(");
191
190
  f.print(" ", f.string(ext.typeName), ", ");
@@ -652,7 +651,7 @@ function generateWktStaticMethods(schema: Schema, f: GeneratedFile, message: Des
652
651
  case "google.protobuf.BoolValue":
653
652
  case "google.protobuf.StringValue":
654
653
  case "google.protobuf.BytesValue": {
655
- const {typing} = getFieldTyping(ref.value, f);
654
+ const {typing} = getFieldTypeInfo(ref.value);
656
655
  f.print(" static readonly fieldWrapper = {")
657
656
  f.print(" wrapField(value: ", typing, "): ", message, " {")
658
657
  f.print(" return new ", message, "({value});")
package/src/util.ts ADDED
@@ -0,0 +1,315 @@
1
+ // Copyright 2021-2024 Buf Technologies, Inc.
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+
15
+ import {
16
+ codegenInfo,
17
+ DescEnumValue,
18
+ DescExtension,
19
+ DescField,
20
+ FieldDescriptorProto_Label,
21
+ LongType,
22
+ ScalarType,
23
+ ScalarValue,
24
+ } from "@bufbuild/protobuf";
25
+ import type { Printable } from "@bufbuild/protoplugin/ecmascript";
26
+ import { localName } from "@bufbuild/protoplugin/ecmascript";
27
+
28
+ export function getFieldTypeInfo(field: DescField | DescExtension): {
29
+ typing: Printable;
30
+ optional: boolean;
31
+ typingInferrableFromZeroValue: boolean;
32
+ } {
33
+ const typing: Printable = [];
34
+ let typingInferrableFromZeroValue: boolean;
35
+ let optional = false;
36
+ switch (field.fieldKind) {
37
+ case "scalar":
38
+ typing.push(scalarTypeScriptType(field.scalar, field.longType));
39
+ optional =
40
+ field.optional ||
41
+ field.proto.label === FieldDescriptorProto_Label.REQUIRED;
42
+ typingInferrableFromZeroValue = true;
43
+ break;
44
+ case "message": {
45
+ const baseType = codegenInfo.getUnwrappedFieldType(field);
46
+ if (baseType !== undefined) {
47
+ typing.push(scalarTypeScriptType(baseType, LongType.BIGINT));
48
+ } else {
49
+ typing.push({
50
+ kind: "es_ref_message",
51
+ type: field.message,
52
+ typeOnly: true,
53
+ });
54
+ }
55
+ optional = true;
56
+ typingInferrableFromZeroValue = true;
57
+ break;
58
+ }
59
+ case "enum":
60
+ typing.push({
61
+ kind: "es_ref_enum",
62
+ type: field.enum,
63
+ typeOnly: true,
64
+ });
65
+ optional =
66
+ field.optional ||
67
+ field.proto.label === FieldDescriptorProto_Label.REQUIRED;
68
+ typingInferrableFromZeroValue = true;
69
+ break;
70
+ case "map": {
71
+ let keyType: string;
72
+ switch (field.mapKey) {
73
+ case ScalarType.INT32:
74
+ case ScalarType.FIXED32:
75
+ case ScalarType.UINT32:
76
+ case ScalarType.SFIXED32:
77
+ case ScalarType.SINT32:
78
+ keyType = "number";
79
+ break;
80
+ default:
81
+ keyType = "string";
82
+ break;
83
+ }
84
+ let valueType: Printable;
85
+ switch (field.mapValue.kind) {
86
+ case "scalar":
87
+ valueType = scalarTypeScriptType(
88
+ field.mapValue.scalar,
89
+ LongType.BIGINT,
90
+ );
91
+ break;
92
+ case "message":
93
+ valueType = {
94
+ kind: "es_ref_message",
95
+ type: field.mapValue.message,
96
+ typeOnly: true,
97
+ };
98
+ break;
99
+ case "enum":
100
+ valueType = {
101
+ kind: "es_ref_enum",
102
+ type: field.mapValue.enum,
103
+ typeOnly: true,
104
+ };
105
+ break;
106
+ }
107
+ typing.push("{ [key: ", keyType, "]: ", valueType, " }");
108
+ typingInferrableFromZeroValue = false;
109
+ optional = false;
110
+ break;
111
+ }
112
+ }
113
+ if (field.repeated) {
114
+ typing.push("[]");
115
+ optional = false;
116
+ typingInferrableFromZeroValue = false;
117
+ }
118
+ return { typing, optional, typingInferrableFromZeroValue };
119
+ }
120
+
121
+ /**
122
+ * Return a printable expression for the default value of a field.
123
+ * Only applicable for singular scalar and enum fields.
124
+ */
125
+ export function getFieldDefaultValueExpression(
126
+ field: DescField | DescExtension,
127
+ enumAs:
128
+ | "enum_value_as_is"
129
+ | "enum_value_as_integer"
130
+ | "enum_value_as_cast_integer" = "enum_value_as_is",
131
+ ): Printable | undefined {
132
+ if (field.repeated) {
133
+ return undefined;
134
+ }
135
+ if (field.fieldKind !== "enum" && field.fieldKind !== "scalar") {
136
+ return undefined;
137
+ }
138
+ const defaultValue = field.getDefaultValue();
139
+ if (defaultValue === undefined) {
140
+ return undefined;
141
+ }
142
+ switch (field.fieldKind) {
143
+ case "enum": {
144
+ const enumValue = field.enum.values.find(
145
+ (value) => value.number === defaultValue,
146
+ );
147
+ if (enumValue === undefined) {
148
+ throw new Error(
149
+ `invalid enum default value: ${String(defaultValue)} for ${enumValue}`,
150
+ );
151
+ }
152
+ return literalEnumValue(enumValue, enumAs);
153
+ }
154
+ case "scalar":
155
+ return literalScalarValue(defaultValue, field);
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Return a printable expression for the zero value of a field.
161
+ *
162
+ * Returns either:
163
+ * - empty array literal for repeated fields
164
+ * - empty object literal for maps
165
+ * - undefined for message fields
166
+ * - an enums first value
167
+ * - scalar zero value
168
+ */
169
+ export function getFieldZeroValueExpression(
170
+ field: DescField | DescExtension,
171
+ enumAs:
172
+ | "enum_value_as_is"
173
+ | "enum_value_as_integer"
174
+ | "enum_value_as_cast_integer" = "enum_value_as_is",
175
+ ): Printable | undefined {
176
+ if (field.repeated) {
177
+ return "[]";
178
+ }
179
+ switch (field.fieldKind) {
180
+ case "message":
181
+ return undefined;
182
+ case "map":
183
+ return "{}";
184
+ case "enum": {
185
+ // In proto3, the first enum value must be zero.
186
+ // In proto2, protobuf-go returns the first value as the default.
187
+ if (field.enum.values.length < 1) {
188
+ throw new Error("invalid enum: missing at least one value");
189
+ }
190
+ const zeroValue = field.enum.values[0];
191
+ return literalEnumValue(zeroValue, enumAs);
192
+ }
193
+ case "scalar": {
194
+ const defaultValue = codegenInfo.scalarZeroValue(
195
+ field.scalar,
196
+ field.longType,
197
+ );
198
+ return literalScalarValue(defaultValue, field);
199
+ }
200
+ }
201
+ }
202
+
203
+ function literalScalarValue(
204
+ value: ScalarValue,
205
+ field: (DescField | DescExtension) & { fieldKind: "scalar" },
206
+ ): Printable {
207
+ switch (field.scalar) {
208
+ case ScalarType.DOUBLE:
209
+ case ScalarType.FLOAT:
210
+ case ScalarType.INT32:
211
+ case ScalarType.FIXED32:
212
+ case ScalarType.UINT32:
213
+ case ScalarType.SFIXED32:
214
+ case ScalarType.SINT32:
215
+ if (typeof value != "number") {
216
+ throw new Error(
217
+ `Unexpected value for ${ScalarType[field.scalar]} ${field.toString()}: ${String(value)}`,
218
+ );
219
+ }
220
+ return value;
221
+ case ScalarType.BOOL:
222
+ if (typeof value != "boolean") {
223
+ throw new Error(
224
+ `Unexpected value for ${ScalarType[field.scalar]} ${field.toString()}: ${String(value)}`,
225
+ );
226
+ }
227
+ return value;
228
+ case ScalarType.STRING:
229
+ if (typeof value != "string") {
230
+ throw new Error(
231
+ `Unexpected value for ${ScalarType[field.scalar]} ${field.toString()}: ${String(value)}`,
232
+ );
233
+ }
234
+ return { kind: "es_string", value };
235
+ case ScalarType.BYTES:
236
+ if (!(value instanceof Uint8Array)) {
237
+ throw new Error(
238
+ `Unexpected value for ${ScalarType[field.scalar]} ${field.toString()}: ${String(value)}`,
239
+ );
240
+ }
241
+ return value;
242
+ case ScalarType.INT64:
243
+ case ScalarType.SINT64:
244
+ case ScalarType.SFIXED64:
245
+ case ScalarType.UINT64:
246
+ case ScalarType.FIXED64:
247
+ if (typeof value != "bigint" && typeof value != "string") {
248
+ throw new Error(
249
+ `Unexpected value for ${ScalarType[field.scalar]} ${field.toString()}: ${String(value)}`,
250
+ );
251
+ }
252
+ return {
253
+ kind: "es_proto_int64",
254
+ type: field.scalar,
255
+ longType: field.longType,
256
+ value,
257
+ };
258
+ }
259
+ }
260
+
261
+ function literalEnumValue(
262
+ value: DescEnumValue,
263
+ enumAs:
264
+ | "enum_value_as_is"
265
+ | "enum_value_as_integer"
266
+ | "enum_value_as_cast_integer",
267
+ ): Printable {
268
+ switch (enumAs) {
269
+ case "enum_value_as_is":
270
+ return [
271
+ { kind: "es_ref_enum", type: value.parent, typeOnly: false },
272
+ ".",
273
+ localName(value),
274
+ ];
275
+ case "enum_value_as_integer":
276
+ return [
277
+ value.number,
278
+ " /* ",
279
+ value.parent.typeName,
280
+ ".",
281
+ value.name,
282
+ " */",
283
+ ];
284
+ case "enum_value_as_cast_integer":
285
+ return [
286
+ value.number,
287
+ " as ",
288
+ { kind: "es_ref_enum", type: value.parent, typeOnly: true },
289
+ ".",
290
+ localName(value),
291
+ ];
292
+ }
293
+ }
294
+
295
+ function scalarTypeScriptType(type: ScalarType, longType: LongType): Printable {
296
+ switch (type) {
297
+ case ScalarType.STRING:
298
+ return "string";
299
+ case ScalarType.BOOL:
300
+ return "boolean";
301
+ case ScalarType.UINT64:
302
+ case ScalarType.SFIXED64:
303
+ case ScalarType.FIXED64:
304
+ case ScalarType.SINT64:
305
+ case ScalarType.INT64:
306
+ if (longType === LongType.STRING) {
307
+ return "string";
308
+ }
309
+ return "bigint";
310
+ case ScalarType.BYTES:
311
+ return "Uint8Array";
312
+ default:
313
+ return "number";
314
+ }
315
+ }