@bufbuild/protoc-gen-es 2.3.0 → 2.5.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,3 +118,16 @@ the top of each file to skip type checks: `// @ts-nocheck`.
118
118
 
119
119
  Generates JSON types for every Protobuf message and enumeration. Calling `toJson()` automatically returns the JSON type
120
120
  if available. Learn more about [JSON types](https://github.com/bufbuild/protobuf-es/blob/main/MANUAL.md#json-types).
121
+
122
+ ### `valid_types` (experimental)
123
+
124
+ Generates a Valid type for every Protobuf message. Possible values:
125
+
126
+ - `valid_types=legacy_required`: Message fields with the `required` label, or the Edition feature
127
+ `features.field_presence=LEGACY_REQUIRED`, are generated as non-optional properties.
128
+ - `valid_types=protovalidate_required`: Message fields with protovalidate's [`required` rule](https://buf.build/docs/reference/protovalidate/rules/field_rules/#required)
129
+ rule are generated as non-optional properties.
130
+
131
+ You can combine both options with `+`—for example, `valid_types=legacy_required+protovalidate_required`.
132
+
133
+ Learn more about [Valid types](https://github.com/bufbuild/protobuf-es/blob/main/MANUAL.md#valid-types).
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bufbuild/protoc-gen-es",
3
- "version": "2.3.0",
3
+ "version": "2.5.0",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Protocol Buffers code generator for ECMAScript",
6
6
  "keywords": [
@@ -24,17 +24,22 @@
24
24
  "scripts": {
25
25
  "prebuild": "rm -rf ./dist/cjs/*",
26
26
  "build": "../../node_modules/typescript/bin/tsc --project tsconfig.json --outDir ./dist/cjs",
27
+ "bootstrap": "protoc --es_out=src/gen --es_opt=target=ts,import_extension=js --proto_path proto proto/minimal-validate.proto",
28
+ "postbootstrap": "license-header src/gen",
27
29
  "format": "biome format --write",
28
30
  "license-header": "license-header",
29
31
  "lint": "biome lint --error-on-warnings"
30
32
  },
31
33
  "preferUnplugged": true,
32
34
  "dependencies": {
33
- "@bufbuild/protobuf": "^2.3.0",
34
- "@bufbuild/protoplugin": "2.3.0"
35
+ "@bufbuild/protobuf": "^2.5.0",
36
+ "@bufbuild/protoplugin": "2.5.0"
37
+ },
38
+ "devDependencies": {
39
+ "upstream-protobuf": "*"
35
40
  },
36
41
  "peerDependencies": {
37
- "@bufbuild/protobuf": "2.3.0"
42
+ "@bufbuild/protobuf": "2.5.0"
38
43
  },
39
44
  "peerDependenciesMeta": {
40
45
  "@bufbuild/protobuf": {
@@ -0,0 +1,137 @@
1
+ import type { GenEnum, GenExtension, GenFile, GenMessage } from "@bufbuild/protobuf/codegenv1";
2
+ import type { FieldOptions, MessageOptions, OneofOptions } from "@bufbuild/protobuf/wkt";
3
+ import type { Message } from "@bufbuild/protobuf";
4
+ /**
5
+ * Describes the file minimal-validate.proto.
6
+ */
7
+ export declare const file_minimal_validate: GenFile;
8
+ /**
9
+ * @generated from message buf.validate.MessageRules
10
+ */
11
+ export type MessageRules = Message<"buf.validate.MessageRules"> & {
12
+ /**
13
+ * @generated from field: optional bool disabled = 1;
14
+ */
15
+ disabled: boolean;
16
+ };
17
+ /**
18
+ * Describes the message buf.validate.MessageRules.
19
+ * Use `create(MessageRulesSchema)` to create a new message.
20
+ */
21
+ export declare const MessageRulesSchema: GenMessage<MessageRules>;
22
+ /**
23
+ * @generated from message buf.validate.OneofRules
24
+ */
25
+ export type OneofRules = Message<"buf.validate.OneofRules"> & {
26
+ /**
27
+ * @generated from field: optional bool required = 1;
28
+ */
29
+ required: boolean;
30
+ };
31
+ /**
32
+ * Describes the message buf.validate.OneofRules.
33
+ * Use `create(OneofRulesSchema)` to create a new message.
34
+ */
35
+ export declare const OneofRulesSchema: GenMessage<OneofRules>;
36
+ /**
37
+ * @generated from message buf.validate.FieldRules
38
+ */
39
+ export type FieldRules = Message<"buf.validate.FieldRules"> & {
40
+ /**
41
+ * @generated from field: optional bool required = 25;
42
+ */
43
+ required: boolean;
44
+ /**
45
+ * @generated from field: optional buf.validate.Ignore ignore = 27;
46
+ */
47
+ ignore: Ignore;
48
+ /**
49
+ * @generated from oneof buf.validate.FieldRules.type
50
+ */
51
+ type: {
52
+ /**
53
+ * @generated from field: buf.validate.RepeatedRules repeated = 18;
54
+ */
55
+ value: RepeatedRules;
56
+ case: "repeated";
57
+ } | {
58
+ /**
59
+ * @generated from field: buf.validate.MapRules map = 19;
60
+ */
61
+ value: MapRules;
62
+ case: "map";
63
+ } | {
64
+ case: undefined;
65
+ value?: undefined;
66
+ };
67
+ };
68
+ /**
69
+ * Describes the message buf.validate.FieldRules.
70
+ * Use `create(FieldRulesSchema)` to create a new message.
71
+ */
72
+ export declare const FieldRulesSchema: GenMessage<FieldRules>;
73
+ /**
74
+ * @generated from message buf.validate.RepeatedRules
75
+ */
76
+ export type RepeatedRules = Message<"buf.validate.RepeatedRules"> & {
77
+ /**
78
+ * @generated from field: optional buf.validate.FieldRules items = 4;
79
+ */
80
+ items?: FieldRules;
81
+ };
82
+ /**
83
+ * Describes the message buf.validate.RepeatedRules.
84
+ * Use `create(RepeatedRulesSchema)` to create a new message.
85
+ */
86
+ export declare const RepeatedRulesSchema: GenMessage<RepeatedRules>;
87
+ /**
88
+ * @generated from message buf.validate.MapRules
89
+ */
90
+ export type MapRules = Message<"buf.validate.MapRules"> & {
91
+ /**
92
+ * @generated from field: optional buf.validate.FieldRules values = 5;
93
+ */
94
+ values?: FieldRules;
95
+ };
96
+ /**
97
+ * Describes the message buf.validate.MapRules.
98
+ * Use `create(MapRulesSchema)` to create a new message.
99
+ */
100
+ export declare const MapRulesSchema: GenMessage<MapRules>;
101
+ /**
102
+ * @generated from enum buf.validate.Ignore
103
+ */
104
+ export declare enum Ignore {
105
+ /**
106
+ * @generated from enum value: IGNORE_UNSPECIFIED = 0;
107
+ */
108
+ UNSPECIFIED = 0,
109
+ /**
110
+ * @generated from enum value: IGNORE_IF_UNPOPULATED = 1;
111
+ */
112
+ IF_UNPOPULATED = 1,
113
+ /**
114
+ * @generated from enum value: IGNORE_IF_DEFAULT_VALUE = 2;
115
+ */
116
+ IF_DEFAULT_VALUE = 2,
117
+ /**
118
+ * @generated from enum value: IGNORE_ALWAYS = 3;
119
+ */
120
+ ALWAYS = 3
121
+ }
122
+ /**
123
+ * Describes the enum buf.validate.Ignore.
124
+ */
125
+ export declare const IgnoreSchema: GenEnum<Ignore>;
126
+ /**
127
+ * @generated from extension: optional buf.validate.MessageRules message = 1159;
128
+ */
129
+ export declare const message: GenExtension<MessageOptions, MessageRules>;
130
+ /**
131
+ * @generated from extension: optional buf.validate.OneofRules oneof = 1159;
132
+ */
133
+ export declare const oneof: GenExtension<OneofOptions, OneofRules>;
134
+ /**
135
+ * @generated from extension: optional buf.validate.FieldRules field = 1159;
136
+ */
137
+ export declare const field: GenExtension<FieldOptions, FieldRules>;
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ // Copyright 2021-2025 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.field = exports.oneof = exports.message = exports.IgnoreSchema = exports.Ignore = exports.MapRulesSchema = exports.RepeatedRulesSchema = exports.FieldRulesSchema = exports.OneofRulesSchema = exports.MessageRulesSchema = exports.file_minimal_validate = void 0;
17
+ const codegenv1_1 = require("@bufbuild/protobuf/codegenv1");
18
+ const wkt_1 = require("@bufbuild/protobuf/wkt");
19
+ /**
20
+ * Describes the file minimal-validate.proto.
21
+ */
22
+ exports.file_minimal_validate = (0, codegenv1_1.fileDesc)("ChZtaW5pbWFsLXZhbGlkYXRlLnByb3RvEgxidWYudmFsaWRhdGUiIAoMTWVzc2FnZVJ1bGVzEhAKCGRpc2FibGVkGAEgASgIIh4KCk9uZW9mUnVsZXMSEAoIcmVxdWlyZWQYASABKAgipAEKCkZpZWxkUnVsZXMSEAoIcmVxdWlyZWQYGSABKAgSJAoGaWdub3JlGBsgASgOMhQuYnVmLnZhbGlkYXRlLklnbm9yZRIvCghyZXBlYXRlZBgSIAEoCzIbLmJ1Zi52YWxpZGF0ZS5SZXBlYXRlZFJ1bGVzSAASJQoDbWFwGBMgASgLMhYuYnVmLnZhbGlkYXRlLk1hcFJ1bGVzSABCBgoEdHlwZSI4Cg1SZXBlYXRlZFJ1bGVzEicKBWl0ZW1zGAQgASgLMhguYnVmLnZhbGlkYXRlLkZpZWxkUnVsZXMiNAoITWFwUnVsZXMSKAoGdmFsdWVzGAUgASgLMhguYnVmLnZhbGlkYXRlLkZpZWxkUnVsZXMqawoGSWdub3JlEhYKEklHTk9SRV9VTlNQRUNJRklFRBAAEhkKFUlHTk9SRV9JRl9VTlBPUFVMQVRFRBABEhsKF0lHTk9SRV9JRl9ERUZBVUxUX1ZBTFVFEAISEQoNSUdOT1JFX0FMV0FZUxADOlYKB21lc3NhZ2USHy5nb29nbGUucHJvdG9idWYuTWVzc2FnZU9wdGlvbnMYhwkgASgLMhouYnVmLnZhbGlkYXRlLk1lc3NhZ2VSdWxlc1IHbWVzc2FnZTpOCgVvbmVvZhIdLmdvb2dsZS5wcm90b2J1Zi5PbmVvZk9wdGlvbnMYhwkgASgLMhguYnVmLnZhbGlkYXRlLk9uZW9mUnVsZXNSBW9uZW9mOk4KBWZpZWxkEh0uZ29vZ2xlLnByb3RvYnVmLkZpZWxkT3B0aW9ucxiHCSABKAsyGC5idWYudmFsaWRhdGUuRmllbGRSdWxlc1IFZmllbGQ", [wkt_1.file_google_protobuf_descriptor]);
23
+ /**
24
+ * Describes the message buf.validate.MessageRules.
25
+ * Use `create(MessageRulesSchema)` to create a new message.
26
+ */
27
+ exports.MessageRulesSchema = (0, codegenv1_1.messageDesc)(exports.file_minimal_validate, 0);
28
+ /**
29
+ * Describes the message buf.validate.OneofRules.
30
+ * Use `create(OneofRulesSchema)` to create a new message.
31
+ */
32
+ exports.OneofRulesSchema = (0, codegenv1_1.messageDesc)(exports.file_minimal_validate, 1);
33
+ /**
34
+ * Describes the message buf.validate.FieldRules.
35
+ * Use `create(FieldRulesSchema)` to create a new message.
36
+ */
37
+ exports.FieldRulesSchema = (0, codegenv1_1.messageDesc)(exports.file_minimal_validate, 2);
38
+ /**
39
+ * Describes the message buf.validate.RepeatedRules.
40
+ * Use `create(RepeatedRulesSchema)` to create a new message.
41
+ */
42
+ exports.RepeatedRulesSchema = (0, codegenv1_1.messageDesc)(exports.file_minimal_validate, 3);
43
+ /**
44
+ * Describes the message buf.validate.MapRules.
45
+ * Use `create(MapRulesSchema)` to create a new message.
46
+ */
47
+ exports.MapRulesSchema = (0, codegenv1_1.messageDesc)(exports.file_minimal_validate, 4);
48
+ /**
49
+ * @generated from enum buf.validate.Ignore
50
+ */
51
+ var Ignore;
52
+ (function (Ignore) {
53
+ /**
54
+ * @generated from enum value: IGNORE_UNSPECIFIED = 0;
55
+ */
56
+ Ignore[Ignore["UNSPECIFIED"] = 0] = "UNSPECIFIED";
57
+ /**
58
+ * @generated from enum value: IGNORE_IF_UNPOPULATED = 1;
59
+ */
60
+ Ignore[Ignore["IF_UNPOPULATED"] = 1] = "IF_UNPOPULATED";
61
+ /**
62
+ * @generated from enum value: IGNORE_IF_DEFAULT_VALUE = 2;
63
+ */
64
+ Ignore[Ignore["IF_DEFAULT_VALUE"] = 2] = "IF_DEFAULT_VALUE";
65
+ /**
66
+ * @generated from enum value: IGNORE_ALWAYS = 3;
67
+ */
68
+ Ignore[Ignore["ALWAYS"] = 3] = "ALWAYS";
69
+ })(Ignore || (exports.Ignore = Ignore = {}));
70
+ /**
71
+ * Describes the enum buf.validate.Ignore.
72
+ */
73
+ exports.IgnoreSchema = (0, codegenv1_1.enumDesc)(exports.file_minimal_validate, 0);
74
+ /**
75
+ * @generated from extension: optional buf.validate.MessageRules message = 1159;
76
+ */
77
+ exports.message = (0, codegenv1_1.extDesc)(exports.file_minimal_validate, 0);
78
+ /**
79
+ * @generated from extension: optional buf.validate.OneofRules oneof = 1159;
80
+ */
81
+ exports.oneof = (0, codegenv1_1.extDesc)(exports.file_minimal_validate, 1);
82
+ /**
83
+ * @generated from extension: optional buf.validate.FieldRules field = 1159;
84
+ */
85
+ exports.field = (0, codegenv1_1.extDesc)(exports.file_minimal_validate, 2);
@@ -15,11 +15,12 @@
15
15
  Object.defineProperty(exports, "__esModule", { value: true });
16
16
  exports.protocGenEs = void 0;
17
17
  const reflect_1 = require("@bufbuild/protobuf/reflect");
18
- const codegenv1_1 = require("@bufbuild/protobuf/codegenv1");
18
+ const codegenv2_1 = require("@bufbuild/protobuf/codegenv2");
19
19
  const wkt_1 = require("@bufbuild/protobuf/wkt");
20
20
  const protoplugin_1 = require("@bufbuild/protoplugin");
21
- const util_1 = require("./util");
21
+ const util_js_1 = require("./util.js");
22
22
  const package_json_1 = require("../package.json");
23
+ const valid_types_js_1 = require("./valid-types.js");
23
24
  exports.protocGenEs = (0, protoplugin_1.createEcmaScriptPlugin)({
24
25
  name: "protoc-gen-es",
25
26
  version: `v${String(package_json_1.version)}`,
@@ -30,6 +31,10 @@ exports.protocGenEs = (0, protoplugin_1.createEcmaScriptPlugin)({
30
31
  });
31
32
  function parseOptions(options) {
32
33
  let jsonTypes = false;
34
+ let validTypes = {
35
+ legacyRequired: false,
36
+ protovalidateRequired: false,
37
+ };
33
38
  for (const { key, value } of options) {
34
39
  switch (key) {
35
40
  case "json_types":
@@ -38,11 +43,25 @@ function parseOptions(options) {
38
43
  }
39
44
  jsonTypes = ["true", "1"].includes(value);
40
45
  break;
46
+ case "valid_types":
47
+ for (const part of value.split("+")) {
48
+ switch (part) {
49
+ case "protovalidate_required":
50
+ validTypes.protovalidateRequired = true;
51
+ break;
52
+ case "legacy_required":
53
+ validTypes.legacyRequired = true;
54
+ break;
55
+ default:
56
+ throw new Error();
57
+ }
58
+ }
59
+ break;
41
60
  default:
42
61
  throw new Error();
43
62
  }
44
63
  }
45
- return { jsonTypes };
64
+ return { jsonTypes, validTypes };
46
65
  }
47
66
  // This annotation informs bundlers that the succeeding function call is free of
48
67
  // side effects. This means the symbol can be removed from the module during
@@ -67,18 +86,13 @@ function generateTs(schema) {
67
86
  if (schema.options.jsonTypes) {
68
87
  generateMessageJsonShape(f, desc, "ts");
69
88
  }
89
+ if (schema.options.validTypes.legacyRequired || schema.options.validTypes.protovalidateRequired) {
90
+ generateMessageValidShape(f, desc, schema.options.validTypes, "ts");
91
+ }
70
92
  generateDescDoc(f, desc);
71
93
  const name = f.importSchema(desc).name;
72
- const Shape = f.importShape(desc);
73
- const { GenMessage, messageDesc } = f.runtime.codegen;
74
- if (schema.options.jsonTypes) {
75
- const JsonType = f.importJson(desc);
76
- f.print(f.export("const", name), ": ", GenMessage, "<", Shape, ", ", JsonType, ">", " = ", pure);
77
- }
78
- else {
79
- f.print(f.export("const", name), ": ", GenMessage, "<", Shape, ">", " = ", pure);
80
- }
81
- const call = (0, util_1.functionCall)(messageDesc, [fileDesc, ...(0, codegenv1_1.pathInFileDesc)(desc)]);
94
+ f.print(f.export("const", name), ": ", (0, util_js_1.messageGenType)(desc, f, schema.options), " = ", pure);
95
+ const call = (0, util_js_1.functionCall)(f.runtime.codegen.messageDesc, [fileDesc, ...(0, codegenv2_1.pathInFileDesc)(desc)]);
82
96
  f.print(" ", call, ";");
83
97
  f.print();
84
98
  break;
@@ -99,7 +113,7 @@ function generateTs(schema) {
99
113
  else {
100
114
  f.print(f.export("const", name), ": ", GenEnum, "<", Shape, ">", " = ", pure);
101
115
  }
102
- const call = (0, util_1.functionCall)(enumDesc, [fileDesc, ...(0, codegenv1_1.pathInFileDesc)(desc)]);
116
+ const call = (0, util_js_1.functionCall)(enumDesc, [fileDesc, ...(0, codegenv2_1.pathInFileDesc)(desc)]);
103
117
  f.print(" ", call, ";");
104
118
  f.print();
105
119
  break;
@@ -108,8 +122,8 @@ function generateTs(schema) {
108
122
  const { GenExtension, extDesc } = f.runtime.codegen;
109
123
  const name = f.importSchema(desc).name;
110
124
  const E = f.importShape(desc.extendee);
111
- const V = (0, util_1.fieldTypeScriptType)(desc, f.runtime).typing;
112
- const call = (0, util_1.functionCall)(extDesc, [fileDesc, ...(0, codegenv1_1.pathInFileDesc)(desc)]);
125
+ const V = (0, util_js_1.fieldTypeScriptType)(desc, f.runtime).typing;
126
+ const call = (0, util_js_1.functionCall)(extDesc, [fileDesc, ...(0, codegenv2_1.pathInFileDesc)(desc)]);
113
127
  f.print(f.jsDoc(desc));
114
128
  f.print(f.export("const", name), ": ", GenExtension, "<", E, ", ", V, ">", " = ", pure);
115
129
  f.print(" ", call, ";");
@@ -119,7 +133,7 @@ function generateTs(schema) {
119
133
  case "service": {
120
134
  const { GenService, serviceDesc } = f.runtime.codegen;
121
135
  const name = f.importSchema(desc).name;
122
- const call = (0, util_1.functionCall)(serviceDesc, [fileDesc, ...(0, codegenv1_1.pathInFileDesc)(desc)]);
136
+ const call = (0, util_js_1.functionCall)(serviceDesc, [fileDesc, ...(0, codegenv2_1.pathInFileDesc)(desc)]);
123
137
  f.print(f.jsDoc(desc));
124
138
  f.print(f.export("const", name), ": ", GenService, "<", getServiceShapeExpr(f, desc), "> = ", pure);
125
139
  f.print(" ", call, ";");
@@ -146,7 +160,7 @@ function generateJs(schema) {
146
160
  const name = f.importSchema(desc).name;
147
161
  generateDescDoc(f, desc);
148
162
  const { messageDesc } = f.runtime.codegen;
149
- const call = (0, util_1.functionCall)(messageDesc, [fileDesc, ...(0, codegenv1_1.pathInFileDesc)(desc)]);
163
+ const call = (0, util_js_1.functionCall)(messageDesc, [fileDesc, ...(0, codegenv2_1.pathInFileDesc)(desc)]);
150
164
  f.print(f.export("const", name), " = ", pure);
151
165
  f.print(" ", call, ";");
152
166
  f.print();
@@ -159,7 +173,7 @@ function generateJs(schema) {
159
173
  const name = f.importSchema(desc).name;
160
174
  f.print(f.export("const", name), " = ", pure);
161
175
  const { enumDesc } = f.runtime.codegen;
162
- const call = (0, util_1.functionCall)(enumDesc, [fileDesc, ...(0, codegenv1_1.pathInFileDesc)(desc)]);
176
+ const call = (0, util_js_1.functionCall)(enumDesc, [fileDesc, ...(0, codegenv2_1.pathInFileDesc)(desc)]);
163
177
  f.print(" ", call, ";");
164
178
  f.print();
165
179
  }
@@ -168,7 +182,7 @@ function generateJs(schema) {
168
182
  f.print(f.jsDoc(desc));
169
183
  f.print(f.export("const", f.importShape(desc).name), " = ", pure);
170
184
  const { tsEnum } = f.runtime.codegen;
171
- const call = (0, util_1.functionCall)(tsEnum, [f.importSchema(desc)]);
185
+ const call = (0, util_js_1.functionCall)(tsEnum, [f.importSchema(desc)]);
172
186
  f.print(" ", call, ";");
173
187
  f.print();
174
188
  }
@@ -179,7 +193,7 @@ function generateJs(schema) {
179
193
  const name = f.importSchema(desc).name;
180
194
  f.print(f.export("const", name), " = ", pure);
181
195
  const { extDesc } = f.runtime.codegen;
182
- const call = (0, util_1.functionCall)(extDesc, [fileDesc, ...(0, codegenv1_1.pathInFileDesc)(desc)]);
196
+ const call = (0, util_js_1.functionCall)(extDesc, [fileDesc, ...(0, codegenv2_1.pathInFileDesc)(desc)]);
183
197
  f.print(" ", call, ";");
184
198
  f.print();
185
199
  break;
@@ -189,7 +203,7 @@ function generateJs(schema) {
189
203
  const name = f.importSchema(desc).name;
190
204
  f.print(f.export("const", name), " = ", pure);
191
205
  const { serviceDesc } = f.runtime.codegen;
192
- const call = (0, util_1.functionCall)(serviceDesc, [fileDesc, ...(0, codegenv1_1.pathInFileDesc)(desc)]);
206
+ const call = (0, util_js_1.functionCall)(serviceDesc, [fileDesc, ...(0, codegenv2_1.pathInFileDesc)(desc)]);
193
207
  f.print(" ", call, ";");
194
208
  f.print();
195
209
  break;
@@ -215,17 +229,12 @@ function generateDts(schema) {
215
229
  if (schema.options.jsonTypes) {
216
230
  generateMessageJsonShape(f, desc, "dts");
217
231
  }
232
+ if (schema.options.validTypes.legacyRequired || schema.options.validTypes.protovalidateRequired) {
233
+ generateMessageValidShape(f, desc, schema.options.validTypes, "dts");
234
+ }
218
235
  const name = f.importSchema(desc).name;
219
- const Shape = f.importShape(desc);
220
- const { GenMessage } = f.runtime.codegen;
221
236
  generateDescDoc(f, desc);
222
- if (schema.options.jsonTypes) {
223
- const JsonType = f.importJson(desc);
224
- f.print(f.export("declare const", name), ": ", GenMessage, "<", Shape, ", ", JsonType, ">", ";");
225
- }
226
- else {
227
- f.print(f.export("declare const", name), ": ", GenMessage, "<", Shape, ">", ";");
228
- }
237
+ f.print(f.export("declare const", name), ": ", (0, util_js_1.messageGenType)(desc, f, schema.options), ";");
229
238
  f.print();
230
239
  break;
231
240
  }
@@ -252,7 +261,7 @@ function generateDts(schema) {
252
261
  const { GenExtension } = f.runtime.codegen;
253
262
  const name = f.importSchema(desc).name;
254
263
  const E = f.importShape(desc.extendee);
255
- const V = (0, util_1.fieldTypeScriptType)(desc, f.runtime).typing;
264
+ const V = (0, util_js_1.fieldTypeScriptType)(desc, f.runtime).typing;
256
265
  f.print(f.jsDoc(desc));
257
266
  f.print(f.export("declare const", name), ": ", GenExtension, "<", E, ", ", V, ">;");
258
267
  f.print();
@@ -302,11 +311,11 @@ function getFileDescCall(f, file, schema) {
302
311
  // messages from CodeGeneratorRequest.proto_file instead.
303
312
  const sourceFile = file.proto;
304
313
  const runtimeFile = schema.proto.protoFile.find(f => f.name == sourceFile.name);
305
- const info = (0, codegenv1_1.embedFileDesc)(runtimeFile !== null && runtimeFile !== void 0 ? runtimeFile : sourceFile);
314
+ const info = (0, codegenv2_1.embedFileDesc)(runtimeFile !== null && runtimeFile !== void 0 ? runtimeFile : sourceFile);
306
315
  if (info.bootable && !f.runtime.create.from.startsWith("@bufbuild/protobuf")) {
307
316
  // google/protobuf/descriptor.proto is embedded as a plain object when
308
317
  // bootstrapping to avoid recursion
309
- return (0, util_1.functionCall)(f.runtime.codegen.boot, [JSON.stringify(info.boot())]);
318
+ return (0, util_js_1.functionCall)(f.runtime.codegen.boot, [JSON.stringify(info.boot())]);
310
319
  }
311
320
  const { fileDesc } = f.runtime.codegen;
312
321
  if (file.dependencies.length > 0) {
@@ -314,12 +323,12 @@ function getFileDescCall(f, file, schema) {
314
323
  kind: "es_desc_ref",
315
324
  desc: f,
316
325
  }));
317
- return (0, util_1.functionCall)(fileDesc, [
326
+ return (0, util_js_1.functionCall)(fileDesc, [
318
327
  f.string(info.base64()),
319
328
  f.array(deps),
320
329
  ]);
321
330
  }
322
- return (0, util_1.functionCall)(fileDesc, [f.string(info.base64())]);
331
+ return (0, util_js_1.functionCall)(fileDesc, [f.string(info.base64())]);
323
332
  }
324
333
  // biome-ignore format: want this to read well
325
334
  function getServiceShapeExpr(f, service) {
@@ -375,33 +384,28 @@ function generateMessageShape(f, message, target) {
375
384
  f.print(f.jsDoc(message));
376
385
  f.print(f.export(declaration, f.importShape(message).name), " = ", Message, "<", f.string(message.typeName), "> & {");
377
386
  for (const member of message.members) {
378
- switch (member.kind) {
379
- case "oneof":
380
- f.print(f.jsDoc(member, " "));
381
- f.print(" ", member.localName, ": {");
382
- for (const field of member.fields) {
383
- if (member.fields.indexOf(field) > 0) {
384
- f.print(` } | {`);
385
- }
386
- f.print(f.jsDoc(field, " "));
387
- const { typing } = (0, util_1.fieldTypeScriptType)(field, f.runtime);
388
- f.print(` value: `, typing, `;`);
389
- f.print(` case: "`, field.localName, `";`);
390
- }
391
- f.print(` } | { case: undefined; value?: undefined };`);
392
- break;
393
- default: {
394
- f.print(f.jsDoc(member, " "));
395
- const { typing, optional } = (0, util_1.fieldTypeScriptType)(member, f.runtime);
396
- if (optional) {
397
- f.print(" ", member.localName, "?: ", typing, ";");
398
- }
399
- else {
400
- f.print(" ", member.localName, ": ", typing, ";");
401
- }
402
- break;
403
- }
387
+ generateMessageShapeMember(f, member);
388
+ if (message.members.indexOf(member) < message.members.length - 1) {
389
+ f.print();
404
390
  }
391
+ }
392
+ f.print("};");
393
+ f.print();
394
+ }
395
+ // biome-ignore format: want this to read well
396
+ function generateMessageValidShape(f, message, validTypes, target) {
397
+ const declaration = target == "ts" ? "type" : "declare type";
398
+ const needsCustomValidType = (validTypes.legacyRequired && message.fields.some(valid_types_js_1.isLegacyRequired)) || (validTypes.protovalidateRequired && message.fields.some(valid_types_js_1.isProtovalidateRequired));
399
+ if (!needsCustomValidType) {
400
+ f.print(f.export(declaration, f.importValid(message).name), " = ", f.importShape(message), ";");
401
+ f.print();
402
+ return;
403
+ }
404
+ f.print(f.jsDoc(message));
405
+ const { Message } = f.runtime;
406
+ f.print(f.export(declaration, f.importValid(message).name), " = ", Message, "<", f.string(message.typeName), "> & {");
407
+ for (const member of message.members) {
408
+ generateMessageShapeMember(f, member, validTypes);
405
409
  if (message.members.indexOf(member) < message.members.length - 1) {
406
410
  f.print();
407
411
  }
@@ -410,6 +414,41 @@ function generateMessageShape(f, message, target) {
410
414
  f.print();
411
415
  }
412
416
  // biome-ignore format: want this to read well
417
+ function generateMessageShapeMember(f, member, validTypes) {
418
+ switch (member.kind) {
419
+ case "oneof":
420
+ f.print(f.jsDoc(member, " "));
421
+ f.print(" ", member.localName, ": {");
422
+ for (const field of member.fields) {
423
+ if (member.fields.indexOf(field) > 0) {
424
+ f.print(` } | {`);
425
+ }
426
+ f.print(f.jsDoc(field, " "));
427
+ const { typing } = (0, util_js_1.fieldTypeScriptType)(field, f.runtime, validTypes && !(0, valid_types_js_1.isProtovalidateDisabled)(field));
428
+ f.print(` value: `, typing, `;`);
429
+ f.print(` case: "`, field.localName, `";`);
430
+ }
431
+ f.print(` } | { case: undefined; value?: undefined };`);
432
+ break;
433
+ case "field":
434
+ f.print(f.jsDoc(member, " "));
435
+ let { typing, optional } = (0, util_js_1.fieldTypeScriptType)(member, f.runtime, validTypes && !(0, valid_types_js_1.isProtovalidateDisabled)(member));
436
+ if (optional && validTypes) {
437
+ const isRequired = (validTypes.legacyRequired && (0, valid_types_js_1.isLegacyRequired)(member)) || (validTypes.protovalidateRequired && (0, valid_types_js_1.isProtovalidateRequired)(member));
438
+ if (isRequired) {
439
+ optional = false;
440
+ }
441
+ }
442
+ if (optional) {
443
+ f.print(" ", member.localName, "?: ", typing, ";");
444
+ }
445
+ else {
446
+ f.print(" ", member.localName, ": ", typing, ";");
447
+ }
448
+ break;
449
+ }
450
+ }
451
+ // biome-ignore format: want this to read well
413
452
  function generateMessageJsonShape(f, message, target) {
414
453
  const exp = f.export(target == "ts" ? "type" : "declare type", f.importJson(message).name);
415
454
  f.print(f.jsDoc(message));
@@ -442,7 +481,7 @@ function generateMessageJsonShape(f, message, target) {
442
481
  break;
443
482
  default:
444
483
  if ((0, wkt_1.isWrapperDesc)(message)) {
445
- f.print(exp, " = ", (0, util_1.fieldJsonType)(message.fields[0]), ";");
484
+ f.print(exp, " = ", (0, util_js_1.fieldJsonType)(message.fields[0]), ";");
446
485
  }
447
486
  else {
448
487
  f.print(exp, " = {");
@@ -456,7 +495,7 @@ function generateMessageJsonShape(f, message, target) {
456
495
  || containsSpecialChar.test(jsonName)) {
457
496
  jsonName = f.string(jsonName);
458
497
  }
459
- f.print(" ", jsonName, "?: ", (0, util_1.fieldJsonType)(field), ";");
498
+ f.print(" ", jsonName, "?: ", (0, util_js_1.fieldJsonType)(field), ";");
460
499
  if (message.fields.indexOf(field) < message.fields.length - 1) {
461
500
  f.print();
462
501
  }
@@ -1,6 +1,24 @@
1
- import { type DescExtension, type DescField } from "@bufbuild/protobuf";
1
+ import { type DescExtension, type DescField, type DescMessage } from "@bufbuild/protobuf";
2
2
  import type { GeneratedFile, Printable } from "@bufbuild/protoplugin";
3
- export declare function fieldTypeScriptType(field: DescField | DescExtension, imports: GeneratedFile["runtime"]): {
3
+ /**
4
+ * Returns a type expression for `GenMessage` from @bufbuild/protobuf/codegenv2,
5
+ * with the mandatory `RuntimeShape` parameter, and the optional `OptShapes`
6
+ * parameter providing a JSON type, and/or a Valid type.
7
+ *
8
+ * For example:
9
+ * - `GenMessage<Example>`
10
+ * - `GenMessage<Example, {jsonType: ExampleJson}>`
11
+ * - `GenMessage<Example, {validType: ExampleValid}>`
12
+ * - `GenMessage<Example, {jsonType: ExampleJson, validType: ExampleValid}>`
13
+ */
14
+ export declare function messageGenType(desc: DescMessage, f: GeneratedFile, options: {
15
+ jsonTypes: boolean;
16
+ validTypes: {
17
+ legacyRequired: boolean;
18
+ protovalidateRequired: boolean;
19
+ };
20
+ }): Printable;
21
+ export declare function fieldTypeScriptType(field: DescField | DescExtension, imports: GeneratedFile["runtime"], validTypes?: boolean): {
4
22
  typing: Printable;
5
23
  optional: boolean;
6
24
  };
@@ -13,22 +13,48 @@
13
13
  // See the License for the specific language governing permissions and
14
14
  // limitations under the License.
15
15
  Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.messageGenType = messageGenType;
16
17
  exports.fieldTypeScriptType = fieldTypeScriptType;
17
18
  exports.fieldJsonType = fieldJsonType;
18
19
  exports.functionCall = functionCall;
19
20
  const protobuf_1 = require("@bufbuild/protobuf");
20
- const codegenv1_1 = require("@bufbuild/protobuf/codegenv1");
21
+ const codegenv2_1 = require("@bufbuild/protobuf/codegenv2");
21
22
  const wkt_1 = require("@bufbuild/protobuf/wkt");
22
- function fieldTypeScriptType(field, imports) {
23
+ /**
24
+ * Returns a type expression for `GenMessage` from @bufbuild/protobuf/codegenv2,
25
+ * with the mandatory `RuntimeShape` parameter, and the optional `OptShapes`
26
+ * parameter providing a JSON type, and/or a Valid type.
27
+ *
28
+ * For example:
29
+ * - `GenMessage<Example>`
30
+ * - `GenMessage<Example, {jsonType: ExampleJson}>`
31
+ * - `GenMessage<Example, {validType: ExampleValid}>`
32
+ * - `GenMessage<Example, {jsonType: ExampleJson, validType: ExampleValid}>`
33
+ */
34
+ function messageGenType(desc, f, options) {
35
+ let p2 = [];
36
+ if (options.jsonTypes) {
37
+ p2.push(["jsonType: ", f.importJson(desc)]);
38
+ }
39
+ if (options.validTypes.legacyRequired ||
40
+ options.validTypes.protovalidateRequired) {
41
+ p2.push(["validType: ", f.importValid(desc)]);
42
+ }
43
+ if (p2.length > 0) {
44
+ p2 = [", {", commaSeparate(p2), "}"];
45
+ }
46
+ return [f.runtime.codegen.GenMessage, "<", f.importShape(desc), p2, ">"];
47
+ }
48
+ function fieldTypeScriptType(field, imports, validTypes = false) {
23
49
  const typing = [];
24
50
  let optional = false;
25
51
  switch (field.fieldKind) {
26
52
  case "scalar":
27
- typing.push((0, codegenv1_1.scalarTypeScriptType)(field.scalar, field.longAsString));
53
+ typing.push((0, codegenv2_1.scalarTypeScriptType)(field.scalar, field.longAsString));
28
54
  optional = field.proto.proto3Optional;
29
55
  break;
30
56
  case "message": {
31
- typing.push(messageFieldTypeScriptType(field, imports));
57
+ typing.push(messageFieldTypeScriptType(field, imports, validTypes));
32
58
  optional = true;
33
59
  break;
34
60
  }
@@ -49,10 +75,10 @@ function fieldTypeScriptType(field, imports) {
49
75
  }, "[]");
50
76
  break;
51
77
  case "scalar":
52
- typing.push((0, codegenv1_1.scalarTypeScriptType)(field.scalar, field.longAsString), "[]");
78
+ typing.push((0, codegenv2_1.scalarTypeScriptType)(field.scalar, field.longAsString), "[]");
53
79
  break;
54
80
  case "message": {
55
- typing.push(messageFieldTypeScriptType(field, imports), "[]");
81
+ typing.push(messageFieldTypeScriptType(field, imports, validTypes), "[]");
56
82
  break;
57
83
  }
58
84
  }
@@ -74,10 +100,10 @@ function fieldTypeScriptType(field, imports) {
74
100
  let valueType;
75
101
  switch (field.mapKind) {
76
102
  case "scalar":
77
- valueType = (0, codegenv1_1.scalarTypeScriptType)(field.scalar, false);
103
+ valueType = (0, codegenv2_1.scalarTypeScriptType)(field.scalar, false);
78
104
  break;
79
105
  case "message":
80
- valueType = messageFieldTypeScriptType(field, imports);
106
+ valueType = messageFieldTypeScriptType(field, imports, validTypes);
81
107
  break;
82
108
  case "enum":
83
109
  valueType = {
@@ -93,18 +119,24 @@ function fieldTypeScriptType(field, imports) {
93
119
  }
94
120
  return { typing, optional };
95
121
  }
96
- function messageFieldTypeScriptType(field, imports) {
122
+ function messageFieldTypeScriptType(field, imports, validTypes) {
97
123
  var _a;
98
124
  if ((0, wkt_1.isWrapperDesc)(field.message) &&
99
125
  !field.oneof &&
100
126
  field.fieldKind == "message") {
101
127
  const baseType = field.message.fields[0].scalar;
102
- return (0, codegenv1_1.scalarTypeScriptType)(baseType, false);
128
+ return (0, codegenv2_1.scalarTypeScriptType)(baseType, false);
103
129
  }
104
130
  if (field.message.typeName == wkt_1.StructSchema.typeName &&
105
131
  ((_a = field.parent) === null || _a === void 0 ? void 0 : _a.typeName) != wkt_1.ValueSchema.typeName) {
106
132
  return imports.JsonObject;
107
133
  }
134
+ if (validTypes) {
135
+ return {
136
+ kind: "es_valid_type_ref",
137
+ desc: field.message,
138
+ };
139
+ }
108
140
  return {
109
141
  kind: "es_shape_ref",
110
142
  desc: field.message,
@@ -113,7 +145,7 @@ function messageFieldTypeScriptType(field, imports) {
113
145
  function fieldJsonType(field) {
114
146
  switch (field.fieldKind) {
115
147
  case "scalar":
116
- return (0, codegenv1_1.scalarJsonType)(field.scalar);
148
+ return (0, codegenv2_1.scalarJsonType)(field.scalar);
117
149
  case "message":
118
150
  return {
119
151
  kind: "es_json_type_ref",
@@ -135,7 +167,7 @@ function fieldJsonType(field) {
135
167
  "[]",
136
168
  ];
137
169
  case "scalar": {
138
- const t = (0, codegenv1_1.scalarJsonType)(field.scalar);
170
+ const t = (0, codegenv2_1.scalarJsonType)(field.scalar);
139
171
  if (t.includes("|")) {
140
172
  return ["(", t, ")[]"];
141
173
  }
@@ -168,7 +200,7 @@ function fieldJsonType(field) {
168
200
  let valueType;
169
201
  switch (field.mapKind) {
170
202
  case "scalar":
171
- valueType = (0, codegenv1_1.scalarJsonType)(field.scalar);
203
+ valueType = (0, codegenv2_1.scalarJsonType)(field.scalar);
172
204
  break;
173
205
  case "message":
174
206
  valueType = {
@@ -0,0 +1,23 @@
1
+ import { type DescField } from "@bufbuild/protobuf";
2
+ /**
3
+ * Returns true if the field's protovalidate rules are (conditionally) disabled.
4
+ *
5
+ * Note that this function only applies to message fields (singular, repeated, map),
6
+ * and always returns false for other field types.
7
+ */
8
+ export declare function isProtovalidateDisabled(descField: DescField): boolean;
9
+ /**
10
+ * Returns true if the field is required by protovalidate.
11
+ *
12
+ * Note that this function only applies to message fields (singular, repeated, map),
13
+ * and always returns false for other field types.
14
+ */
15
+ export declare function isProtovalidateRequired(descField: DescField): boolean;
16
+ /**
17
+ * Returns true if the field has the proto2 `required` label, or the Edition
18
+ * feature field_presence = LEGACY_REQUIRED.
19
+ *
20
+ * Note that this function only applies to singular message fields, and always
21
+ * returns false for other fields.
22
+ */
23
+ export declare function isLegacyRequired(descField: DescField): boolean;
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ // Copyright 2021-2025 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.isProtovalidateDisabled = isProtovalidateDisabled;
17
+ exports.isProtovalidateRequired = isProtovalidateRequired;
18
+ exports.isLegacyRequired = isLegacyRequired;
19
+ const protobuf_1 = require("@bufbuild/protobuf");
20
+ const wkt_1 = require("@bufbuild/protobuf/wkt");
21
+ const minimal_validate_pb_js_1 = require("./gen/minimal-validate_pb.js");
22
+ /**
23
+ * Returns true if the field's protovalidate rules are (conditionally) disabled.
24
+ *
25
+ * Note that this function only applies to message fields (singular, repeated, map),
26
+ * and always returns false for other field types.
27
+ */
28
+ function isProtovalidateDisabled(descField) {
29
+ if (descField.message === undefined) {
30
+ return false;
31
+ }
32
+ const messageRules = (0, protobuf_1.getOption)(descField.parent, minimal_validate_pb_js_1.message);
33
+ if (messageRules.disabled) {
34
+ return true;
35
+ }
36
+ const fieldRules = (0, protobuf_1.getOption)(descField, minimal_validate_pb_js_1.field);
37
+ if (fieldRules.ignore == minimal_validate_pb_js_1.Ignore.ALWAYS ||
38
+ (descField.fieldKind == "message" &&
39
+ fieldRules.ignore == minimal_validate_pb_js_1.Ignore.IF_DEFAULT_VALUE)) {
40
+ return true;
41
+ }
42
+ const childRules = descField.fieldKind == "list" && fieldRules.type.case == "repeated"
43
+ ? fieldRules.type.value.items
44
+ : descField.fieldKind == "map" && fieldRules.type.case == "map"
45
+ ? fieldRules.type.value.values
46
+ : undefined;
47
+ if (childRules) {
48
+ return (childRules.ignore == minimal_validate_pb_js_1.Ignore.ALWAYS ||
49
+ childRules.ignore == minimal_validate_pb_js_1.Ignore.IF_DEFAULT_VALUE);
50
+ }
51
+ return false;
52
+ }
53
+ /**
54
+ * Returns true if the field is required by protovalidate.
55
+ *
56
+ * Note that this function only applies to message fields (singular, repeated, map),
57
+ * and always returns false for other field types.
58
+ */
59
+ function isProtovalidateRequired(descField) {
60
+ if (!(0, protobuf_1.hasOption)(descField, minimal_validate_pb_js_1.field)) {
61
+ return false;
62
+ }
63
+ const messageRules = (0, protobuf_1.getOption)(descField.parent, minimal_validate_pb_js_1.message);
64
+ if (messageRules.disabled) {
65
+ return false;
66
+ }
67
+ const fieldRules = (0, protobuf_1.getOption)(descField, minimal_validate_pb_js_1.field);
68
+ if (fieldRules.ignore === minimal_validate_pb_js_1.Ignore.ALWAYS) {
69
+ return false;
70
+ }
71
+ return fieldRules.required;
72
+ }
73
+ /**
74
+ * Returns true if the field has the proto2 `required` label, or the Edition
75
+ * feature field_presence = LEGACY_REQUIRED.
76
+ *
77
+ * Note that this function only applies to singular message fields, and always
78
+ * returns false for other fields.
79
+ */
80
+ function isLegacyRequired(descField) {
81
+ return (descField.fieldKind == "message" &&
82
+ descField.presence == wkt_1.FeatureSet_FieldPresence.LEGACY_REQUIRED);
83
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bufbuild/protoc-gen-es",
3
- "version": "2.3.0",
3
+ "version": "2.5.0",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Protocol Buffers code generator for ECMAScript",
6
6
  "keywords": [
@@ -24,17 +24,22 @@
24
24
  "scripts": {
25
25
  "prebuild": "rm -rf ./dist/cjs/*",
26
26
  "build": "../../node_modules/typescript/bin/tsc --project tsconfig.json --outDir ./dist/cjs",
27
+ "bootstrap": "protoc --es_out=src/gen --es_opt=target=ts,import_extension=js --proto_path proto proto/minimal-validate.proto",
28
+ "postbootstrap": "license-header src/gen",
27
29
  "format": "biome format --write",
28
30
  "license-header": "license-header",
29
31
  "lint": "biome lint --error-on-warnings"
30
32
  },
31
33
  "preferUnplugged": true,
32
34
  "dependencies": {
33
- "@bufbuild/protobuf": "^2.3.0",
34
- "@bufbuild/protoplugin": "2.3.0"
35
+ "@bufbuild/protobuf": "^2.5.0",
36
+ "@bufbuild/protoplugin": "2.5.0"
37
+ },
38
+ "devDependencies": {
39
+ "upstream-protobuf": "*"
35
40
  },
36
41
  "peerDependencies": {
37
- "@bufbuild/protobuf": "2.3.0"
42
+ "@bufbuild/protobuf": "2.5.0"
38
43
  },
39
44
  "peerDependenciesMeta": {
40
45
  "@bufbuild/protobuf": {