UNPKG

@cosmology/ast

Version:
329 lines (328 loc) 14.7 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (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; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.createAggregatedLCDClient = exports.createLCDClient = exports.makeTemplateTag = exports.getUrlTemplateString = void 0; const t = __importStar(require("@babel/types")); const utils_1 = require("../../../utils"); const getResponseTypeName = (context, name) => { return name + (context.options.useSDKTypes ? 'SDKType' : ''); }; const returnReponseType = (context, name) => { return t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Promise'), t.tsTypeParameterInstantiation([ t.tsTypeReference(t.identifier(getResponseTypeName(context, name))) ]))); }; const firstLower = (s) => s = s.charAt(0).toLowerCase() + s.slice(1); const firstUpper = (s) => s = s.charAt(0).toUpperCase() + s.slice(1); const returnAwaitRequest = (context, responseType, // method: 'get' | 'post', hasOptions = false) => { const args = [ t.identifier('endpoint') ]; // if (method === 'post') { // args.push(t.identifier('body')); // } if (hasOptions) { args.push(t.identifier('options')); } let returned = t.awaitExpression((0, utils_1.callExpression)(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier('req')), t.identifier('get')), args, t.tsTypeParameterInstantiation([ t.tsTypeReference(t.identifier(getResponseTypeName(context, responseType))) ]))); if (context.pluginValue('useSDKTypes') && context.pluginValue('prototypes.methods.fromSDKJSON')) { //useSDKTypes && prototypes.methods.fromSDKJSON returned = t.callExpression(t.memberExpression(t.identifier(responseType), t.identifier('fromSDKJSON')), [returned]); } else if (!context.pluginValue('useSDKTypes') && context.pluginValue('prototypes.methods.fromJSON')) { //!useSDKTypes && prototypes.methods.fromJSON returned = t.callExpression(t.memberExpression(t.identifier(responseType), t.identifier('fromJSON')), [returned]); } return t.returnStatement(returned); }; const makeOptionsObject = () => { return t.variableDeclaration('const', [ t.variableDeclarator((0, utils_1.identifier)('options', t.tsTypeAnnotation(t.tsAnyKeyword())), t.objectExpression([ t.objectProperty(t.identifier('params'), t.objectExpression([])) ])) ]); }; const setParamOption = (context, name, svc) => { const flippedCasing = Object.keys(svc.info.casing).reduce((m, v) => { m[svc.info.casing[v]] = v; return m; }, {}); const queryParam = flippedCasing[name] ? flippedCasing[name] : name; const param = svc.info.paramMap[name]; // options.params.group_id = params.groupId; let expr = t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.memberExpression(t.identifier('options'), t.identifier('params')), t.identifier(queryParam)), t.memberExpression(t.identifier('params'), t.identifier(param)))); if (name === 'pagination') { context.addUtil('setPaginationParams'); expr = t.expressionStatement(t.callExpression(t.identifier('setPaginationParams'), [ t.identifier('options'), t.memberExpression(t.identifier('params'), t.identifier('pagination'), false) ])); } return t.ifStatement(t.binaryExpression('!==', t.unaryExpression('typeof', t.optionalMemberExpression(t.identifier('params'), t.identifier(param), false, true)), t.stringLiteral('undefined')), t.blockStatement([ expr ])); }; // breaks a url string to prepare it for template strings const getUrlTemplateString = (url) => { const parts = url.split('/').filter(a => a !== ''); let cur = []; let strs = []; let atEnd = false; for (let p = 0; p < parts.length; p++) { const part = parts[p]; if (/[{}]+/.test(part)) { if (p === parts.length - 1) atEnd = true; if (cur.length) { const vals = cur.join('/'); strs.push(vals); } else { strs.push('/'); } cur = []; } else { cur.push(part); } } if (cur.length) { strs.push(cur.join('/')); } strs = strs.filter(str => str !== '').map((v, i) => { if (i === 0) { if (!v.endsWith('/')) v = `${v}/`; return v; } else if (i === strs.length - 1) { if (atEnd) { // we want them to end with / if it's an "atEnd" el if (!v.endsWith('/')) v = `${v}/`; return v; } // they should all start with "/" if (!v.startsWith('/')) v = `/${v}`; return v; } if (!v.endsWith('/')) v = `${v}/`; if (!v.startsWith('/')) v = `/${v}`; return v; }); return { strs, atEnd }; }; exports.getUrlTemplateString = getUrlTemplateString; const routeRegexForReplace = /[^\{\}\\-\_\\.$/a-zA-Z0-9]+/g; function makeTemplateTag(info, noLeadingSlash = true) { const route = info.url .split('/') .filter(a => a !== '') .map(a => { if (a.startsWith('{')) { // clean weird routes like this one: // /ibc/apps/transfer/v1/denom_traces/{hash=**} return a.replace(routeRegexForReplace, ''); } else { return a; } }) .join('/'); const segments = route.split('/'); const expressions = []; const quasis = []; let accumulatedPath = ''; let isFirst = true; segments.forEach((segment, _index) => { if (noLeadingSlash && segment === '') return; if (segment.startsWith('{') && segment.endsWith('}')) { // Dynamic segment const paramName = segment.slice(1, -1); // Push the accumulated static text as a quasi before adding the expression quasis.push(t.templateElement({ raw: accumulatedPath + '/', cooked: accumulatedPath }, false)); accumulatedPath = ''; // Reset accumulated path after adding to quasis // expressions.push(t.identifier(`params.${paramName}`)); expressions.push(t.memberExpression(t.identifier('params'), t.identifier(info.casing?.[paramName] ? info.casing[paramName] : paramName))); // Prepare the next quasi to start with a slash if this is not the last segment isFirst = false; } else { // Accumulate static text, ensuring to prepend a slash if it's not the first segment accumulatedPath += (isFirst ? '' : '/') + segment; isFirst = false; } }); // Add the final accumulated static text as the last quasi quasis.push(t.templateElement({ raw: accumulatedPath, cooked: accumulatedPath }, true)); // Mark the last quasi as tail return t.templateLiteral(quasis, expressions); } exports.makeTemplateTag = makeTemplateTag; const makeComment = (comment) => { return [{ type: 'CommentBlock', value: ` ${comment} ` }]; }; const buildRequestMethod = (context, serviceMethod) => { const methodName = firstLower(serviceMethod.name); const comment = serviceMethod.comment ?? serviceMethod.name; if (!serviceMethod.info) { throw new Error('No Service URL!'); } const queryParams = serviceMethod.info.queryParams.map(param => { return setParamOption(context, param, serviceMethod); }); const optionsAst = []; if (serviceMethod.info.queryParams.length) { // options params object optionsAst.push(makeOptionsObject()); } // parse field types Object.entries(serviceMethod.fields ?? {}) .forEach(([key, value]) => { switch (value.parsedType.type) { case 'Type': // this gets the import for us and loads them into ctx // if later we need to get subtypes, we have it all w/ctx context.getTypeName(value); case 'native': } }); const fieldNames = Object.keys(serviceMethod.fields ?? {}); const hasParams = fieldNames.length > 0; const paramName = hasParams ? 'params' : '_params'; let methodArgs = (0, utils_1.identifier)(paramName, t.tsTypeAnnotation(t.tsTypeReference(t.identifier(serviceMethod.requestType)))); // 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([ ...optionsAst, // if optional params not undefined ...queryParams, // endpoint t.variableDeclaration('const', [ t.variableDeclarator(t.identifier('endpoint'), makeTemplateTag(serviceMethod.info)) ]), // return returnAwaitRequest(context, serviceMethod.responseType, // serviceMethod.info.method, serviceMethod.info.queryParams.length > 0) ]); if (context.pluginValue('classesUseArrowFunctions')) { return (0, utils_1.classProperty)(t.identifier(methodName), (0, utils_1.arrowFunctionExpression)([methodArgs], body, t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Promise'), t.tsTypeParameterInstantiation([ t.tsTypeReference(t.identifier(getResponseTypeName(context, serviceMethod.responseType))) ]))), true), undefined, undefined, undefined, undefined, undefined, undefined, makeComment(comment)); } return (0, utils_1.classMethod)('method', t.identifier(methodName), [ methodArgs ], body, returnReponseType(context, serviceMethod.responseType), makeComment(comment), false, false, false, true // async ); }; // 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 createLCDClientClassBody = (context, clientName, methods, service) => { let boundMethods = []; // until the super() issue is figured out, we have to remove this if (service && !context.pluginValue('classesUseArrowFunctions')) { boundMethods = Object.keys(service.methods).map(key => { const method = service.methods[key]; if (typeof method.options?.['(google.api.http).get'] !== 'undefined') { const methodName = firstLower(method.name); return bindThis(methodName); } }).filter(Boolean); } return t.exportNamedDeclaration(t.classDeclaration(t.identifier(clientName), null, t.classBody([ t.classProperty(t.identifier('req'), null, t.tsTypeAnnotation(t.tsTypeReference(t.identifier('LCDClient')))), // constructor t.classMethod('constructor', t.identifier('constructor'), [ (0, utils_1.objectPattern)([ t.objectProperty(t.identifier('requestClient'), t.identifier('requestClient'), false, true) ], t.tsTypeAnnotation(t.tsTypeLiteral([ t.tsPropertySignature(t.identifier('requestClient'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier('LCDClient')))) ]))) ], t.blockStatement([ t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.thisExpression(), t.identifier('req')), t.identifier('requestClient'))), /// methods ...boundMethods ])), ...methods ]))); }; const createLCDClient = (context, service) => { const methods = Object.keys(service.methods).map(key => { const method = service.methods[key]; if (method.info && (typeof method.options?.['(google.api.http).get'] !== 'undefined')) { return buildRequestMethod(context, method); } }).filter(Boolean); context.addUtil('LCDClient'); if (methods.length) { const clientName = 'LCDQueryClient'; return createLCDClientClassBody(context, clientName, methods, service); } }; exports.createLCDClient = createLCDClient; const createAggregatedLCDClient = (context, services, clientName) => { context.addUtil('LCDClient'); const methods = services.reduce((m, service) => { const innerMethods = Object.keys(service.methods).map(key => { const method = service.methods[key]; if (method.info && (typeof method.options?.['(google.api.http).get'] !== 'undefined')) { return buildRequestMethod(context, method); } }).filter(Boolean); return [...m, ...innerMethods]; }, []); return createLCDClientClassBody(context, clientName, methods); }; exports.createAggregatedLCDClient = createAggregatedLCDClient;