UNPKG

@cosmology/ast

Version:
269 lines (268 loc) 13.9 kB
import * as t from '@babel/types'; import { arrowFunctionExpression, classDeclaration, classMethod, classProperty, commentBlock, identifier, tsMethodSignature } from '../../../../utils'; import { camel, getServiceImplement } from '@cosmology/utils'; import { processRpcComment, returnReponseType, cleanType, optionalBool } from '../utils/rpc'; import { BinaryCoder } from '../../../../utils/binary-coder-expression'; const rpcMethodDefinition = (name, svc, trailingComments, leadingComments) => { const requestType = svc.requestType; const responseType = svc.responseType; const fieldNames = Object.keys(svc.fields ?? {}); const hasParams = fieldNames.length > 0; const optional = optionalBool(hasParams, fieldNames); const methodArgs = identifier('request', t.tsTypeAnnotation(t.tsTypeReference(t.identifier(requestType))), optional); return tsMethodSignature(t.identifier(name), null, [ methodArgs ], returnReponseType(responseType), trailingComments, leadingComments); }; const rpcTxMethodDefinition = (name, svc, trailingComments, leadingComments) => { const requestType = svc.requestType; const responseType = t.tsTypeReference(t.identifier("DeliverTxResponse")); const methodArgs = [ identifier("signerAddress", t.tsTypeAnnotation(t.tsStringKeyword())), identifier("message", t.tsTypeAnnotation(t.tsTypeReference(t.identifier(requestType)))), identifier("fee", t.tsTypeAnnotation(t.tsUnionType([ t.tsNumberKeyword(), t.tsTypeReference(t.identifier("StdFee")), t.tsLiteralType(t.stringLiteral("auto")), ]))), identifier("memo", t.tsTypeAnnotation(t.tsStringKeyword()), true), ]; return tsMethodSignature(t.identifier(name), null, methodArgs, returnReponseType(responseType), trailingComments, leadingComments); }; // this.Accounts = this.Accounts.bind(this); // MARKED AS NOT DRY (used in rpc/lcd) const bindThis = (name) => { return t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.thisExpression(), t.identifier(name)), t.callExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier(name)), t.identifier('bind')), [ t.thisExpression() ]))); }; // const data = QueryAccountsRequest.encode(request).finish(); const encodeData = (name) => { return t.variableDeclaration('const', [ t.variableDeclarator(t.identifier('data'), t.callExpression(t.memberExpression(t.callExpression(t.memberExpression(t.identifier(name), t.identifier('encode')), [ t.identifier('request') ]), t.identifier('finish')), [])) ]); }; // const promise = this.rpc.request("cosmos.auth.v1beta1.Query", "Accounts", data); const promiseRequest = (name, packageImportName) => { name = cleanType(name); return t.variableDeclaration('const', [ t.variableDeclarator(t.identifier('promise'), t.callExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier('rpc')), t.identifier('request')), [ t.stringLiteral(packageImportName), t.stringLiteral(name), t.identifier('data') ])) ]); }; // return promise.then((data) => QueryAccountsResponse.decode(new _m0.Reader(data))); const returnPromise = (name, context) => { name = cleanType(name); return t.returnStatement(t.callExpression(t.memberExpression(t.identifier('promise'), t.identifier('then')), [ t.arrowFunctionExpression([ t.identifier('data') ], t.callExpression(t.memberExpression(t.identifier(name), t.identifier('decode')), [ t.newExpression(BinaryCoder.getReaderMemberExp(context), [ t.identifier('data') ]), ...(context.options.interfaces.enabled && context.options.interfaces.useUseInterfacesParams ? [ t.identifier('undefined'), t.identifier('useInterfaces') ] : []), ])) ])); }; const makeComment = (comment) => { return [{ type: 'CommentBlock', value: ` ${comment} ` }]; }; const rpcClassMethod = (context, name, msg, svc, packageImport) => { const requestType = svc.requestType; const responseType = svc.responseType; const comment = svc.comment ?? svc.name; let methodArgs = identifier('request', t.tsTypeAnnotation(t.tsTypeReference(t.identifier(requestType)))); const fieldNames = Object.keys(svc.fields ?? {}); const hasParams = fieldNames.length > 0; // if no params, then let's default to empty object for cleaner API if (!hasParams) { methodArgs = t.assignmentPattern(methodArgs, t.objectExpression([])); } else if (hasParams && fieldNames.length === 1 && fieldNames.includes('pagination')) { const paginationDefaultFromPartial = context.pluginValue('prototypes.paginationDefaultFromPartial'); // if only argument "required" is pagination // also default to empty methodArgs = t.assignmentPattern(methodArgs, t.objectExpression([ t.objectProperty(t.identifier('pagination'), paginationDefaultFromPartial ? t.callExpression(t.memberExpression(t.identifier("PageRequest"), t.identifier("fromPartial")), [t.objectExpression([])]) : t.identifier('undefined'), false, false) ])); } const body = t.blockStatement([ // const data = QueryAccountsRequest.encode(request).finish(); encodeData(requestType), // const promise = this.rpc.request("cosmos.auth.v1beta1.Query", "Accounts", data); promiseRequest(msg, packageImport), // return promise.then((data) => QueryAccountsResponse.decode(new _m0.Reader(data))); returnPromise(responseType, context) ]); if (context.pluginValue('classesUseArrowFunctions')) { return classProperty(t.identifier(name), arrowFunctionExpression([methodArgs], body, returnReponseType(responseType), true), undefined, undefined, undefined, undefined, undefined, undefined, makeComment(comment)); } return classMethod('method', t.identifier(name), [ methodArgs, ...(context.options.interfaces.enabled && context.options.interfaces.useUseInterfacesParams ? [ t.assignmentPattern(identifier('useInterfaces', t.tsTypeAnnotation(t.tsBooleanKeyword())), t.identifier((context.pluginValue('interfaces.useByDefaultRpc') ?? true).toString())) ] : []), ], body, returnReponseType(responseType)); }; const rpcTxClassMethod = (context, name, msg, svc, packageImport) => { const requestType = svc.requestType; const responseType = t.tsTypeReference(t.identifier("DeliverTxResponse")); const comment = svc.comment ?? svc.name; const methodArgs = [ identifier("signerAddress", t.tsTypeAnnotation(t.tsStringKeyword())), identifier("message", t.tsTypeAnnotation(t.tsTypeReference(t.identifier(requestType)))), t.assignmentPattern(identifier("fee", t.tsTypeAnnotation(t.tsUnionType([ t.tsNumberKeyword(), t.tsTypeReference(t.identifier("StdFee")), t.tsLiteralType(t.stringLiteral("auto")), ]))), t.stringLiteral("auto")), t.assignmentPattern(identifier("memo", t.tsTypeAnnotation(t.tsStringKeyword())), t.stringLiteral("")), ]; const body = t.blockStatement([ // generate: // const data = [ // { // typeUrl: MsgCreateValidator.typeUrl, // value: message, // }, // ]; t.variableDeclaration('const', [ t.variableDeclarator(t.identifier('data'), t.arrayExpression([ t.objectExpression([ t.objectProperty(t.identifier('typeUrl'), t.memberExpression(t.identifier(svc.requestType), t.identifier('typeUrl'))), t.objectProperty(t.identifier('value'), t.identifier('message')) ]) ])) ]), // generate: // return this.rpc.signAndBroadcast!( // signerAddress, // data, // fee, // memo // ); t.returnStatement(t.callExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier('rpc')), t.identifier('signAndBroadcast!')), [ t.identifier('signerAddress'), t.identifier('data'), t.identifier('fee'), t.identifier('memo') ])) ]); if (context.pluginValue('classesUseArrowFunctions')) { return classProperty(t.identifier(name), arrowFunctionExpression(methodArgs, body, returnReponseType(responseType), true), undefined, undefined, undefined, undefined, undefined, undefined, makeComment(comment)); } return classMethod('method', t.identifier(name), methodArgs, body, returnReponseType(responseType)); }; const rpcClassConstructor = (context, methods) => { const useTelescopeGeneratedType = context.pluginValue('prototypes.typingsFormat.useTelescopeGeneratedType'); let bound = []; if (!context.pluginValue('classesUseArrowFunctions')) { bound = methods.map(method => bindThis(method)); } return classMethod('constructor', t.identifier('constructor'), [ identifier('rpc', t.tsTypeAnnotation(t.tsTypeReference(t.identifier(useTelescopeGeneratedType ? 'TxRpc' : 'Rpc')))) ], t.blockStatement([ t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.thisExpression(), t.identifier('rpc')), t.identifier('rpc'))), /// methods ...bound ])); }; export const createRpcClientInterface = (context, service, name, methodKeys, nameMapping) => { const serviceImplement = context.pluginValue('rpcClients.serviceImplement'); const camelRpcMethods = context.pluginValue('rpcClients.camelCase'); const keys = methodKeys && methodKeys.length ? methodKeys : Object.keys(service.methods ?? {}); const methods = keys .map((key) => { const methodName = camelRpcMethods ? camel(key) : key; const implementType = getServiceImplement(service.name, context.ref.proto.package, methodName, serviceImplement); const method = service.methods[key]; if (!method) { return null; } const nameWithPkg = `${context.ref.proto.package}.${methodName}`; const methodAlias = nameMapping && nameMapping[nameWithPkg] ? nameMapping[nameWithPkg] : methodName; const leadingComments = method.comment ? [commentBlock(processRpcComment(method))] : []; let trailingComments = []; switch (implementType) { case "Tx": context.addUtil("DeliverTxResponse"); context.addUtil("StdFee"); return rpcTxMethodDefinition(methodAlias, method, trailingComments, leadingComments); case "Query": default: return rpcMethodDefinition(methodAlias, method, trailingComments, leadingComments); } }).filter(Boolean); const obj = t.exportNamedDeclaration(t.tsInterfaceDeclaration(t.identifier(name ?? service.name), null, [], t.tsInterfaceBody([ ...methods ]))); if (service.comment) { obj.leadingComments = [commentBlock(`* ${service.comment} `)]; } return obj; }; export const getRpcClassName = (service) => { return `${service.name}ClientImpl`; }; export const createRpcClientClass = (context, service) => { const serviceImplement = context.pluginValue('rpcClients.serviceImplement'); const useTelescopeGeneratedType = context.pluginValue('prototypes.typingsFormat.useTelescopeGeneratedType'); if (useTelescopeGeneratedType) { context.addUtil('TxRpc'); } else { context.addUtil('Rpc'); } BinaryCoder.addUtil(context, 'reader'); const camelRpcMethods = context.pluginValue('rpcClients.camelCase'); const name = getRpcClassName(service); const implementsName = service.name; const methodNames = Object.keys(service.methods ?? {}) .map(key => { return camelRpcMethods ? camel(key) : key; }); const methods = Object.keys(service.methods ?? {}) .map(key => { const name = camelRpcMethods ? camel(key) : key; const implementType = getServiceImplement(service.name, context.ref.proto.package, name, serviceImplement); const method = service.methods[key]; switch (implementType) { case "Tx": context.addUtil("DeliverTxResponse"); context.addUtil("StdFee"); return rpcTxClassMethod(context, name, key, method, context.ref.proto.package + '.' + service.name); case "Query": default: return rpcClassMethod(context, name, key, method, context.ref.proto.package + '.' + service.name); } }); return t.exportNamedDeclaration(classDeclaration(t.identifier(name), null, t.classBody([ classProperty(t.identifier('rpc'), null, t.tsTypeAnnotation(t.tsTypeReference(t.identifier(useTelescopeGeneratedType ? 'TxRpc' : 'Rpc'))), null, false, false, true, 'private'), // CONSTRUCTOR rpcClassConstructor(context, methodNames), // METHODS ...methods ]), null, [ t.tsExpressionWithTypeArguments(t.identifier(implementsName)) ])); }; export const createRpcInterface = (context, service) => { return t.tsInterfaceDeclaration(t.identifier('Rpc'), null, [], t.tsInterfaceBody([ t.tsMethodSignature(t.identifier('request'), null, [ identifier('service', t.tsTypeAnnotation(t.tsStringKeyword())), identifier('method', t.tsTypeAnnotation(t.tsStringKeyword())), identifier('data', t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Uint8Array')))) ], t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Promise'), t.tsTypeParameterInstantiation([ t.tsTypeReference(t.identifier('Uint8Array')) ])))) ])); };