@bufbuild/protoplugin 0.1.0 → 0.2.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.
@@ -11,25 +11,85 @@
11
11
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  // See the License for the specific language governing permissions and
13
13
  // limitations under the License.
14
- import { createSchema } from "./ecmascript/schema.js";
15
- import { CodeGeneratorResponse } from "@bufbuild/protobuf";
14
+ import { createSchema, toResponse } from "./ecmascript/schema.js";
15
+ import { transpile } from "./ecmascript/transpile.js";
16
16
  import { PluginOptionError } from "./error.js";
17
17
  /**
18
18
  * Create a new code generator plugin for ECMAScript.
19
19
  * The plugin can generate JavaScript, TypeScript, or TypeScript declaration
20
20
  * files.
21
21
  */
22
- export function createEcmaScriptPlugin(init, generateFn) {
22
+ export function createEcmaScriptPlugin(init) {
23
+ let transpileJs = false;
24
+ let transpileDts = false;
23
25
  return {
24
26
  name: init.name,
25
27
  version: init.version,
26
28
  run(req) {
27
- const { targets, tsNocheck, bootstrapWkt, rewriteImports } = parseParameter(req.parameter, init.parseOption);
28
- const { schema, toResponse } = createSchema(req, targets, init.name, init.version, tsNocheck, bootstrapWkt, rewriteImports);
29
- generateFn(schema);
30
- const res = new CodeGeneratorResponse();
31
- toResponse(res);
32
- return res;
29
+ var _a;
30
+ const { targets, tsNocheck, bootstrapWkt, rewriteImports, keepEmptyFiles, } = parseParameter(req.parameter, init.parseOption);
31
+ const { schema, getFileInfo } = createSchema(req, targets, init.name, init.version, tsNocheck, bootstrapWkt, rewriteImports, keepEmptyFiles);
32
+ const targetTs = schema.targets.includes("ts");
33
+ const targetJs = schema.targets.includes("js");
34
+ const targetDts = schema.targets.includes("dts");
35
+ // Generate TS files under the following conditions:
36
+ // - if they are explicitly specified as a target.
37
+ // - if js is specified as a target but no js generator is provided.
38
+ // - if dts is specified as a target, but no dts generator is provided.
39
+ // In the latter two cases, it is because we need the generated TS files
40
+ // to use for transpiling js and/or dts.
41
+ let tsFiles = [];
42
+ if (targetTs ||
43
+ (targetJs && !init.generateJs) ||
44
+ (targetDts && !init.generateDts)) {
45
+ init.generateTs(schema, "ts");
46
+ // Save off the generated TypeScript files so that we can pass these
47
+ // to the transpilation process if necessary. We do not want to pass
48
+ // JavaScript files for a few reasons:
49
+ // 1. Our usage of allowJs in the compiler options will cause issues
50
+ // with attempting to transpile .ts and .js files to the same location.
51
+ // 2. There should be no reason to transpile JS because generateTs
52
+ // functions are required, so users would never be able to only specify
53
+ // a generateJs function and expect to transpile declarations.
54
+ // 3. Transpiling is somewhat expensive and situations with an
55
+ // extremely large amount of files could have performance impacts.
56
+ tsFiles = getFileInfo();
57
+ }
58
+ if (targetJs) {
59
+ if (init.generateJs) {
60
+ init.generateJs(schema, "js");
61
+ }
62
+ else {
63
+ transpileJs = true;
64
+ }
65
+ }
66
+ if (targetDts) {
67
+ if (init.generateDts) {
68
+ init.generateDts(schema, "dts");
69
+ }
70
+ else {
71
+ transpileDts = true;
72
+ }
73
+ }
74
+ // Get generated files. If ts was specified as a target, then we want
75
+ // all generated files. If ts was not specified, we still may have
76
+ // generated TypeScript files to assist in transpilation. If they were
77
+ // generated but not specified in the target out, we shouldn't produce
78
+ // these files in the CodeGeneratorResponse.
79
+ let files = getFileInfo();
80
+ if (!targetTs && tsFiles.length > 0) {
81
+ files = files.filter((file) => !tsFiles.some((tsFile) => tsFile.name === file.name));
82
+ }
83
+ // If either boolean is true, it means it was specified in the target out
84
+ // but no generate function was provided. This also means that we will
85
+ // have generated .ts files above.
86
+ if (transpileJs || transpileDts) {
87
+ const transpileFn = (_a = init.transpile) !== null && _a !== void 0 ? _a : transpile;
88
+ // Transpile the TypeScript files and add to the master list of files
89
+ const transpiledFiles = transpileFn(tsFiles, transpileJs, transpileDts);
90
+ files.push(...transpiledFiles);
91
+ }
92
+ return toResponse(files);
33
93
  },
34
94
  };
35
95
  }
@@ -37,6 +97,7 @@ function parseParameter(parameter, parseOption) {
37
97
  let targets = ["js", "dts"];
38
98
  let tsNocheck = true;
39
99
  let bootstrapWkt = false;
100
+ let keepEmptyFiles = false;
40
101
  const rewriteImports = [];
41
102
  for (const { key, value } of splitParameter(parameter)) {
42
103
  switch (key) {
@@ -94,6 +155,21 @@ function parseParameter(parameter, parseOption) {
94
155
  rewriteImports.push({ pattern, target });
95
156
  break;
96
157
  }
158
+ case "keep_empty_files": {
159
+ switch (value) {
160
+ case "true":
161
+ case "1":
162
+ keepEmptyFiles = true;
163
+ break;
164
+ case "false":
165
+ case "0":
166
+ keepEmptyFiles = false;
167
+ break;
168
+ default:
169
+ throw new PluginOptionError(`${key}=${value}`);
170
+ }
171
+ break;
172
+ }
97
173
  default:
98
174
  if (parseOption === undefined) {
99
175
  throw new PluginOptionError(`${key}=${value}`);
@@ -107,7 +183,7 @@ function parseParameter(parameter, parseOption) {
107
183
  break;
108
184
  }
109
185
  }
110
- return { targets, tsNocheck, bootstrapWkt, rewriteImports };
186
+ return { targets, tsNocheck, bootstrapWkt, rewriteImports, keepEmptyFiles };
111
187
  }
112
188
  function splitParameter(parameter) {
113
189
  if (parameter == undefined) {
@@ -0,0 +1,105 @@
1
+ // Copyright 2021-2022 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
+ import { proto3, BinaryReader, ScalarType, } from "@bufbuild/protobuf";
15
+ /**
16
+ * Returns the value of a custom option with a scalar type.
17
+ *
18
+ * If no option is found, returns undefined.
19
+ */
20
+ export function findCustomScalarOption(desc, id, scalarType) {
21
+ const reader = createBinaryReader(desc, id);
22
+ if (reader) {
23
+ switch (scalarType) {
24
+ case ScalarType.INT32:
25
+ return reader.int32();
26
+ case ScalarType.UINT32:
27
+ return reader.uint32();
28
+ case ScalarType.SINT32:
29
+ return reader.sint32();
30
+ case ScalarType.FIXED32:
31
+ return reader.fixed32();
32
+ case ScalarType.SFIXED32:
33
+ return reader.sfixed32();
34
+ case ScalarType.FLOAT:
35
+ return reader.float();
36
+ case ScalarType.DOUBLE:
37
+ return reader.double();
38
+ case ScalarType.INT64:
39
+ return reader.int64();
40
+ case ScalarType.SINT64:
41
+ return reader.sint64();
42
+ case ScalarType.SFIXED64:
43
+ return reader.sfixed64();
44
+ case ScalarType.UINT64:
45
+ return reader.uint64();
46
+ case ScalarType.FIXED64:
47
+ return reader.fixed64();
48
+ case ScalarType.BOOL:
49
+ return reader.bool();
50
+ case ScalarType.BYTES:
51
+ return reader.bytes();
52
+ case ScalarType.STRING:
53
+ return reader.string();
54
+ default: {
55
+ break;
56
+ }
57
+ }
58
+ }
59
+ return undefined;
60
+ }
61
+ /**
62
+ * Returns the value of a custom message option for the given descriptor and ID.
63
+ * The msgType param is then used to deserialize the message for returning to the caller.
64
+ *
65
+ * If no options are found, returns undefined.
66
+ *
67
+ * If the message option is unable to be read or deserialized, an error will be thrown.
68
+ */
69
+ export function findCustomMessageOption(desc, id, msgType) {
70
+ const reader = createBinaryReader(desc, id);
71
+ if (reader) {
72
+ try {
73
+ const data = reader.bytes();
74
+ return msgType.fromBinary(data);
75
+ }
76
+ catch (e) {
77
+ const innerMessage = e instanceof Error ? e.message : String(e);
78
+ throw new Error(`failed to access message option: ${innerMessage}`);
79
+ }
80
+ }
81
+ return undefined;
82
+ }
83
+ /**
84
+ * Returns the value of a custom enum option for the given descriptor and ID.
85
+ *
86
+ * If no options are found, returns undefined.
87
+ */
88
+ export function findCustomEnumOption(desc, id) {
89
+ return findCustomScalarOption(desc, id, ScalarType.INT32);
90
+ }
91
+ /**
92
+ * Returns a binary reader for the given descriptor and field ID.
93
+ */
94
+ function createBinaryReader(desc, id) {
95
+ const opt = desc.proto.options;
96
+ let reader = undefined;
97
+ if (opt !== undefined) {
98
+ const unknownFields = proto3.bin.listUnknownFields(opt);
99
+ const field = unknownFields.find((f) => f.no === id);
100
+ if (field) {
101
+ reader = new BinaryReader(field.data);
102
+ }
103
+ }
104
+ return reader;
105
+ }
@@ -98,10 +98,7 @@ export function makeJsDoc(desc, indentation = "") {
98
98
  case "enum_value":
99
99
  text += `@generated from enum value: ${desc.declarationString()};`;
100
100
  break;
101
- case "scalar_field":
102
- case "enum_field":
103
- case "message_field":
104
- case "map_field":
101
+ case "field":
105
102
  text += `@generated from field: ${desc.declarationString()};`;
106
103
  break;
107
104
  default:
@@ -133,12 +130,12 @@ export function makeJsDoc(desc, indentation = "") {
133
130
  export function getFieldTyping(field, file) {
134
131
  const typing = [];
135
132
  let optional = false;
136
- switch (field.kind) {
137
- case "scalar_field":
133
+ switch (field.fieldKind) {
134
+ case "scalar":
138
135
  typing.push(scalarTypeScriptType(field.scalar));
139
136
  optional = field.optional;
140
137
  break;
141
- case "message_field": {
138
+ case "message": {
142
139
  const baseType = getUnwrappedFieldType(field);
143
140
  if (baseType !== undefined) {
144
141
  typing.push(scalarTypeScriptType(baseType));
@@ -149,11 +146,11 @@ export function getFieldTyping(field, file) {
149
146
  optional = true;
150
147
  break;
151
148
  }
152
- case "enum_field":
149
+ case "enum":
153
150
  typing.push(file.import(field.enum).toTypeOnly());
154
151
  optional = field.optional;
155
152
  break;
156
- case "map_field": {
153
+ case "map": {
157
154
  let keyType;
158
155
  switch (field.mapKey) {
159
156
  case ScalarType.INT32:
@@ -222,15 +219,15 @@ export function literalString(value) {
222
219
  '"');
223
220
  }
224
221
  export function getFieldExplicitDefaultValue(field, protoInt64Symbol) {
225
- switch (field.kind) {
226
- case "enum_field": {
222
+ switch (field.fieldKind) {
223
+ case "enum": {
227
224
  const value = field.enum.values.find((v) => v.number === field.getDefaultValue());
228
225
  if (value !== undefined) {
229
226
  return [value.parent, ".", localName(value)];
230
227
  }
231
228
  break;
232
229
  }
233
- case "scalar_field": {
230
+ case "scalar": {
234
231
  const defaultValue = field.getDefaultValue();
235
232
  if (defaultValue === undefined) {
236
233
  break;
@@ -283,7 +280,7 @@ export function getFieldIntrinsicDefaultValue(field) {
283
280
  typingInferrable: false,
284
281
  };
285
282
  }
286
- if (field.kind == "map_field") {
283
+ if (field.fieldKind == "map") {
287
284
  return {
288
285
  defaultValue: "{}",
289
286
  typingInferrable: false,
@@ -292,8 +289,8 @@ export function getFieldIntrinsicDefaultValue(field) {
292
289
  let defaultValue = undefined;
293
290
  let typingInferrable = false;
294
291
  if (field.parent.file.syntax == "proto3") {
295
- switch (field.kind) {
296
- case "enum_field": {
292
+ switch (field.fieldKind) {
293
+ case "enum": {
297
294
  if (!field.optional) {
298
295
  const zeroValue = field.enum.values.find((v) => v.number === 0);
299
296
  if (zeroValue === undefined) {
@@ -304,7 +301,7 @@ export function getFieldIntrinsicDefaultValue(field) {
304
301
  }
305
302
  break;
306
303
  }
307
- case "scalar_field":
304
+ case "scalar":
308
305
  if (!field.optional) {
309
306
  typingInferrable = true;
310
307
  if (field.scalar === ScalarType.STRING) {
@@ -11,11 +11,10 @@
11
11
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  // See the License for the specific language governing permissions and
13
13
  // limitations under the License.
14
- import { CodeGeneratorResponse_File, } from "@bufbuild/protobuf";
15
14
  import { createImportSymbol } from "./import-symbol.js";
16
15
  import { literalString, makeFilePreamble } from "./gencommon.js";
17
16
  import { makeImportPathRelative } from "./import-path.js";
18
- export function createGeneratedFile(name, importPath, createTypeImport, runtimeImports, preambleSettings) {
17
+ export function createGeneratedFile(name, importPath, createTypeImport, runtimeImports, preambleSettings, keepEmpty) {
19
18
  let preamble;
20
19
  const el = [];
21
20
  return {
@@ -32,22 +31,20 @@ export function createGeneratedFile(name, importPath, createTypeImport, runtimeI
32
31
  import(typeOrName, from) {
33
32
  if (typeof typeOrName == "string") {
34
33
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
35
- return createImportSymbol(name, from);
34
+ return createImportSymbol(typeOrName, from);
36
35
  }
37
36
  return createTypeImport(typeOrName);
38
37
  },
39
- toResponse(res) {
40
- let content = elToContent(el, importPath);
41
- if (content.length === 0) {
38
+ getFileInfo() {
39
+ const content = elToContent(el, importPath);
40
+ if (!keepEmpty && content.length === 0) {
42
41
  return;
43
42
  }
44
- if (preamble !== undefined) {
45
- content = preamble + "\n" + content;
46
- }
47
- res.file.push(new CodeGeneratorResponse_File({
48
- name: name,
43
+ return {
44
+ name,
49
45
  content,
50
- }));
46
+ preamble,
47
+ };
51
48
  },
52
49
  };
53
50
  }
@@ -19,3 +19,4 @@ export {} from "./generated-file.js";
19
19
  export {} from "./import-symbol.js";
20
20
  export const { localName } = codegenInfo;
21
21
  export { createJsDocBlock, getFieldExplicitDefaultValue, getFieldIntrinsicDefaultValue, getFieldTyping, makeJsDoc, literalString, } from "./gencommon.js";
22
+ export { findCustomScalarOption, findCustomMessageOption, findCustomEnumOption, } from "./custom-options.js";
@@ -11,12 +11,12 @@
11
11
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  // See the License for the specific language governing permissions and
13
13
  // limitations under the License.
14
- import { codegenInfo, CodeGeneratorResponse_Feature, createDescriptorSet, protoInt64, } from "@bufbuild/protobuf";
14
+ import { codegenInfo, CodeGeneratorResponse, CodeGeneratorResponse_Feature, createDescriptorSet, protoInt64, } from "@bufbuild/protobuf";
15
15
  import { createGeneratedFile } from "./generated-file.js";
16
16
  import { createRuntimeImports } from "./runtime-imports.js";
17
17
  import { createImportSymbol } from "./import-symbol.js";
18
18
  import { deriveImportPath, makeImportPath, rewriteImportPath, } from "./import-path.js";
19
- export function createSchema(request, targets, pluginName, pluginVersion, tsNocheck, bootstrapWkt, rewriteImports) {
19
+ export function createSchema(request, targets, pluginName, pluginVersion, tsNocheck, bootstrapWkt, rewriteImports, keepEmptyFiles) {
20
20
  const descriptorSet = createDescriptorSet(request.protoFile);
21
21
  const filesToGenerate = findFilesToGenerate(descriptorSet, request);
22
22
  const runtime = createRuntimeImports(bootstrapWkt);
@@ -39,21 +39,36 @@ export function createSchema(request, targets, pluginName, pluginVersion, tsNoch
39
39
  pluginVersion,
40
40
  parameter: request.parameter,
41
41
  tsNocheck,
42
- });
42
+ }, keepEmptyFiles);
43
43
  generatedFiles.push(genFile);
44
44
  return genFile;
45
45
  },
46
46
  };
47
47
  return {
48
48
  schema,
49
- toResponse(res) {
50
- res.supportedFeatures = protoInt64.parse(CodeGeneratorResponse_Feature.PROTO3_OPTIONAL);
51
- for (const genFile of generatedFiles) {
52
- genFile.toResponse(res);
53
- }
49
+ getFileInfo() {
50
+ return generatedFiles.flatMap((file) => {
51
+ const fileInfo = file.getFileInfo();
52
+ // undefined is returned if the file has no content
53
+ if (!fileInfo) {
54
+ return [];
55
+ }
56
+ return [fileInfo];
57
+ });
54
58
  },
55
59
  };
56
60
  }
61
+ export function toResponse(files) {
62
+ return new CodeGeneratorResponse({
63
+ supportedFeatures: protoInt64.parse(CodeGeneratorResponse_Feature.PROTO3_OPTIONAL),
64
+ file: files.map((f) => {
65
+ if (f.preamble !== undefined) {
66
+ f.content = f.preamble + "\n" + f.content;
67
+ }
68
+ return f;
69
+ }),
70
+ });
71
+ }
57
72
  function findFilesToGenerate(descriptorSet, request) {
58
73
  const missing = request.fileToGenerate.filter((fileToGenerate) => descriptorSet.files.every((file) => fileToGenerate !== file.name + ".proto"));
59
74
  if (missing.length) {
@@ -0,0 +1,115 @@
1
+ // Copyright 2021-2022 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
+ import ts from "typescript";
15
+ import { createDefaultMapFromNodeModules, createSystem, createVirtualCompilerHost, } from "@typescript/vfs";
16
+ /* eslint-disable import/no-named-as-default-member */
17
+ // The default options used to auto-transpile if needed.
18
+ const defaultOptions = {
19
+ // Type checking
20
+ strict: false,
21
+ // modules
22
+ module: ts.ModuleKind.ES2020,
23
+ moduleResolution: ts.ModuleResolutionKind.NodeJs,
24
+ noResolve: true,
25
+ resolveJsonModule: false,
26
+ // emit
27
+ emitBOM: false,
28
+ importsNotUsedAsValues: ts.ImportsNotUsedAsValues.Preserve,
29
+ newLine: ts.NewLineKind.LineFeed,
30
+ preserveValueImports: false,
31
+ // JavaScript Support
32
+ allowJs: true,
33
+ checkJs: false,
34
+ // Language and Environment
35
+ lib: [],
36
+ moduleDetection: "force",
37
+ target: ts.ScriptTarget.ES2017,
38
+ // Completeness
39
+ skipLibCheck: true,
40
+ skipDefaultLibCheck: false,
41
+ };
42
+ /**
43
+ * Create a transpiler using the given compiler options, which will compile the
44
+ * content provided in the files array.
45
+ *
46
+ * Note: this library intentionally transpiles with a pinned older version of
47
+ * TypeScript for stability. This version is denoted in this workspace's
48
+ * package.json. For the default set of compiler options, we use a lenient
49
+ * set of options because the general goal is to emit code as best as we can.
50
+ * For a list of the options used, see `defaultOptions` above.
51
+ *
52
+ * If this is not desirable for plugin authors, they are free to provide their
53
+ * own transpile function as part of the plugin initialization. If one is
54
+ * provided, it will be invoked instead and the framework's auto-transpilation
55
+ * will be bypassed.
56
+ *
57
+ * In addition, note that there is a dependency on @typescript/vfs in the
58
+ * top-level package as well as this package. This is to avoid npm hoisting
59
+ * @typescript/vfs to the top-level node_modules directory, which then causes
60
+ * type mismatches when trying to use it with this package's version of
61
+ * TypeScript. Ideally we would use something like Yarn's nohoist here, but
62
+ * npm does not support that yet.
63
+ */
64
+ function createTranspiler(options, files) {
65
+ const fsMap = createDefaultMapFromNodeModules({
66
+ target: options.target,
67
+ });
68
+ files.forEach((file) => {
69
+ fsMap.set(file.name, file.content);
70
+ });
71
+ const system = createSystem(fsMap);
72
+ const host = createVirtualCompilerHost(system, options, ts);
73
+ return ts.createProgram({
74
+ rootNames: [...fsMap.keys()],
75
+ options,
76
+ host: host.compilerHost,
77
+ });
78
+ }
79
+ export function transpile(files, transpileJs, transpileDts) {
80
+ const options = Object.assign(Object.assign({}, defaultOptions), { declaration: transpileDts, emitDeclarationOnly: transpileDts && !transpileJs });
81
+ // Create the transpiler (a ts.Program object)
82
+ const program = createTranspiler(options, files);
83
+ const results = [];
84
+ let err;
85
+ const result = program.emit(undefined, (fileName, data, writeByteOrderMark, onError, sourceFiles) => {
86
+ // We have to go through some hoops here because the header we add to each
87
+ // file is not part of the AST. So we find the TypeScript file we
88
+ // generated for each emitted file and add the header to each output ourselves.
89
+ if (!sourceFiles) {
90
+ err = new Error(`unable to map emitted file "${fileName}" to a source file: missing source files`);
91
+ return;
92
+ }
93
+ if (sourceFiles.length !== 1) {
94
+ err = new Error(`unable to map emitted file "${fileName}" to a source file: expected 1 source file, got ${sourceFiles.length}`);
95
+ return;
96
+ }
97
+ const file = files.find((x) => sourceFiles[0].fileName === x.name);
98
+ if (!file) {
99
+ err = new Error(`unable to map emitted file "${fileName}" to a source file: not found`);
100
+ return;
101
+ }
102
+ results.push({
103
+ name: fileName,
104
+ preamble: file.preamble,
105
+ content: data,
106
+ });
107
+ });
108
+ if (err) {
109
+ throw err;
110
+ }
111
+ if (result.emitSkipped) {
112
+ throw Error("An problem occurred during transpilation and files were not generated. Contact the plugin author for support.");
113
+ }
114
+ return results;
115
+ }
@@ -1,4 +1,5 @@
1
- import type { Schema } from "./ecmascript";
1
+ import { Schema } from "./ecmascript/schema.js";
2
+ import type { FileInfo } from "./ecmascript/generated-file.js";
2
3
  import type { Plugin } from "./plugin.js";
3
4
  interface PluginInit {
4
5
  /**
@@ -9,7 +10,49 @@ interface PluginInit {
9
10
  * Version of this code generator plugin.
10
11
  */
11
12
  version: string;
13
+ /**
14
+ * A optional parsing function which can be used to customize parameter
15
+ * parsing of the plugin.
16
+ */
12
17
  parseOption?: PluginOptionParseFn;
18
+ /**
19
+ * A function which will generate TypeScript files based on proto input.
20
+ * This function will be invoked by the plugin framework when the plugin runs.
21
+ *
22
+ * Note that this is required to be provided for plugin initialization.
23
+ */
24
+ generateTs: (schema: Schema, target: "ts") => void;
25
+ /**
26
+ * A optional function which will generate JavaScript files based on proto
27
+ * input. This function will be invoked by the plugin framework when the
28
+ * plugin runs.
29
+ *
30
+ * If this function is not provided, the plugin framework will then check if
31
+ * a transpile function is provided. If so, it will be invoked to transpile
32
+ * JavaScript files. If not, the plugin framework will transpile the files
33
+ * itself.
34
+ */
35
+ generateJs?: (schema: Schema, target: "js") => void;
36
+ /**
37
+ * A optional function which will generate TypeScript declaration files
38
+ * based on proto input. This function will be invoked by the plugin
39
+ * framework when the plugin runs.
40
+ *
41
+ * If this function is not provided, the plugin framework will then check if
42
+ * a transpile function is provided. If so, it will be invoked to transpile
43
+ * declaration files. If not, the plugin framework will transpile the files
44
+ * itself.
45
+ */
46
+ generateDts?: (schema: Schema, target: "dts") => void;
47
+ /**
48
+ * A optional function which will transpile a given set of files.
49
+ *
50
+ * This funcion is meant to be used in place of either generateJs,
51
+ * generateDts, or both. However, those functions will take precedence.
52
+ * This means that if generateJs, generateDts, and this transpile function
53
+ * are all provided, this transpile function will be ignored.
54
+ */
55
+ transpile?: (files: FileInfo[], transpileJs: boolean, transpileDts: boolean) => FileInfo[];
13
56
  }
14
57
  declare type PluginOptionParseFn = (key: string, value: string | undefined) => void;
15
58
  /**
@@ -17,5 +60,5 @@ declare type PluginOptionParseFn = (key: string, value: string | undefined) => v
17
60
  * The plugin can generate JavaScript, TypeScript, or TypeScript declaration
18
61
  * files.
19
62
  */
20
- export declare function createEcmaScriptPlugin(init: PluginInit, generateFn: (schema: Schema) => void): Plugin;
63
+ export declare function createEcmaScriptPlugin(init: PluginInit): Plugin;
21
64
  export {};
@@ -0,0 +1,28 @@
1
+ import type { AnyDesc } from "@bufbuild/protobuf";
2
+ import { Message, MessageType, ScalarType } from "@bufbuild/protobuf";
3
+ /**
4
+ * Returns the value of a custom option with a scalar type.
5
+ *
6
+ * If no option is found, returns undefined.
7
+ */
8
+ export declare function findCustomScalarOption<T extends ScalarType>(desc: AnyDesc, id: number, scalarType: T): ScalarValue<T> | undefined;
9
+ /**
10
+ * Returns the value of a custom message option for the given descriptor and ID.
11
+ * The msgType param is then used to deserialize the message for returning to the caller.
12
+ *
13
+ * If no options are found, returns undefined.
14
+ *
15
+ * If the message option is unable to be read or deserialized, an error will be thrown.
16
+ */
17
+ export declare function findCustomMessageOption<T extends Message<T>>(desc: AnyDesc, id: number, msgType: MessageType<T>): T | undefined;
18
+ /**
19
+ * Returns the value of a custom enum option for the given descriptor and ID.
20
+ *
21
+ * If no options are found, returns undefined.
22
+ */
23
+ export declare function findCustomEnumOption(desc: AnyDesc, id: number): number | undefined;
24
+ /**
25
+ * ScalarValue is a conditional type that pairs a ScalarType value with its concrete type.
26
+ */
27
+ declare type ScalarValue<T> = T extends ScalarType.STRING ? string : T extends ScalarType.INT32 ? number : T extends ScalarType.UINT32 ? number : T extends ScalarType.UINT32 ? number : T extends ScalarType.SINT32 ? number : T extends ScalarType.FIXED32 ? number : T extends ScalarType.SFIXED32 ? number : T extends ScalarType.FLOAT ? number : T extends ScalarType.DOUBLE ? number : T extends ScalarType.INT64 ? bigint | string : T extends ScalarType.SINT64 ? bigint | string : T extends ScalarType.SFIXED64 ? bigint | string : T extends ScalarType.UINT64 ? bigint | string : T extends ScalarType.FIXED64 ? bigint | string : T extends ScalarType.BOOL ? boolean : T extends ScalarType.BYTES ? Uint8Array : never;
28
+ export {};
@@ -1,7 +1,6 @@
1
1
  import { DescEnum, DescEnumValue, DescField, DescFile, DescMessage, DescMethod, DescOneof, DescService, ScalarType } from "@bufbuild/protobuf";
2
- import type { GeneratedFile } from "./generated-file.js";
2
+ import type { GeneratedFile, Printable } from "./generated-file.js";
3
3
  import type { ImportSymbol } from "./import-symbol.js";
4
- declare type Printable = Parameters<GeneratedFile["print"]>[number];
5
4
  export declare function makeFilePreamble(file: DescFile, pluginName: string, pluginVersion: string, parameter: string | undefined, tsNoCheck: boolean): string;
6
5
  export declare function createJsDocBlock(text: string, indentation?: string): Printable;
7
6
  export declare function makeJsDoc(desc: DescEnum | DescEnumValue | DescMessage | DescOneof | DescField | DescService | DescMethod, indentation?: string): Printable;
@@ -20,4 +19,3 @@ export declare function getFieldIntrinsicDefaultValue(field: DescField): {
20
19
  defaultValue: Printable | undefined;
21
20
  typingInferrable: boolean;
22
21
  };
23
- export {};