@bufbuild/protoplugin 2.0.0-alpha.3 → 2.0.0-beta.1

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.
@@ -2,7 +2,8 @@ import { type SupportedEdition } from "@bufbuild/protobuf";
2
2
  import type { Schema } from "./schema.js";
3
3
  import type { FileInfo } from "./generated-file.js";
4
4
  import type { Plugin } from "./plugin.js";
5
- interface PluginInit {
5
+ import type { RawPluginOptions } from "./parameter.js";
6
+ interface PluginInit<Options extends object> {
6
7
  /**
7
8
  * Name of this code generator plugin.
8
9
  */
@@ -15,7 +16,7 @@ interface PluginInit {
15
16
  * An optional parsing function which can be used to parse your own plugin
16
17
  * options.
17
18
  */
18
- parseOption?: (key: string, value: string) => void;
19
+ parseOptions?: (rawOptions: RawPluginOptions) => Options;
19
20
  /**
20
21
  * The earliest edition supported by this plugin. Defaults to the minimum
21
22
  * edition supported by @bufbuild/protobuf.
@@ -32,7 +33,7 @@ interface PluginInit {
32
33
  *
33
34
  * Note that this is required to be provided for plugin initialization.
34
35
  */
35
- generateTs: (schema: Schema, target: "ts") => void;
36
+ generateTs: (schema: Schema<Options>, target: "ts") => void;
36
37
  /**
37
38
  * A optional function which will generate JavaScript files based on proto
38
39
  * input. This function will be invoked by the plugin framework when the
@@ -43,7 +44,7 @@ interface PluginInit {
43
44
  * JavaScript files. If not, the plugin framework will transpile the files
44
45
  * itself.
45
46
  */
46
- generateJs?: (schema: Schema, target: "js") => void;
47
+ generateJs?: (schema: Schema<Options>, target: "js") => void;
47
48
  /**
48
49
  * A optional function which will generate TypeScript declaration files
49
50
  * based on proto input. This function will be invoked by the plugin
@@ -54,7 +55,7 @@ interface PluginInit {
54
55
  * declaration files. If not, the plugin framework will transpile the files
55
56
  * itself.
56
57
  */
57
- generateDts?: (schema: Schema, target: "dts") => void;
58
+ generateDts?: (schema: Schema<Options>, target: "dts") => void;
58
59
  /**
59
60
  * An optional function which will transpile a given set of files.
60
61
  *
@@ -75,5 +76,5 @@ interface PluginInit {
75
76
  * The plugin can generate JavaScript, TypeScript, or TypeScript declaration
76
77
  * files.
77
78
  */
78
- export declare function createEcmaScriptPlugin(init: PluginInit): Plugin;
79
+ export declare function createEcmaScriptPlugin<Options extends object = object>(init: PluginInit<Options>): Plugin;
79
80
  export {};
@@ -35,7 +35,7 @@ function createEcmaScriptPlugin(init) {
35
35
  var _a, _b, _c;
36
36
  const minimumEdition = (_a = init.minimumEdition) !== null && _a !== void 0 ? _a : protobuf_2.minimumEdition;
37
37
  const maximumEdition = (_b = init.maximumEdition) !== null && _b !== void 0 ? _b : protobuf_2.maximumEdition;
38
- const parameter = (0, parameter_js_1.parseParameter)(req.parameter, init.parseOption);
38
+ const parameter = (0, parameter_js_1.parseParameter)(req.parameter, init.parseOptions);
39
39
  const schema = (0, schema_js_1.createSchema)(req, parameter, init.name, init.version, minimumEdition, maximumEdition);
40
40
  const targetTs = schema.targets.includes("ts");
41
41
  const targetJs = schema.targets.includes("js");
@@ -97,7 +97,7 @@ function createEcmaScriptPlugin(init) {
97
97
  if (transpileJs || transpileDts) {
98
98
  const transpileFn = (_c = init.transpile) !== null && _c !== void 0 ? _c : transpile_js_1.transpile;
99
99
  // Transpile the TypeScript files and add to the master list of files
100
- const transpiledFiles = transpileFn(tsFiles, transpileJs, transpileDts, parameter.jsImportStyle);
100
+ const transpiledFiles = transpileFn(tsFiles, transpileJs, transpileDts, parameter.parsed.jsImportStyle);
101
101
  files.push(...transpiledFiles);
102
102
  }
103
103
  return toResponse(files, minimumEdition, maximumEdition);
@@ -106,7 +106,7 @@ function createEcmaScriptPlugin(init) {
106
106
  }
107
107
  exports.createEcmaScriptPlugin = createEcmaScriptPlugin;
108
108
  function toResponse(files, minimumEdition, maximumEdition) {
109
- return (0, protobuf_1.create)(wkt_1.CodeGeneratorResponseDesc, {
109
+ return (0, protobuf_1.create)(wkt_1.CodeGeneratorResponseSchema, {
110
110
  supportedFeatures: protobuf_1.protoInt64.parse(wkt_1.CodeGeneratorResponse_Feature.PROTO3_OPTIONAL |
111
111
  wkt_1.CodeGeneratorResponse_Feature.SUPPORTS_EDITIONS),
112
112
  minimumEdition,
@@ -58,7 +58,7 @@ export interface GeneratedFile {
58
58
  * descriptor. The comment block will contain the original comments from the
59
59
  * protobuf source, and annotations such as `@generated from message MyMessage`.
60
60
  */
61
- jsDoc(desc: Exclude<AnyDesc, DescFile>, indentation?: string): Printable;
61
+ jsDoc(schema: Exclude<AnyDesc, DescFile>, indentation?: string): Printable;
62
62
  /**
63
63
  * Create a printable export statement. For example:
64
64
  *
@@ -79,11 +79,18 @@ export interface GeneratedFile {
79
79
  /**
80
80
  * Import a message or enumeration generated by protoc-gen-es.
81
81
  */
82
- importShape(desc: DescMessage | DescEnum): ImportSymbol;
82
+ importShape(schema: DescMessage | DescEnum): ImportSymbol;
83
+ /**
84
+ * Import a message or enumeration JSON type generated by protoc-gen-es.
85
+ *
86
+ * Note that protoc-gen-es only generates JSON types with the plugin option
87
+ * `json_types=true`.
88
+ */
89
+ importJson(desc: DescMessage | DescEnum): ImportSymbol;
83
90
  /**
84
91
  * Import a descriptor generated by protoc-gen-es.
85
92
  */
86
- importDesc(desc: DescMessage | DescEnum | DescExtension | DescService | DescFile, typeOnly?: boolean): ImportSymbol;
93
+ importSchema(schema: DescMessage | DescEnum | DescExtension | DescService | DescFile, typeOnly?: boolean): ImportSymbol;
87
94
  /**
88
95
  * Import any symbol from a file or package.
89
96
  *
@@ -94,10 +101,15 @@ export interface GeneratedFile {
94
101
  * relative to the project root. The import path is automatically made
95
102
  * relative to the current file.
96
103
  */
97
- import(name: string, from: string): ImportSymbol;
104
+ import(name: string, from: string, typeOnly?: boolean): ImportSymbol;
98
105
  /**
99
106
  * In case you need full control over exports and imports, use print() and
100
107
  * formulate your own imports and exports based on this property.
108
+ *
109
+ * With the plugin option `js_import_style=legacy_commonjs`, this property
110
+ * reports "legacy_commonjs", but only if the current target is "js".
111
+ * This matches the behavior of import(), which also only generates CommonJS
112
+ * under this condition.
101
113
  */
102
114
  readonly jsImportStyle: "module" | "legacy_commonjs";
103
115
  /**
@@ -112,4 +124,4 @@ export type ResolveDescImportFn = (desc: DescMessage | DescEnum | DescExtension
112
124
  export type ResolveShapeImportFn = (desc: DescMessage | DescEnum) => ImportSymbol;
113
125
  export type RewriteImportFn = (path: string) => string;
114
126
  export type CreatePreambleFn = (descFile: DescFile) => string;
115
- export declare function createGeneratedFile(name: string, importPath: string, jsImportStyle: "module" | "legacy_commonjs", rewriteImport: RewriteImportFn, resolveDescImport: ResolveDescImportFn, resolveShapeImport: ResolveShapeImportFn, createPreamble: CreatePreambleFn, runtime: RuntimeImports): GeneratedFileController;
127
+ export declare function createGeneratedFile(name: string, importPath: string, jsImportStyle: "module" | "legacy_commonjs", rewriteImport: RewriteImportFn, resolveDescImport: ResolveDescImportFn, resolveShapeImport: ResolveShapeImportFn, resolveJsonImport: ResolveShapeImportFn, createPreamble: CreatePreambleFn, runtime: RuntimeImports): GeneratedFileController;
@@ -18,7 +18,7 @@ const protobuf_1 = require("@bufbuild/protobuf");
18
18
  const import_symbol_js_1 = require("./import-symbol.js");
19
19
  const import_path_js_1 = require("./import-path.js");
20
20
  const jsdoc_js_1 = require("./jsdoc.js");
21
- function createGeneratedFile(name, importPath, jsImportStyle, rewriteImport, resolveDescImport, resolveShapeImport, createPreamble, runtime) {
21
+ function createGeneratedFile(name, importPath, jsImportStyle, rewriteImport, resolveDescImport, resolveShapeImport, resolveJsonImport, createPreamble, runtime) {
22
22
  let preamble;
23
23
  const el = [];
24
24
  return {
@@ -44,6 +44,7 @@ function createGeneratedFile(name, importPath, jsImportStyle, rewriteImport, res
44
44
  runtime,
45
45
  resolveDescImport,
46
46
  resolveShapeImport,
47
+ resolveJsonImport,
47
48
  }, printables);
48
49
  el.push("\n");
49
50
  },
@@ -60,23 +61,26 @@ function createGeneratedFile(name, importPath, jsImportStyle, rewriteImport, res
60
61
  value: string,
61
62
  };
62
63
  },
63
- jsDoc(textOrDesc, indentation) {
64
+ jsDoc(textOrSchema, indentation) {
64
65
  return {
65
66
  kind: "es_jsdoc",
66
- text: typeof textOrDesc == "string"
67
- ? textOrDesc
68
- : (0, jsdoc_js_1.createJsDocTextFromDesc)(textOrDesc),
67
+ text: typeof textOrSchema == "string"
68
+ ? textOrSchema
69
+ : (0, jsdoc_js_1.createJsDocTextFromDesc)(textOrSchema),
69
70
  indentation,
70
71
  };
71
72
  },
72
- importDesc(desc, typeOnly = false) {
73
- return resolveDescImport(desc, typeOnly);
73
+ importSchema(schema, typeOnly = false) {
74
+ return resolveDescImport(schema, typeOnly);
74
75
  },
75
- importShape(desc) {
76
- return resolveShapeImport(desc);
76
+ importShape(schema) {
77
+ return resolveShapeImport(schema);
77
78
  },
78
- import(name, from) {
79
- return (0, import_symbol_js_1.createImportSymbol)(name, from);
79
+ importJson(desc) {
80
+ return resolveJsonImport(desc);
81
+ },
82
+ import(name, from, typeOnly = false) {
83
+ return (0, import_symbol_js_1.createImportSymbol)(name, from, typeOnly);
80
84
  },
81
85
  jsImportStyle,
82
86
  runtime,
@@ -223,6 +227,9 @@ function printableToEl(opt, printables) {
223
227
  case "es_shape_ref":
224
228
  el.push(opt.resolveShapeImport(p.desc));
225
229
  break;
230
+ case "es_json_type_ref":
231
+ el.push(opt.resolveJsonImport(p.desc));
232
+ break;
226
233
  case "es_jsdoc":
227
234
  el.push((0, jsdoc_js_1.formatJsDocBlock)(p.text, p.indentation));
228
235
  break;
@@ -4,6 +4,7 @@ export { createEcmaScriptPlugin } from "./create-es-plugin.js";
4
4
  export { getComments, getDeclarationString, getPackageComments, getSyntaxComments, } from "./source-code-info.js";
5
5
  export type { Target } from "./target.js";
6
6
  export type { Schema } from "./schema.js";
7
+ export type { EcmaScriptPluginOptions } from "./parameter.js";
7
8
  export type { GeneratedFile, FileInfo } from "./generated-file.js";
8
9
  export type { ImportSymbol } from "./import-symbol.js";
9
10
  export { createImportSymbol } from "./import-symbol.js";
@@ -1,4 +1,17 @@
1
1
  import type { DescEnum, DescExtension, DescFile, DescMessage, DescService } from "@bufbuild/protobuf";
2
+ /**
3
+ * Return a file path for the give file descriptor.
4
+ */
2
5
  export declare function generateFilePath(file: DescFile, bootstrapWkt: boolean, filesToGenerate: DescFile[]): string;
3
- export declare function localDescName(desc: DescFile | DescEnum | DescMessage | DescExtension | DescService): string;
4
- export declare function localShapeName(desc: DescEnum | DescMessage): string;
6
+ /**
7
+ * Return a safe identifier for a generated descriptor.
8
+ */
9
+ export declare function generatedDescName(desc: DescFile | DescEnum | DescMessage | DescExtension | DescService): string;
10
+ /**
11
+ * Return a safe identifier for a generated shape.
12
+ */
13
+ export declare function generatedShapeName(desc: DescEnum | DescMessage): string;
14
+ /**
15
+ * Return a safe identifier for a generated JSON type.
16
+ */
17
+ export declare function generatedJsonTypeName(desc: DescEnum | DescMessage): string;
package/dist/cjs/names.js CHANGED
@@ -13,10 +13,13 @@
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.localShapeName = exports.localDescName = exports.generateFilePath = void 0;
16
+ exports.generatedJsonTypeName = exports.generatedShapeName = exports.generatedDescName = exports.generateFilePath = void 0;
17
17
  const codegenv1_1 = require("@bufbuild/protobuf/codegenv1");
18
18
  const reflect_1 = require("@bufbuild/protobuf/reflect");
19
19
  const safe_identifier_js_1 = require("./safe-identifier.js");
20
+ /**
21
+ * Return a file path for the give file descriptor.
22
+ */
20
23
  function generateFilePath(file, bootstrapWkt, filesToGenerate) {
21
24
  // Well-known types are published with the runtime package. We usually want
22
25
  // the generated code to import them from the runtime package, with the
@@ -34,86 +37,158 @@ function generateFilePath(file, bootstrapWkt, filesToGenerate) {
34
37
  return "./" + file.name + "_pb.js";
35
38
  }
36
39
  exports.generateFilePath = generateFilePath;
37
- function localDescName(desc) {
40
+ /**
41
+ * Return a safe identifier for a generated descriptor.
42
+ */
43
+ function generatedDescName(desc) {
38
44
  const file = desc.kind == "file" ? desc : desc.file;
39
45
  const { descNames } = allNames(file);
40
46
  const name = descNames.get(desc);
41
47
  if (name === undefined) {
42
- throw new Error(`unable to determine unique identifier for ${desc.toString()}`);
48
+ throw new Error(`unable to determine unique descriptor name for ${desc.toString()}`);
43
49
  }
44
50
  return name;
45
51
  }
46
- exports.localDescName = localDescName;
47
- function localShapeName(desc) {
52
+ exports.generatedDescName = generatedDescName;
53
+ /**
54
+ * Return a safe identifier for a generated shape.
55
+ */
56
+ function generatedShapeName(desc) {
48
57
  const { shapeNames } = allNames(desc.file);
49
58
  const name = shapeNames.get(desc);
50
59
  if (name === undefined) {
51
- throw new Error(`unable to determine unique identifier for ${desc.toString()}`);
60
+ throw new Error(`unable to determine unique shape name for ${desc.toString()}`);
52
61
  }
53
62
  return name;
54
63
  }
55
- exports.localShapeName = localShapeName;
64
+ exports.generatedShapeName = generatedShapeName;
65
+ /**
66
+ * Return a safe identifier for a generated JSON type.
67
+ */
68
+ function generatedJsonTypeName(desc) {
69
+ const { jsonTypeNames } = allNames(desc.file);
70
+ const name = jsonTypeNames.get(desc);
71
+ if (name === undefined) {
72
+ throw new Error(`unable to determine unique json type name for ${desc.toString()}`);
73
+ }
74
+ return name;
75
+ }
76
+ exports.generatedJsonTypeName = generatedJsonTypeName;
77
+ /**
78
+ * Compute the ideal name for a generated descriptor.
79
+ */
56
80
  function idealDescName(desc, i) {
57
81
  const escape = i === 0 ? "" : i === 1 ? "$" : `$${i - 1}`;
82
+ if (desc.kind == "file") {
83
+ const name = "file_" + desc.name.replace(/[^a-zA-Z0-9_]+/g, "_");
84
+ return (0, safe_identifier_js_1.safeIdentifier)(name + escape);
85
+ }
58
86
  switch (desc.kind) {
59
- case "file":
60
- return ((0, safe_identifier_js_1.safeIdentifier)("fileDesc_" + desc.name.replace(/[^a-zA-Z0-9_]+/g, "_")) + escape);
61
87
  case "enum":
62
- return baseName(desc) + "Desc" + escape;
88
+ return (0, safe_identifier_js_1.safeIdentifier)(identifier(desc) + "Schema" + escape);
63
89
  case "message":
64
- return baseName(desc) + "Desc" + escape;
90
+ return (0, safe_identifier_js_1.safeIdentifier)(identifier(desc) + "Schema" + escape);
65
91
  case "extension":
66
- return baseName(desc) + escape;
92
+ return (0, safe_identifier_js_1.safeIdentifier)(identifier(desc) + escape);
67
93
  case "service":
68
- return baseName(desc) + escape;
94
+ return (0, safe_identifier_js_1.safeIdentifier)(identifier(desc) + escape);
69
95
  }
70
96
  }
97
+ /**
98
+ * Compute the ideal name for a generated shape.
99
+ */
71
100
  function idealShapeName(desc, i) {
72
101
  const escape = i === 0 ? "" : i === 1 ? "$" : `$${i - 1}`;
73
- return baseName(desc) + escape;
102
+ return (0, safe_identifier_js_1.safeIdentifier)(identifier(desc) + escape);
74
103
  }
75
- function baseName(desc) {
104
+ /**
105
+ * Compute the ideal name for a generated JSON type.
106
+ */
107
+ function idealJsonTypeName(desc, i) {
108
+ const escape = i === 0 ? "" : i === 1 ? "$" : `$${i - 1}`;
109
+ return (0, safe_identifier_js_1.safeIdentifier)(identifier(desc) + "Json" + escape);
110
+ }
111
+ /**
112
+ * Return an identifier for the given descriptor based on its type name.
113
+ *
114
+ * The type name for a protobuf message is the package name (if any), plus
115
+ * the names of parent messages it is nested in (if any), plus the name of
116
+ * the element, separated by dots. For example: foo.bar.ParentMsg.MyEnum.
117
+ *
118
+ * ECMAScript does not have packages or namespaces, so we need a single
119
+ * identifier. Our convention is to drop the package name, and to join other
120
+ * parts of the name with an underscore. For example: ParentMsg_MyEnum.
121
+ */
122
+ function identifier(desc) {
76
123
  const pkg = desc.file.proto.package;
77
124
  const offset = pkg.length > 0 ? pkg.length + 1 : 0;
78
- const name = desc.typeName.substring(offset).replace(/\./g, "_");
79
- return (0, safe_identifier_js_1.safeIdentifier)(name);
125
+ const nameWithoutPkg = desc.typeName.substring(offset);
126
+ return nameWithoutPkg.replace(/\./g, "_");
80
127
  }
128
+ /**
129
+ * Compute all ideal names for the elements in the file, resolving name clashes.
130
+ */
81
131
  function allNames(file) {
82
132
  const taken = new Set();
133
+ // In the first pass, register shape names
83
134
  const shapeNames = new Map();
135
+ for (const desc of (0, reflect_1.nestedTypes)(file)) {
136
+ if (desc.kind != "enum" && desc.kind != "message") {
137
+ continue;
138
+ }
139
+ let name;
140
+ for (let i = 0;; i++) {
141
+ name = idealShapeName(desc, i);
142
+ if (!taken.has(name)) {
143
+ break;
144
+ }
145
+ }
146
+ taken.add(name);
147
+ shapeNames.set(desc, name);
148
+ }
149
+ // In the second pass, register desc names
84
150
  const descNames = new Map();
85
151
  for (const desc of [file, ...(0, reflect_1.nestedTypes)(file)]) {
152
+ let name;
86
153
  switch (desc.kind) {
87
154
  case "enum":
88
155
  case "message": {
89
- let descName;
90
- let shapeName;
91
156
  for (let i = 0;; i++) {
92
- descName = idealDescName(desc, i);
93
- shapeName = idealShapeName(desc, i);
94
- if (!taken.has(descName) && !taken.has(shapeName)) {
157
+ name = idealDescName(desc, i);
158
+ if (!taken.has(name)) {
95
159
  break;
96
160
  }
97
161
  }
98
- taken.add(descName);
99
- taken.add(shapeName);
100
- descNames.set(desc, descName);
101
- shapeNames.set(desc, shapeName);
102
162
  break;
103
163
  }
104
164
  default: {
105
- let descName;
106
165
  for (let i = 0;; i++) {
107
- descName = idealDescName(desc, i);
108
- if (!taken.has(descName)) {
166
+ name = idealDescName(desc, i);
167
+ if (!taken.has(name)) {
109
168
  break;
110
169
  }
111
170
  }
112
- taken.add(descName);
113
- descNames.set(desc, descName);
114
171
  break;
115
172
  }
116
173
  }
174
+ taken.add(name);
175
+ descNames.set(desc, name);
176
+ }
177
+ // In the third pass, register json type names
178
+ const jsonTypeNames = new Map();
179
+ for (const desc of (0, reflect_1.nestedTypes)(file)) {
180
+ if (desc.kind != "enum" && desc.kind != "message") {
181
+ continue;
182
+ }
183
+ let name;
184
+ for (let i = 0;; i++) {
185
+ name = idealJsonTypeName(desc, i);
186
+ if (!taken.has(name)) {
187
+ break;
188
+ }
189
+ }
190
+ taken.add(name);
191
+ jsonTypeNames.set(desc, name);
117
192
  }
118
- return { shapeNames, descNames };
193
+ return { shapeNames, jsonTypeNames, descNames };
119
194
  }
@@ -1,13 +1,74 @@
1
1
  import type { Target } from "./target.js";
2
2
  import type { RewriteImports } from "./import-path.js";
3
- export interface ParsedParameter {
3
+ /**
4
+ * Standard plugin options that every ECMAScript plugin supports.
5
+ */
6
+ export interface EcmaScriptPluginOptions {
7
+ /**
8
+ * Controls whether the plugin generates JavaScript, TypeScript,
9
+ * or TypeScript declaration files.
10
+ *
11
+ * The default is ["js", "dts].
12
+ */
4
13
  targets: Target[];
14
+ /**
15
+ * Add an extension to every import, for example ".js" or ".ts".
16
+ *
17
+ * The default is "".
18
+ */
19
+ importExtension: string;
20
+ /**
21
+ * Generate `import` statements or `require()` calls.
22
+ *
23
+ * The default is "module".
24
+ */
25
+ jsImportStyle: "module" | "legacy_commonjs";
26
+ /**
27
+ * Generate an annotation at the top of each file to skip type checks:
28
+ * `// @ts-nocheck`.
29
+ *
30
+ * The default is false.
31
+ */
5
32
  tsNocheck: boolean;
6
- bootstrapWkt: boolean;
33
+ /**
34
+ * Prune empty files from the output.
35
+ *
36
+ * The default is false.
37
+ */
7
38
  keepEmptyFiles: boolean;
39
+ /**
40
+ * @private
41
+ */
42
+ bootstrapWkt: boolean;
43
+ /**
44
+ * @private
45
+ */
8
46
  rewriteImports: RewriteImports;
9
- importExtension: string;
10
- jsImportStyle: "module" | "legacy_commonjs";
11
- sanitizedParameter: string;
12
47
  }
13
- export declare function parseParameter(parameter: string, parseExtraOption: ((key: string, value: string) => void) | undefined): ParsedParameter;
48
+ export interface ParsedParameter<T> {
49
+ parsed: T & EcmaScriptPluginOptions;
50
+ sanitized: string;
51
+ }
52
+ /**
53
+ * Raw options to parse.
54
+ *
55
+ * For example, if a plugin is run with the options foo=123,bar,baz=a,baz=b
56
+ * the raw options are:
57
+ *
58
+ * ```ts
59
+ * [
60
+ * { key: "foo", value: "123" },
61
+ * { key: "bar", value: "" },
62
+ * { key: "baz", value: "a" },
63
+ * { key: "baz", value: "b" },
64
+ * ]
65
+ * ```
66
+ *
67
+ * If your plugin does not recognize an option, it must throw an Error in
68
+ * parseOptions.
69
+ */
70
+ export type RawPluginOptions = {
71
+ key: string;
72
+ value: string;
73
+ }[];
74
+ export declare function parseParameter<T extends object>(parameter: string, parseExtraOptions: ((rawOptions: RawPluginOptions) => T) | undefined): ParsedParameter<T>;
@@ -15,7 +15,7 @@
15
15
  Object.defineProperty(exports, "__esModule", { value: true });
16
16
  exports.parseParameter = void 0;
17
17
  const error_js_1 = require("./error.js");
18
- function parseParameter(parameter, parseExtraOption) {
18
+ function parseParameter(parameter, parseExtraOptions) {
19
19
  let targets = ["js", "dts"];
20
20
  let tsNocheck = false;
21
21
  let bootstrapWkt = false;
@@ -23,6 +23,8 @@ function parseParameter(parameter, parseExtraOption) {
23
23
  const rewriteImports = [];
24
24
  let importExtension = "";
25
25
  let jsImportStyle = "module";
26
+ const extraParameters = [];
27
+ const extraParametersRaw = [];
26
28
  const rawParameters = [];
27
29
  for (const { key, value, raw } of splitParameter(parameter)) {
28
30
  // Whether this key/value plugin parameter pair should be
@@ -118,23 +120,19 @@ function parseParameter(parameter, parseExtraOption) {
118
120
  break;
119
121
  }
120
122
  default:
121
- if (parseExtraOption === undefined) {
123
+ if (parseExtraOptions === undefined) {
122
124
  throw new error_js_1.PluginOptionError(raw);
123
125
  }
124
- try {
125
- parseExtraOption(key, value);
126
- }
127
- catch (e) {
128
- throw new error_js_1.PluginOptionError(raw, e);
129
- }
126
+ extraParameters.push({ key, value });
127
+ extraParametersRaw.push(raw);
130
128
  break;
131
129
  }
132
130
  if (!sanitize) {
133
131
  rawParameters.push(raw);
134
132
  }
135
133
  }
136
- const sanitizedParameter = rawParameters.join(",");
137
- return {
134
+ const sanitizedParameters = rawParameters.join(",");
135
+ const ecmaScriptPluginOptions = {
138
136
  targets,
139
137
  tsNocheck,
140
138
  bootstrapWkt,
@@ -142,8 +140,22 @@ function parseParameter(parameter, parseExtraOption) {
142
140
  importExtension,
143
141
  jsImportStyle,
144
142
  keepEmptyFiles,
145
- sanitizedParameter,
146
143
  };
144
+ if (parseExtraOptions === undefined || extraParameters.length === 0) {
145
+ return {
146
+ parsed: ecmaScriptPluginOptions,
147
+ sanitized: sanitizedParameters,
148
+ };
149
+ }
150
+ try {
151
+ return {
152
+ parsed: Object.assign(ecmaScriptPluginOptions, parseExtraOptions(extraParameters)),
153
+ sanitized: sanitizedParameters,
154
+ };
155
+ }
156
+ catch (err) {
157
+ throw new error_js_1.PluginOptionError(extraParametersRaw.join(","), err);
158
+ }
147
159
  }
148
160
  exports.parseParameter = parseParameter;
149
161
  function splitParameter(parameter) {
@@ -3,7 +3,7 @@ import type { ImportSymbol } from "./import-symbol.js";
3
3
  /**
4
4
  * All types that can be passed to GeneratedFile.print()
5
5
  */
6
- export type Printable = string | number | boolean | bigint | Uint8Array | ImportSymbol | ExportStatement | JSDocBlock | LiteralString | LiteralProtoInt64 | DescImport | ShapeImport | Printable[];
6
+ export type Printable = string | number | boolean | bigint | Uint8Array | ImportSymbol | ExportStatement | JSDocBlock | LiteralString | LiteralProtoInt64 | DescImport | ShapeImport | JsonTypeImport | Printable[];
7
7
  export type ExportStatement = {
8
8
  kind: "es_export_stmt";
9
9
  name: string;
@@ -28,6 +28,10 @@ export type ShapeImport = {
28
28
  readonly kind: "es_shape_ref";
29
29
  desc: DescMessage | DescEnum;
30
30
  };
31
+ export type JsonTypeImport = {
32
+ readonly kind: "es_json_type_ref";
33
+ desc: DescMessage | DescEnum;
34
+ };
31
35
  export type JSDocBlock = {
32
36
  readonly kind: "es_jsdoc";
33
37
  text: string;
@@ -41,9 +41,9 @@ function runNodeJs(plugin) {
41
41
  }
42
42
  readBytes(process.stdin)
43
43
  .then((data) => {
44
- const req = (0, protobuf_1.fromBinary)(wkt_1.CodeGeneratorRequestDesc, data);
44
+ const req = (0, protobuf_1.fromBinary)(wkt_1.CodeGeneratorRequestSchema, data);
45
45
  const res = plugin.run(req);
46
- return writeBytes(process.stdout, (0, protobuf_1.toBinary)(wkt_1.CodeGeneratorResponseDesc, res));
46
+ return writeBytes(process.stdout, (0, protobuf_1.toBinary)(wkt_1.CodeGeneratorResponseSchema, res));
47
47
  })
48
48
  .then(() => process.exit(0))
49
49
  .catch((reason) => {
@@ -3,12 +3,12 @@ import type { CodeGeneratorRequest } from "@bufbuild/protobuf/wkt";
3
3
  import { Edition } from "@bufbuild/protobuf/wkt";
4
4
  import type { FileInfo, GeneratedFile } from "./generated-file.js";
5
5
  import type { Target } from "./target.js";
6
- import type { ParsedParameter } from "./parameter.js";
6
+ import type { EcmaScriptPluginOptions, ParsedParameter } from "./parameter.js";
7
7
  /**
8
8
  * Schema describes the files and types that the plugin is requested to
9
9
  * generate.
10
10
  */
11
- export interface Schema {
11
+ export interface Schema<Options extends object = object> {
12
12
  /**
13
13
  * The files we are asked to generate.
14
14
  */
@@ -21,6 +21,11 @@ export interface Schema {
21
21
  * The plugin option `target`. A code generator should support all targets.
22
22
  */
23
23
  readonly targets: readonly Target[];
24
+ /**
25
+ * Parsed plugin options. They include the standard options for all
26
+ * plugins, and options parsed by your plugin.
27
+ */
28
+ readonly options: Options & EcmaScriptPluginOptions;
24
29
  /**
25
30
  * Generate a new file with the given name.
26
31
  */
@@ -35,9 +40,9 @@ export interface Schema {
35
40
  */
36
41
  readonly proto: CodeGeneratorRequest;
37
42
  }
38
- interface SchemaController extends Schema {
43
+ interface SchemaController<Options extends object> extends Schema<Options> {
39
44
  getFileInfo: () => FileInfo[];
40
45
  prepareGenerate(target: Target): void;
41
46
  }
42
- export declare function createSchema(request: CodeGeneratorRequest, parameter: ParsedParameter, pluginName: string, pluginVersion: string, minimumEdition: Edition, maximumEdition: Edition): SchemaController;
47
+ export declare function createSchema<T extends object>(request: CodeGeneratorRequest, parameter: ParsedParameter<T>, pluginName: string, pluginVersion: string, minimumEdition: Edition, maximumEdition: Edition): SchemaController<T>;
43
48
  export {};