@tgrospic/rnode-grpc-js
Version:
RNode gRPC helpers
256 lines • 13.5 kB
JavaScript
;
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateTs = void 0;
var fs_extra_1 = __importDefault(require("fs-extra"));
var path_1 = __importDefault(require("path"));
var R = __importStar(require("ramda"));
var lib_1 = require("../lib");
var log = console.log;
var readFile = fs_extra_1.default.readFile, writeFile = fs_extra_1.default.writeFile, readdir = fs_extra_1.default.readdir, stat = fs_extra_1.default.stat;
var isFile = function (checkPath) { return __awaiter(void 0, void 0, void 0, function () {
var s;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, stat(checkPath)];
case 1:
s = _a.sent();
return [2 /*return*/, s.isFile()];
}
});
}); };
var readProtoFiles = function (protoPath) { return R.pipeWith(lib_1.then, [
readdir,
(0, lib_1.mapAsync)(function (fileName) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
return [2 /*return*/, path_1.default.resolve(protoPath, fileName)];
}); }); }),
(0, lib_1.filterAsync)(isFile),
(0, lib_1.mapAsync)(function (fp) { return readFile(fp, 'utf8'); }),
])(protoPath); };
// Parse service name
var serviceRegex = /service\s+([a-zA-Z0-9_-]+)/;
// Parse method definition with previous comment where is the return type
var serviceMethodRegex = /(\/\/ Returns on success ([a-zA-Z0-9_-]+)\s+)?rpc\s+([a-zA-Z0-9_-]+)\(([a-zA-Z0-9_-]+)\)\s*returns\s*\(\s*(stream)?\s*Either\s*\)\s*{\s*}/g;
var parseProto = function (protoStr) {
var _a;
var _b = __read(protoStr.match(serviceRegex) || [], 2), _ = _b[0], service = _b[1];
var matches = Array.from(protoStr.matchAll(serviceMethodRegex));
var methodInfo = function (_a) {
var _b = __read(_a, 6), outType = _b[2], name = _b[3], inType = _b[4], stream = _b[5];
return ({ name: name, inType: inType, outType: outType, isStream: stream === 'stream' });
};
var toObj = function (acc, _a) {
var _b;
var name = _a.name, outType = _a.outType;
return (__assign(__assign({}, acc), (_b = {}, _b[name] = outType, _b)));
};
var methods = matches.map(methodInfo).reduce(toObj, {});
if (!R.isEmpty(methods)) {
return _a = {}, _a[service] = methods, _a;
}
};
var parseProtoReponseMeta = R.pipe(R.map(parseProto), R.reject(R.isNil), R.mergeAll);
var methodResolveResponse = function (service, meta) { return function (method, name) {
var requestType = method.requestType, responseType = method.responseType, responseStream = method.responseStream;
var eitherTypeLens = R.lensPath([service, name]);
var eitherType = R.view(eitherTypeLens, meta);
var isResponseEither = responseType === 'Either';
// Replace Either with correct type
var outType = isResponseEither ? eitherType : responseType;
return { name: name, requestType: requestType, responseType: outType, responseStream: responseStream };
}; };
var serviceResolveResponse = function (meta) { return function (_a) {
var name = _a.name, methods = _a.methods;
return ({ name: name, methods: R.mapObjIndexed(methodResolveResponse(name, meta), methods) });
}; };
// Type helpers
var getTsType = function (protoType) { return R.find(function (x) { return x.proto === protoType; }, lib_1.protoTsTypesMapping); };
// Generates service method
var methodCodeGen = function (getType) { return function (_a) {
var _b = __read(_a, 2), name = _b[0], _c = _b[1], requestType = _c.requestType, responseType = _c.responseType, responseStream = _c.responseStream;
// Empty method arguments for Google Protobuf Empty type
var methodArg = requestType == 'google.protobuf.Empty'
? function () { return '()'; }
: function () {
var fields = getType(requestType).fields;
var isSimpleType = function (_a) {
var type = _a.type;
return getTsType(type);
};
var nullable = R.all(isSimpleType, Object.values(fields || {})) ? '?' : '';
return "(_".concat(nullable, ": ").concat(requestType, ")");
};
var suffix = responseStream ? '[]' : '';
return "".concat(name).concat(methodArg(), ": Promise<").concat(responseType || 'Unit').concat(suffix, ">");
}; };
// Generates code for service interface
var serviceCodeGen = function (getType) { return function (_a) {
var name = _a.name, methods = _a.methods;
// Indentation is important, it's used in generated file
return "interface ".concat(name, " {\n ").concat(Object.entries(methods).map(methodCodeGen(getType)).join('\n '), "\n }");
}; };
// Generates code for type field (property)
var fieldCodeGen = function (nullables, key, _a) {
var type = _a.type, rule = _a.rule;
var tsType = getTsType(type);
var isList = rule === 'repeated';
var nullable = tsType || isList || R.includes(key, nullables) ? '?' : '';
var suffixList = isList ? '[]' : '';
var origType = tsType && tsType.ts !== type ? " /* ".concat(type, " */") : '';
return tsType
? "readonly ".concat(key).concat(nullable, ": ").concat(tsType.ts).concat(suffixList).concat(origType)
: "readonly ".concat(key).concat(nullable, ": ").concat(type).concat(suffixList);
};
// Generates code for type interface
var typeCodeGen = function (_a) {
var name = _a.name, fields = _a.fields, nullables = _a.nullables;
var fieldGenKV = function (_a) {
var _b = __read(_a, 2), k = _b[0], field = _b[1];
return fieldCodeGen(nullables, k, field);
};
var isServiceError = function (_a) {
var _b = __read(_a, 2), k = _b[0], type = _b[1].type;
return k === 'error' && type === 'ServiceError';
};
var genFields = R.pipe(Object.entries, R.reject(isServiceError), R.map(fieldGenKV));
// Indentation is important, it's used in generated file
return "interface ".concat(name, " {\n ").concat(genFields(fields).join('\n '), "\n }");
};
// Generates code for binary operations (exposed from generated JS code)
var binaryOpCodeGen = function (_a) {
var name = _a.name;
// Indentation is important, it's used in generated file
return "readonly ".concat(name, ": BinaryOp<").concat(name, ">");
};
var generateTs = function (_a) {
var jsPath = _a.jsPath, protoPath = _a.protoPath, protoSchema = _a.protoSchema, version = _a.version;
return __awaiter(void 0, void 0, void 0, function () {
var schemaFlat, types, getType, getServices, protoFiles, servicesMeta, services, tmplTsPath, tmplTs, tsGenPath, schemaPath, servicesGen, typesGen, binaryGen, tsGen1, tsGen2, tsGen3, tsGen4;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
schemaFlat = R.chain((0, lib_1.flattenSchema)([]), [protoSchema]);
types = R.filter(R.propEq('type', 'type'), schemaFlat);
getType = function (name) { return R.find(R.propEq('name', name), types); };
getServices = function (meta) { return R.pipe(R.filter(R.propEq('type', 'service')), R.map(serviceResolveResponse(meta)))(schemaFlat); };
return [4 /*yield*/, readProtoFiles(protoPath)];
case 1:
protoFiles = _b.sent();
servicesMeta = parseProtoReponseMeta(protoFiles);
services = getServices(servicesMeta);
tmplTsPath = path_1.default.resolve(__dirname, '../../src-template/rnode-grpc-js-tmpl.d.ts');
return [4 /*yield*/, readFile(tmplTsPath, 'utf8')];
case 2:
tmplTs = _b.sent();
tsGenPath = path_1.default.resolve(jsPath, 'rnode-grpc-js.d.ts');
schemaPath = path_1.default.resolve(jsPath, 'rnode-api-schema.json');
servicesGen = R.map(serviceCodeGen(getType), services);
typesGen = R.map(typeCodeGen, types);
binaryGen = R.map(binaryOpCodeGen, types);
tsGen1 = tmplTs.replace('/*__SERVICES__*/', servicesGen.join('\n\n '));
tsGen2 = tsGen1.replace('/*__TYPES__*/', typesGen.join('\n\n '));
tsGen3 = tsGen2.replace('/*__TYPES_BINARY__*/', binaryGen.join('\n '));
tsGen4 = tsGen3.replace('__RNODE_VERSION__', version);
// Write generated TypeScript file
return [4 /*yield*/, writeFile(tsGenPath, tsGen4)
// Write flatten API schema
];
case 3:
// Write generated TypeScript file
_b.sent();
// Write flatten API schema
return [4 /*yield*/, writeFile(schemaPath, JSON.stringify(schemaFlat, null, 2))];
case 4:
// Write flatten API schema
_b.sent();
return [2 /*return*/];
}
});
});
};
exports.generateTs = generateTs;
//# sourceMappingURL=typings.js.map