UNPKG

@cosmology/ast

Version:
433 lines (372 loc) 12.5 kB
import * as t from '@babel/types'; import { ProtoType, ProtoField, TelescopeLogLevel } from '@cosmology/types'; import { identifier, tsPropertySignature, functionDeclaration, makeCommentBlock } from '../../../utils'; import { ProtoParseContext } from '../../context'; import { getBaseCreateTypeFuncName, getFieldOptionality, getFieldOptionalityForDefaults, getOneOfs } from '../types'; import { SymbolNames, getDefaultTSTypeFromProtoType, getFieldTypeReference, TelescopeBaseTypes, getTSType } from '../../types'; import { getTypeUrlWithPkgAndName, getTypeUrl } from '../../amino'; import { TraversalSymbol } from '@cosmology/types'; const getProtoField = ( context: ProtoParseContext, field: ProtoField, type: TelescopeBaseTypes = 'Msg' ) => { let ast: any = 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: ProtoParseContext, name: string, proto: ProtoType, type: TelescopeBaseTypes = '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: ProtoParseContext, name: string, proto: ProtoType ) => { 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 ) ]))); }; interface FieldInfo { isOneOf: boolean, field: ProtoField, name: string, lookupInterface: string, isAnyType: boolean, symbols: TraversalSymbol[] } export const createProtoInterfaceEncodedType = ( context: ProtoParseContext, name: string, proto: ProtoType ) => { const MsgName = SymbolNames.Msg(name); const EncodedMsgName = SymbolNames.Encoded(name); const oneOfs = getOneOfs(proto); const implementsAcceptsAny = context.pluginValue('interfaces.enabled'); const fieldsWithInfo: FieldInfo[] = 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: ProtoParseContext, name: string, proto: ProtoType ) => { 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)) )) };