Notably, this gets us `@actions/github` 6.0.1, which is necessary for `github.context.runAttempt` (so I have also updated `package.json` accordingly). On that note... great job following semver, GitHub! Between this and the non-breaking major version change on `@actions/cache`, I'm starting to feel like nobody on the Actions team understands how software versioning works.
238 lines
14 KiB
JavaScript
238 lines
14 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.createFileRegistryFromRequest = exports.ProtobuftsPlugin = void 0;
|
|
const protobuf_1 = require("@bufbuild/protobuf");
|
|
const wkt_1 = require("@bufbuild/protobuf/wkt");
|
|
const reflect_1 = require("@bufbuild/protobuf/reflect");
|
|
const typescript_compile_1 = require("./framework/typescript-compile");
|
|
const options_1 = require("./options");
|
|
const service_server_generator_grpc_1 = require("./code-gen/service-server-generator-grpc");
|
|
const comment_generator_1 = require("./code-gen/comment-generator");
|
|
const message_interface_generator_1 = require("./code-gen/message-interface-generator");
|
|
const message_type_generator_1 = require("./code-gen/message-type-generator");
|
|
const enum_generator_1 = require("./code-gen/enum-generator");
|
|
const service_type_generator_1 = require("./code-gen/service-type-generator");
|
|
const service_client_generator_generic_1 = require("./code-gen/service-client-generator-generic");
|
|
const file_table_1 = require("./file-table");
|
|
const service_server_generator_generic_1 = require("./code-gen/service-server-generator-generic");
|
|
const service_client_generator_grpc_1 = require("./code-gen/service-client-generator-grpc");
|
|
const ts = require("typescript");
|
|
const well_known_types_1 = require("./message-type-extensions/well-known-types");
|
|
const interpreter_1 = require("./interpreter");
|
|
const symbol_table_1 = require("./framework/symbol-table");
|
|
const typescript_imports_1 = require("./framework/typescript-imports");
|
|
const plugin_base_1 = require("./framework/plugin-base");
|
|
const protobuf_ts_pb_1 = require("./gen/protobuf-ts_pb");
|
|
class ProtobuftsPlugin extends plugin_base_1.PluginBaseProtobufES {
|
|
constructor(version) {
|
|
super();
|
|
this.version = version;
|
|
// we support proto3-optionals, so we let protoc know
|
|
this.getSupportedFeatures = () => [wkt_1.CodeGeneratorResponse_Feature.PROTO3_OPTIONAL];
|
|
this.version = version;
|
|
}
|
|
generate(request) {
|
|
const options = options_1.parseOptions(request.parameter, `by protobuf-ts ${this.version}` + (request.parameter ? ` with parameter ${request.parameter}` : '')), registry = createFileRegistryFromRequest(request), symbols = new symbol_table_1.SymbolTable(), fileTable = new file_table_1.FileTable(options), imports = new typescript_imports_1.TypeScriptImports(symbols, registry), comments = new comment_generator_1.CommentGenerator(), interpreter = new interpreter_1.Interpreter(registry, options), genMessageInterface = new message_interface_generator_1.MessageInterfaceGenerator(symbols, imports, comments, interpreter, options), genEnum = new enum_generator_1.EnumGenerator(symbols, imports, comments, interpreter), genMessageType = new message_type_generator_1.MessageTypeGenerator(registry, imports, comments, interpreter, options), genServiceType = new service_type_generator_1.ServiceTypeGenerator(symbols, imports, comments, interpreter, options), genServerGeneric = new service_server_generator_generic_1.ServiceServerGeneratorGeneric(symbols, imports, comments, interpreter, options), genServerGrpc = new service_server_generator_grpc_1.ServiceServerGeneratorGrpc(symbols, imports, comments, interpreter), genClientGeneric = new service_client_generator_generic_1.ServiceClientGeneratorGeneric(symbols, registry, imports, comments, interpreter, options), genClientGrpc = new service_client_generator_grpc_1.ServiceClientGeneratorGrpc(symbols, registry, imports, comments, interpreter, options);
|
|
// in first pass, register standard file names
|
|
for (let fileDescriptor of registry.files) {
|
|
const base = fileDescriptor.name + (options.addPbSuffix ? "_pb" : "");
|
|
fileTable.register(base + '.ts', fileDescriptor);
|
|
}
|
|
// in second pass, register client and server file names
|
|
for (let descFile of registry.files) {
|
|
const base = descFile.name + (options.addPbSuffix ? "_pb" : "");
|
|
fileTable.register(base + '.server.ts', descFile, 'generic-server');
|
|
fileTable.register(base + '.grpc-server.ts', descFile, 'grpc1-server');
|
|
fileTable.register(base + '.client.ts', descFile, 'client');
|
|
fileTable.register(base + '.promise-client.ts', descFile, 'promise-client');
|
|
fileTable.register(base + '.rx-client.ts', descFile, 'rx-client');
|
|
fileTable.register(base + '.grpc-client.ts', descFile, 'grpc1-client');
|
|
}
|
|
// in third pass, generate files
|
|
for (let descFile of registry.files) {
|
|
const outMain = fileTable.create(descFile), outServerGeneric = fileTable.create(descFile, 'generic-server'), outServerGrpc = fileTable.create(descFile, 'grpc1-server'), outClientCall = fileTable.create(descFile, 'client'),
|
|
// TODO
|
|
//outClientPromise = fileTable.create(descFile, 'promise-client'),
|
|
//outClientRx = fileTable.create(descFile, 'rx-client'),
|
|
outClientGrpc = fileTable.create(descFile, 'grpc1-client');
|
|
// in first pass over types, register all symbols, regardless whether they are going to be used
|
|
for (const desc of reflect_1.nestedTypes(descFile)) {
|
|
switch (desc.kind) {
|
|
case "enum":
|
|
genEnum.registerSymbols(outMain, desc);
|
|
break;
|
|
case "message":
|
|
genMessageInterface.registerSymbols(outMain, desc);
|
|
break;
|
|
case "service":
|
|
genServiceType.registerSymbols(outMain, desc);
|
|
genClientGeneric.registerSymbols(outClientCall, desc);
|
|
genClientGrpc.registerSymbols(outClientGrpc, desc);
|
|
genServerGeneric.registerSymbols(outServerGeneric, desc);
|
|
genServerGrpc.registerSymbols(outServerGrpc, desc);
|
|
break;
|
|
}
|
|
}
|
|
// in second pass over types, generate message interfaces and enums
|
|
for (const desc of reflect_1.nestedTypes(descFile)) {
|
|
switch (desc.kind) {
|
|
case "message":
|
|
genMessageInterface.generateMessageInterface(outMain, desc);
|
|
break;
|
|
case "enum":
|
|
genEnum.generateEnum(outMain, desc);
|
|
break;
|
|
}
|
|
}
|
|
// in third pass over types, generate message and service types
|
|
for (const desc of reflect_1.nestedTypes(descFile)) {
|
|
switch (desc.kind) {
|
|
case "message":
|
|
genMessageType.generateMessageType(outMain, desc, options.getOptimizeMode(descFile));
|
|
break;
|
|
case "service":
|
|
if (options.forceDisableServices) {
|
|
break;
|
|
}
|
|
// service type
|
|
genServiceType.generateServiceType(outMain, desc);
|
|
// clients
|
|
const clientStyles = options.getClientStyles(desc);
|
|
if (clientStyles.includes(protobuf_ts_pb_1.ClientStyle.GENERIC_CLIENT)) {
|
|
genClientGeneric.generateInterface(outClientCall, desc);
|
|
genClientGeneric.generateImplementationClass(outClientCall, desc);
|
|
}
|
|
if (clientStyles.includes(protobuf_ts_pb_1.ClientStyle.GRPC1_CLIENT)) {
|
|
genClientGrpc.generateInterface(outClientGrpc, desc);
|
|
genClientGrpc.generateImplementationClass(outClientGrpc, desc);
|
|
}
|
|
// servers
|
|
const serverStyles = options.getServerStyles(desc);
|
|
if (serverStyles.includes(protobuf_ts_pb_1.ServerStyle.GENERIC_SERVER)) {
|
|
genServerGeneric.generateInterface(outServerGeneric, desc);
|
|
}
|
|
if (serverStyles.includes(protobuf_ts_pb_1.ServerStyle.GRPC1_SERVER)) {
|
|
genServerGrpc.generateInterface(outServerGrpc, desc);
|
|
genServerGrpc.generateDefinition(outServerGrpc, desc);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// plugins should only return files requested to generate
|
|
// unless our option "generate_dependencies" is set.
|
|
// We always return well-known types, because we do not
|
|
// maintain them in a package - they are always generated
|
|
// on demand.
|
|
let tsFiles = fileTable.outFiles.concat();
|
|
if (!options.generateDependencies) {
|
|
tsFiles = tsFiles.filter(file => {
|
|
const protoFilename = file.descFile.proto.name;
|
|
if (request.fileToGenerate.includes(protoFilename)) {
|
|
return true;
|
|
}
|
|
if (well_known_types_1.WellKnownTypes.protoFilenames.includes(protoFilename)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
}
|
|
// if a proto file is imported to use custom options, or if a proto file declares custom options,
|
|
// we do not to emit it. unless it was explicitly requested.
|
|
// TODO why does the fallback condition include "used" files? isn't that what generateDependencies should do?
|
|
tsFiles = tsFiles.filter(of => request.fileToGenerate.includes(of.descFile.proto.name)
|
|
|| this.isFileUsed(of.descFile, tsFiles.map(x => x.descFile)));
|
|
return this.transpile(tsFiles, options);
|
|
}
|
|
transpile(tsFiles, options) {
|
|
if (options.transpileTarget === undefined) {
|
|
return tsFiles;
|
|
}
|
|
const opt = {
|
|
moduleResolution: ts.ModuleResolutionKind.NodeJs,
|
|
skipLibCheck: true,
|
|
declaration: true,
|
|
module: options.transpileModule,
|
|
target: options.transpileTarget,
|
|
};
|
|
const [program,] = typescript_compile_1.setupCompiler(opt, tsFiles, tsFiles.map(f => f.getFilename()));
|
|
const results = [];
|
|
let err;
|
|
program.emit(undefined, (fileName, data, writeByteOrderMark, onError, sourceFiles) => {
|
|
// We have to go through some hoops here because the header we add to each file
|
|
// is not part of the AST. So we find the TypeScript file we generated for each
|
|
// emitted file and add the header to each output ourselves.
|
|
if (!sourceFiles) {
|
|
err = new Error(`unable to map emitted file "${fileName}" to a source file: missing source files`);
|
|
return;
|
|
}
|
|
if (sourceFiles.length !== 1) {
|
|
err = new Error(`unable to map emitted file "${fileName}" to a source file: expected 1 source file, got ${sourceFiles.length}`);
|
|
return;
|
|
}
|
|
const tsFile = tsFiles.find(x => sourceFiles[0].fileName === x.getFilename());
|
|
if (!tsFile) {
|
|
err = new Error(`unable to map emitted file "${fileName}" to a source file: not found`);
|
|
return;
|
|
}
|
|
const content = tsFile.getHeader() + data;
|
|
results.push({
|
|
getFilename() {
|
|
return fileName;
|
|
},
|
|
getContent() {
|
|
return content;
|
|
}
|
|
});
|
|
});
|
|
if (err) {
|
|
throw err;
|
|
}
|
|
return results;
|
|
}
|
|
isFileUsed(descFile, files) {
|
|
for (const type of reflect_1.nestedTypes(descFile)) {
|
|
// TODO do not consider referencing a type that a file defines itself used - filter the second argument to exclude the current file
|
|
if (this.isTypeUsed(type, files)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
isTypeUsed(type, files) {
|
|
for (const otherFile of files) {
|
|
for (const otherType of reflect_1.nestedTypes(otherFile)) {
|
|
switch (otherType.kind) {
|
|
case "service":
|
|
if (type.kind == "message") {
|
|
if (otherType.methods.some(descMethod => descMethod.input.typeName == type.typeName)) {
|
|
return true;
|
|
}
|
|
if (otherType.methods.some(descMethod => descMethod.output.typeName == type.typeName)) {
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
case "message":
|
|
if (otherType.fields.some(descField => { var _a; return ((_a = descField.message) === null || _a === void 0 ? void 0 : _a.typeName) === type.typeName; })) {
|
|
return true;
|
|
}
|
|
if (otherType.fields.some(descField => { var _a; return ((_a = descField.enum) === null || _a === void 0 ? void 0 : _a.typeName) === type.typeName; })) {
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
exports.ProtobuftsPlugin = ProtobuftsPlugin;
|
|
function createFileRegistryFromRequest(request) {
|
|
const set = protobuf_1.create(wkt_1.FileDescriptorSetSchema, {
|
|
file: request.protoFile,
|
|
});
|
|
return protobuf_1.createFileRegistry(set);
|
|
}
|
|
exports.createFileRegistryFromRequest = createFileRegistryFromRequest;
|