UNPKG

@cosmology/ast

Version:
228 lines (227 loc) 10.3 kB
import * as t from '@babel/types'; import { TelescopeLogLevel } from '@cosmology/types'; import { identifier, tsPropertySignature, functionDeclaration, makeCommentBlock } from '../../../utils'; import { getBaseCreateTypeFuncName, getFieldOptionality, getFieldOptionalityForDefaults, getOneOfs } from '../types'; import { SymbolNames, getDefaultTSTypeFromProtoType, getFieldTypeReference, getTSType } from '../../types'; import { getTypeUrlWithPkgAndName, getTypeUrl } from '../../amino'; const getProtoField = (context, field, type = 'Msg') => { let ast = null; const fieldRef = getFieldTypeReference(context, field, type); ast = fieldRef.ast; const isTypeCastableAnyType = fieldRef.isTypeCastableAnyType; if (field.rule === 'repeated') { ast = t.tsArrayType(ast); if (isTypeCastableAnyType) { ast = t.tsUnionType([ ast, t.tsArrayType(t.tsTypeReference(t.identifier('Any'))) ]); } } if (field.keyType) { ast = t.tsUnionType([ t.tsTypeLiteral([ t.tsIndexSignature([ identifier('key', t.tsTypeAnnotation(getTSType(context, field.keyType))) ], t.tsTypeAnnotation(ast)) ]) ]); } return ast; }; export const createProtoType = (context, name, proto, type = 'Msg') => { const oneOfs = getOneOfs(proto); // MARKED AS COSMOS SDK specific code const optionalityMap = {}; // if a param is found to be a route parameter, we assume it's required // if a param is found to be a query parameter, we assume it's optional if (context.pluginValue('prototypes.optionalQueryParams') && context.store.requests[name]) { const svc = context.store.requests[name]; if (svc.info) { svc.info.queryParams.map(param => { optionalityMap[param] = true; }); } } // hard-code optionality for pagination if (context.pluginValue('prototypes.optionalPageRequests')) { if (context.ref.proto.package === 'cosmos.base.query.v1beta1') { if (name === 'PageRequest') { optionalityMap['key'] = true; optionalityMap['offset'] = true; optionalityMap['limit'] = true; optionalityMap['count_total'] = true; optionalityMap['countTotal'] = true; optionalityMap['reverse'] = true; } if (name === 'PageResponse') { optionalityMap['next_key'] = true; optionalityMap['nextKey'] = true; } } } const MsgName = SymbolNames[type](name); const fields = []; if (context.pluginValue('prototypes.addTypeUrlToDecoders') && ((context.pluginValue('interfaces.enabled') && proto.options?.['(cosmos_proto.implements_interface)']) || (context.ref.proto.package === 'google.protobuf' && name === 'Any'))) { const typeUrl = getTypeUrlWithPkgAndName(context.ref.proto.package, name); const typeAnnotation = (context.ref.proto.package === 'google.protobuf' && name === 'Any') ? t.tsTypeAnnotation(t.tsUnionType([ t.tsLiteralType(t.stringLiteral(typeUrl)), t.tsStringKeyword() ])) : t.tsTypeAnnotation(t.tsLiteralType(t.stringLiteral(typeUrl))); fields.push(tsPropertySignature(t.identifier('$typeUrl'), typeAnnotation, true)); } [].push.apply(fields, Object.keys(proto.fields).reduce((m, fieldName) => { const isOneOf = oneOfs.includes(fieldName); const field = proto.fields[fieldName]; const isOptional = getFieldOptionalityForDefaults(context, field, isOneOf); // optionalityMap is coupled to API requests const orig = field.options?.['(telescope:orig)'] ?? fieldName; let optional = false; if (optionalityMap[orig]) { optional = true; } // let fieldNameWithCase = options.useOriginalCase ? orig : fieldName; let fieldNameWithCase = type === 'SDKType' ? orig : fieldName; const protoField = getProtoField(context, field, type); const propSig = tsPropertySignature(t.identifier(fieldNameWithCase), t.tsTypeAnnotation(protoField), // optional || getFieldOptionality(context, field, isOneOf) isOptional || optional); const comments = []; if (field.comment && // no comment for derivative types (type === 'Msg')) { comments.push(makeCommentBlock(field.comment)); } if (field.options?.deprecated) { comments.push(makeCommentBlock('@deprecated')); } if (comments.length) { propSig.leadingComments = comments; } m.push(propSig); return m; }, [])); // declaration const declaration = t.exportNamedDeclaration(t.tsInterfaceDeclaration(t.identifier(MsgName), null, [], t.tsInterfaceBody(fields))); const comments = []; if (proto.comment) { comments.push(makeCommentBlock(proto.comment)); } if (proto.options?.deprecated) { comments.push(makeCommentBlock('@deprecated')); } if (comments.length) { declaration.leadingComments = comments; } return declaration; }; export const createProtoTypeType = (context, name, proto) => { const ProtoMsgName = SymbolNames.ProtoMsg(name); const typeUrl = getTypeUrl(context.ref.proto, proto); const typ = typeUrl ? t.tsLiteralType(t.stringLiteral(typeUrl)) : t.tsTypeReference(t.identifier('string')); return t.exportNamedDeclaration(t.tsInterfaceDeclaration(t.identifier(ProtoMsgName), null, [], t.tsInterfaceBody([ tsPropertySignature(t.identifier(context.options.prototypes.parser.keepCase ? 'type_url' : 'typeUrl'), t.tsTypeAnnotation(typ), false), tsPropertySignature(t.identifier('value'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Uint8Array'))), false) ]))); }; export const createProtoInterfaceEncodedType = (context, name, proto) => { const MsgName = SymbolNames.Msg(name); const EncodedMsgName = SymbolNames.Encoded(name); const oneOfs = getOneOfs(proto); const implementsAcceptsAny = context.pluginValue('interfaces.enabled'); const fieldsWithInfo = Object.keys(proto.fields) .reduce((m, fieldName) => { const isOneOf = oneOfs.includes(fieldName); const field = proto.fields[fieldName]; const lookupInterface = field.options?.['(cosmos_proto.accepts_interface)']; const isAnyType = field.parsedType?.type === 'Type' && field.parsedType?.name === 'Any'; // MARKED AS NOT DRY (symbols) let symbols = null; if (implementsAcceptsAny && lookupInterface) { symbols = context.store._symbols.filter(s => s.implementsType === lookupInterface && s.ref === context.ref.filename); if (!symbols.length && context.options.logLevel >= TelescopeLogLevel.Warn) { console.warn(`[WARN] ${lookupInterface} is accepted but not implemented`); } } const isAnyInterface = isAnyType && lookupInterface && implementsAcceptsAny && symbols; if (!isAnyInterface) return m; // ONLY INTERFACES! return [...m, { isOneOf, field, name: fieldName, lookupInterface, isAnyType, symbols }]; }, []).filter(Boolean); if (!fieldsWithInfo.length) return; const interfaceFields = fieldsWithInfo.map(fieldInfo => { return t.tsLiteralType(t.stringLiteral(fieldInfo.name)); }); const fields = fieldsWithInfo.map(fieldsInfo => { const { field, isOneOf, name: fieldName } = fieldsInfo; let optional = false; const protoField = getProtoField(context, field, 'ProtoMsg'); const propSig = tsPropertySignature(t.identifier(fieldName), t.tsTypeAnnotation(protoField), optional || getFieldOptionality(context, field, isOneOf)); const comments = []; if (field.comment) { comments.push(makeCommentBlock(field.comment)); } if (field.options?.deprecated) { comments.push(makeCommentBlock('@deprecated')); } if (comments.length) { propSig.leadingComments = comments; } return propSig; }); return t.exportNamedDeclaration(t.tsTypeAliasDeclaration(t.identifier(EncodedMsgName), null, t.tsIntersectionType([ t.tsTypeReference(t.identifier('Omit'), t.tsTypeParameterInstantiation([ t.tsTypeReference(t.identifier(MsgName)), interfaceFields.length > 1 ? t.tsUnionType([ ...interfaceFields ]) : interfaceFields[0] ])), t.tsTypeLiteral([ ...fields ]) ]))); }; export const createCreateProtoType = (context, name, proto) => { const oneOfs = getOneOfs(proto); const fields = []; if (context.pluginValue('prototypes.addTypeUrlToDecoders') && ((context.pluginValue('interfaces.enabled') && proto.options?.['(cosmos_proto.implements_interface)']) || (context.ref.proto.package === 'google.protobuf' && name === 'Any'))) { const typeUrl = getTypeUrlWithPkgAndName(context.ref.proto.package, name); fields.push(t.objectProperty(t.identifier('$typeUrl'), t.stringLiteral(typeUrl))); } [].push.apply(fields, Object.keys(proto.fields).map(key => { const isOneOf = oneOfs.includes(key); const isOptional = getFieldOptionality(context, proto.fields[key], isOneOf); return { name: key, ...proto.fields[key], isOneOf, isOptional }; }) .map(field => { return t.objectProperty(t.identifier(field.name), getDefaultTSTypeFromProtoType(context, field, field.isOneOf)); })); return functionDeclaration(t.identifier(getBaseCreateTypeFuncName(name)), [], t.blockStatement([ t.returnStatement(t.objectExpression([ ...fields, ])) ]), false, false, t.tsTypeAnnotation(t.tsTypeReference(t.identifier(name)))); };