@bufbuild/protoplugin 0.1.1 → 0.2.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.
- package/README.md +21 -27
- package/dist/cjs/create-es-plugin.js +85 -9
- package/dist/cjs/ecmascript/custom-options.js +115 -0
- package/dist/cjs/ecmascript/gencommon.js +13 -16
- package/dist/cjs/ecmascript/generated-file.js +9 -12
- package/dist/cjs/ecmascript/index.js +6 -2
- package/dist/cjs/ecmascript/schema.js +24 -8
- package/dist/cjs/ecmascript/transpile.js +122 -0
- package/dist/esm/create-es-plugin.js +86 -10
- package/dist/esm/ecmascript/custom-options.js +109 -0
- package/dist/esm/ecmascript/gencommon.js +13 -16
- package/dist/esm/ecmascript/generated-file.js +9 -12
- package/dist/esm/ecmascript/index.js +2 -1
- package/dist/esm/ecmascript/schema.js +23 -8
- package/dist/esm/ecmascript/transpile.js +115 -0
- package/dist/types/create-es-plugin.d.ts +45 -2
- package/dist/types/ecmascript/custom-options.d.ts +32 -0
- package/dist/types/ecmascript/gencommon.d.ts +1 -3
- package/dist/types/ecmascript/generated-file.d.ts +12 -5
- package/dist/types/ecmascript/index.d.ts +3 -2
- package/dist/types/ecmascript/schema.d.ts +4 -3
- package/dist/types/ecmascript/transpile.d.ts +2 -0
- package/package.json +8 -6
|
@@ -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 {
|
|
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
|
|
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
|
-
|
|
28
|
-
const {
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
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,109 @@
|
|
|
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, extensionNumber, scalarType) {
|
|
21
|
+
const reader = createBinaryReader(desc, extensionNumber);
|
|
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
|
|
63
|
+
* extension number.
|
|
64
|
+
* The msgType param is then used to deserialize the message for returning to
|
|
65
|
+
* the caller.
|
|
66
|
+
*
|
|
67
|
+
* If no options are found, returns undefined.
|
|
68
|
+
*
|
|
69
|
+
* If the message option is unable to be read or deserialized, an error will
|
|
70
|
+
* be thrown.
|
|
71
|
+
*/
|
|
72
|
+
export function findCustomMessageOption(desc, extensionNumber, msgType) {
|
|
73
|
+
const reader = createBinaryReader(desc, extensionNumber);
|
|
74
|
+
if (reader) {
|
|
75
|
+
try {
|
|
76
|
+
const data = reader.bytes();
|
|
77
|
+
return msgType.fromBinary(data);
|
|
78
|
+
}
|
|
79
|
+
catch (e) {
|
|
80
|
+
const innerMessage = e instanceof Error ? e.message : String(e);
|
|
81
|
+
throw new Error(`failed to access message option: ${innerMessage}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Returns the value of a custom enum option for the given descriptor and
|
|
88
|
+
* extension number.
|
|
89
|
+
*
|
|
90
|
+
* If no options are found, returns undefined.
|
|
91
|
+
*/
|
|
92
|
+
export function findCustomEnumOption(desc, extensionNumber) {
|
|
93
|
+
return findCustomScalarOption(desc, extensionNumber, ScalarType.INT32);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Returns a binary reader for the given descriptor and extension number.
|
|
97
|
+
*/
|
|
98
|
+
function createBinaryReader(desc, extensionNumber) {
|
|
99
|
+
const opt = desc.proto.options;
|
|
100
|
+
let reader = undefined;
|
|
101
|
+
if (opt !== undefined) {
|
|
102
|
+
const unknownFields = proto3.bin.listUnknownFields(opt);
|
|
103
|
+
const field = unknownFields.find((f) => f.no === extensionNumber);
|
|
104
|
+
if (field) {
|
|
105
|
+
reader = new BinaryReader(field.data);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return reader;
|
|
109
|
+
}
|
|
@@ -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 "
|
|
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.
|
|
137
|
-
case "
|
|
133
|
+
switch (field.fieldKind) {
|
|
134
|
+
case "scalar":
|
|
138
135
|
typing.push(scalarTypeScriptType(field.scalar));
|
|
139
136
|
optional = field.optional;
|
|
140
137
|
break;
|
|
141
|
-
case "
|
|
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 "
|
|
149
|
+
case "enum":
|
|
153
150
|
typing.push(file.import(field.enum).toTypeOnly());
|
|
154
151
|
optional = field.optional;
|
|
155
152
|
break;
|
|
156
|
-
case "
|
|
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.
|
|
226
|
-
case "
|
|
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 "
|
|
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.
|
|
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.
|
|
296
|
-
case "
|
|
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 "
|
|
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(
|
|
34
|
+
return createImportSymbol(typeOrName, from);
|
|
36
35
|
}
|
|
37
36
|
return createTypeImport(typeOrName);
|
|
38
37
|
},
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
45
|
-
|
|
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
|
}
|
|
@@ -17,5 +17,6 @@ export {} from "./schema.js";
|
|
|
17
17
|
export {} from "./runtime-imports.js";
|
|
18
18
|
export {} from "./generated-file.js";
|
|
19
19
|
export {} from "./import-symbol.js";
|
|
20
|
-
export const { localName } = codegenInfo;
|
|
20
|
+
export const { localName, reifyWkt } = 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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
|
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
|
|
63
|
+
export declare function createEcmaScriptPlugin(init: PluginInit): Plugin;
|
|
21
64
|
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
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, extensionNumber: number, scalarType: T): ScalarValue<T> | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* Returns the value of a custom message option for the given descriptor and
|
|
11
|
+
* extension number.
|
|
12
|
+
* The msgType param is then used to deserialize the message for returning to
|
|
13
|
+
* the caller.
|
|
14
|
+
*
|
|
15
|
+
* If no options are found, returns undefined.
|
|
16
|
+
*
|
|
17
|
+
* If the message option is unable to be read or deserialized, an error will
|
|
18
|
+
* be thrown.
|
|
19
|
+
*/
|
|
20
|
+
export declare function findCustomMessageOption<T extends Message<T>>(desc: AnyDesc, extensionNumber: number, msgType: MessageType<T>): T | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Returns the value of a custom enum option for the given descriptor and
|
|
23
|
+
* extension number.
|
|
24
|
+
*
|
|
25
|
+
* If no options are found, returns undefined.
|
|
26
|
+
*/
|
|
27
|
+
export declare function findCustomEnumOption(desc: AnyDesc, extensionNumber: number): number | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* ScalarValue is a conditional type that pairs a ScalarType value with its concrete type.
|
|
30
|
+
*/
|
|
31
|
+
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;
|
|
32
|
+
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 {};
|