@cosmology/ast
Version:
Cosmos TypeScript AST generation
449 lines (448 loc) • 21.7 kB
JavaScript
import * as t from '@babel/types';
import { BILLION, identifier, TypeLong } from '../../../utils';
import { getDefaultTSTypeFromProtoType, getFieldNames, getDefaultTSTypeFromAminoTypeDefault } from '../../types';
import { getInterfaceToAminoName } from '../implements';
import { shouldOmitEmpty } from '@cosmology/utils';
const setValue = (args, valExpr) => {
const { propName, origName } = getFieldNames(args.field);
const omitEmpty = shouldOmitEmpty(args.context, args.field);
valExpr = valExpr ?? t.memberExpression(t.identifier("message"), t.identifier(propName));
if (omitEmpty) {
valExpr = t.conditionalExpression(t.binaryExpression("===", valExpr, getDefaultTSTypeFromProtoType(args.context, args.field, args.isOneOf, true)), t.identifier('undefined'), valExpr);
}
else {
valExpr = t.logicalExpression("??", valExpr, getDefaultTSTypeFromProtoType(args.context, args.field, args.isOneOf, true));
}
return t.expressionStatement(t.assignmentExpression("=", t.memberExpression(t.identifier("obj"), t.identifier(origName)), valExpr));
};
export const toAminoJSON = {
scalar(args, valExpr) {
return setValue(args, valExpr);
},
string(args) {
let valueExpr;
const useCosmosSDKDec = args.context.pluginValue('aminoEncoding.customTypes.useCosmosSDKDec');
if (useCosmosSDKDec) {
const isCosmosSDKDec = (args.field.options?.['(gogoproto.customtype)'] ==
'github.com/cosmos/cosmos-sdk/types.Dec') ||
(args.field.options?.['(gogoproto.customtype)'] ==
'cosmossdk.io/math.LegacyDec');
if (isCosmosSDKDec) {
args.context.addUtil('padDecimal');
const { propName } = getFieldNames(args.field);
valueExpr = t.callExpression(t.identifier('padDecimal'), [
t.memberExpression(t.identifier('message'), t.identifier(propName))
]);
}
}
return setValue(args, valueExpr);
},
double(args) {
return toAminoJSON.scalar(args);
},
float(args) {
return toAminoJSON.scalar(args);
},
bool(args) {
return toAminoJSON.scalar(args);
},
number(args) {
return toAminoJSON.scalar(args);
},
int32(args) {
return toAminoJSON.scalar(args);
},
uint32(args) {
return toAminoJSON.scalar(args);
},
sint32(args) {
return toAminoJSON.scalar(args);
},
fixed32(args) {
return toAminoJSON.scalar(args);
},
sfixed32(args) {
return toAminoJSON.scalar(args);
},
// obj.big = message.big ? message.big.toString() : "0";
// obj.o_big = message.oBig !== BigInt(0) ? message.oBig.toString() : undefined;
long(args) {
const { propName, origName } = getFieldNames(args.field);
const omitEmpty = shouldOmitEmpty(args.context, args.field);
const nullTest = omitEmpty
? TypeLong.getLongNotZero(propName, args.context)
: t.memberExpression(t.identifier("message"), t.identifier(propName));
const template = require('@babel/template').default;
// Create a template that represents message.oBig.toString()
const expressionTemplate = template.expression(`
MESSAGE.OBJECT?.METHOD()
`);
// Generate AST node from the template by providing the identifiers
const messageNode = expressionTemplate({
MESSAGE: t.identifier('message'),
OBJECT: t.identifier(propName),
METHOD: t.identifier('toString'),
});
return t.expressionStatement(t.assignmentExpression("=", t.memberExpression(t.identifier("obj"), t.identifier(origName)), t.conditionalExpression(nullTest, messageNode, omitEmpty ? t.identifier("undefined") : t.stringLiteral("0"))));
},
int64(args) {
return toAminoJSON.long(args);
},
uint64(args) {
return toAminoJSON.long(args);
},
sint64(args) {
return toAminoJSON.long(args);
},
fixed64(args) {
return toAminoJSON.long(args);
},
sfixed64(args) {
return toAminoJSON.long(args);
},
// obj.proto = message.proto ? AccessConfig.toAmino(message.proto) : AccessConfig.toAmino(AccessConfig.fromPartial({}));
// obj.o_proto = message.oProto ? AccessConfig.toAmino(message.oProto) : undefined;
protoType(args) {
const { propName, origName } = getFieldNames(args.field);
const name = args.context.getTypeName(args.field);
const omitEmpty = shouldOmitEmpty(args.context, args.field);
let defaultValue = omitEmpty ? t.identifier('undefined') : getDefaultTSTypeFromAminoTypeDefault(args.context, args.field);
if (args.field.type === 'ibc.core.client.v1.Height') {
defaultValue = t.objectExpression([]);
}
return t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier('obj'), t.identifier(origName)), t.conditionalExpression(t.memberExpression(t.identifier('message'), t.identifier(propName)), t.callExpression(t.memberExpression(t.identifier(name), t.identifier('toAmino')), [
t.memberExpression(t.identifier('message'), t.identifier(propName)),
...(args.context.options.interfaces.enabled && args.context.options.interfaces.useUseInterfacesParams ? [
t.identifier('useInterfaces')
] : []),
]), defaultValue)));
},
anyType(args) {
const { propName, origName } = getFieldNames(args.field);
const interfaceName = args.field.options['(cosmos_proto.accepts_interface)'];
const interfaceFnName = getInterfaceToAminoName(interfaceName);
args.context.getTypeName(args.field);
const omitEmpty = shouldOmitEmpty(args.context, args.field);
let defaultValue = !omitEmpty ? t.objectExpression([
t.objectProperty(t.identifier("type"), t.stringLiteral("")),
t.objectProperty(t.identifier("value"), t.objectExpression([])),
]) : t.identifier('undefined');
let aminoFuncExpr = t.callExpression(t.identifier(interfaceFnName), [
t.tsAsExpression(t.memberExpression(t.identifier('message'), t.identifier(propName)), t.tsTypeReference(t.identifier('Any'))),
...(args.context.options.interfaces.enabled && args.context.options.interfaces.useUseInterfacesParams ? [
t.identifier('useInterfaces')
] : []),
]);
const isGlobalRegistry = args.context.options.interfaces?.useGlobalDecoderRegistry;
if (isGlobalRegistry) {
aminoFuncExpr = t.callExpression(t.memberExpression(t.identifier('GlobalDecoderRegistry'), t.identifier('toAminoMsg')), [
t.memberExpression(t.identifier('message'), t.identifier(propName))
]);
}
return t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier('obj'), t.identifier(origName)), t.conditionalExpression(t.memberExpression(t.identifier('message'), t.identifier(propName)), aminoFuncExpr, defaultValue)));
},
type(args) {
if (!args.context.options.aminoEncoding.useLegacyInlineEncoding &&
args.context.options.interfaces.enabled &&
args.field.type === 'google.protobuf.Any' &&
args.field.options['(cosmos_proto.accepts_interface)']) {
return toAminoJSON.anyType(args);
}
return toAminoJSON.protoType(args);
},
enum(args) {
return toAminoJSON.scalar(args);
},
bytes(args) {
args.context.addUtil('base64FromBytes');
const { propName, origName } = getFieldNames(args.field);
const omitEmpty = shouldOmitEmpty(args.context, args.field);
let defaultValue = !omitEmpty ? t.stringLiteral("") : t.identifier('undefined');
const expr = t.callExpression(t.identifier('base64FromBytes'), [
t.memberExpression(t.identifier('message'), t.identifier(propName))
]);
return t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier('obj'), t.identifier(origName)), t.conditionalExpression(t.memberExpression(t.identifier('message'), t.identifier(propName)), expr, defaultValue)));
},
duration(args) {
return toAminoJSON.type(args);
},
timestamp(args) {
const timestampFormat = args.context.pluginValue('prototypes.typingsFormat.timestamp');
switch (timestampFormat) {
case 'timestamp':
return toAminoJSON.type(args);
case 'date':
default:
return toAminoJSON.timestampDate(args);
}
},
timestampDate(args) {
const { propName, origName } = getFieldNames(args.field);
args.context.addUtil('toTimestamp');
const omitEmpty = shouldOmitEmpty(args.context, args.field);
let defaultValue = !omitEmpty ? getDefaultTSTypeFromProtoType(args.context, args.field, args.isOneOf, true) : t.identifier('undefined');
return t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier('obj'), t.identifier(origName)), t.conditionalExpression(t.memberExpression(t.identifier('message'), t.identifier(propName)), t.callExpression(t.memberExpression(t.identifier('Timestamp'), t.identifier('toAmino')), [
t.callExpression(t.identifier('toTimestamp'), [
t.memberExpression(t.identifier('message'), t.identifier(propName))
])
]), defaultValue)));
},
pubkey(args) {
args.context.addUtil('decodePubkey');
const { propName, origName } = getFieldNames(args.field);
const omitEmpty = shouldOmitEmpty(args.context, args.field);
let defaultValue = !omitEmpty ? getDefaultTSTypeFromProtoType(args.context, args.field, args.isOneOf, true) : t.identifier('undefined');
return t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier('obj'), t.identifier(origName)), t.conditionalExpression(t.memberExpression(t.identifier('message'), t.identifier(propName)),
//
t.callExpression(t.identifier('decodePubkey'), [
t.memberExpression(t.identifier('message'), t.identifier(propName)),
]),
//
defaultValue)));
},
rawBytes(args) {
args.context.addUtil('fromUtf8');
const { propName, origName } = getFieldNames(args.field);
const omitEmpty = shouldOmitEmpty(args.context, args.field);
let defaultValue = !omitEmpty ? t.objectExpression([]) : t.identifier('undefined');
return t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier('obj'), t.identifier(origName)), t.conditionalExpression(t.memberExpression(t.identifier('message'), t.identifier(propName)),
//
t.callExpression(t.memberExpression(t.identifier('JSON'), t.identifier('parse')), [
t.callExpression(t.identifier('fromUtf8'), [
t.memberExpression(t.identifier('message'), t.identifier(propName)),
])
]),
//
defaultValue)));
},
wasmByteCode(args) {
args.context.addUtil('toBase64');
const { propName, origName } = getFieldNames(args.field);
const omitEmpty = shouldOmitEmpty(args.context, args.field);
let defaultValue = !omitEmpty ? t.stringLiteral("") : t.identifier('undefined');
return t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier('obj'), t.identifier(origName)), t.conditionalExpression(t.memberExpression(t.identifier('message'), t.identifier(propName)),
//
t.callExpression(t.identifier('toBase64'), [
t.memberExpression(t.identifier('message'), t.identifier(propName)),
]),
//
defaultValue)));
},
keyHash(args) {
const { propName, origName } = getFieldNames(args.field);
const keyType = args.field.keyType;
const valueType = args.field.parsedType.name;
let toAminoJSON = null;
switch (valueType) {
case 'string':
toAminoJSON = t.identifier('v');
break;
case 'uint32':
case 'int32':
toAminoJSON = t.callExpression(t.memberExpression(t.identifier('Math'), t.identifier('round')), [
t.identifier('v')
]);
break;
case 'int64':
case 'uint64':
toAminoJSON = t.callExpression(t.memberExpression(t.identifier('v'), t.identifier('toString')), []);
break;
default:
toAminoJSON = t.callExpression(t.memberExpression(t.identifier(valueType), t.identifier('toAmino')), [
t.identifier('v')
]);
}
return [
t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier('obj'), t.identifier(origName)), t.objectExpression([]))),
//
t.ifStatement(t.memberExpression(t.identifier('message'), t.identifier(propName)), t.blockStatement([
t.expressionStatement(t.callExpression(t.memberExpression(t.callExpression(t.memberExpression(t.identifier('Object'), t.identifier('entries')), [
t.memberExpression(t.identifier('message'), t.identifier(propName))
]), t.identifier('forEach')), [
t.arrowFunctionExpression([
t.arrayPattern([
t.identifier('k'),
t.identifier('v')
])
], t.blockStatement([
t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.memberExpression(t.identifier('obj'), t.identifier(origName)), t.identifier('k'), true), toAminoJSON))
]))
]))
]))
];
},
array(args, expr) {
const { propName, origName } = getFieldNames(args.field);
return t.ifStatement(t.memberExpression(t.identifier('message'), t.identifier(propName)), t.blockStatement([
t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier('obj'), t.identifier(origName)), t.callExpression(t.memberExpression(t.memberExpression(t.identifier('message'), t.identifier(propName)), t.identifier('map')), [
t.arrowFunctionExpression([
t.identifier('e')
], expr)
])))
]), t.blockStatement([
t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier('obj'), t.identifier(origName)), t.memberExpression(t.identifier('message'), t.identifier(propName))))
]));
}
};
export const arrayTypes = {
scalar() {
return t.identifier('e');
},
string(args) {
const useCosmosSDKDec = args.context.pluginValue('aminoEncoding.customTypes.useCosmosSDKDec');
if (useCosmosSDKDec) {
const isCosmosSDKDec = (args.field.options?.['(gogoproto.customtype)'] ==
'github.com/cosmos/cosmos-sdk/types.Dec') ||
(args.field.options?.['(gogoproto.customtype)'] ==
'cosmossdk.io/math.LegacyDec');
if (isCosmosSDKDec) {
args.context.addUtil('padDecimal');
const { propName } = getFieldNames(args.field);
return t.callExpression(t.identifier('padDecimal'), [
t.identifier('e')
]);
}
}
return arrayTypes.scalar();
},
double() {
return arrayTypes.scalar();
},
float() {
return arrayTypes.scalar();
},
bool() {
return arrayTypes.scalar();
},
number() {
return arrayTypes.scalar();
},
int32() {
return arrayTypes.number();
},
uint32() {
return arrayTypes.number();
},
sint32() {
return arrayTypes.number();
},
fixed32() {
return arrayTypes.number();
},
sfixed32() {
return arrayTypes.number();
},
long(args) {
return TypeLong.getToStringArray(args.context);
},
int64(args) {
return arrayTypes.long(args);
},
uint64(args) {
return arrayTypes.long(args);
},
sint64(args) {
return arrayTypes.long(args);
},
fixed64(args) {
return arrayTypes.long(args);
},
sfixed64(args) {
return arrayTypes.long(args);
},
rawBytes(args) {
args.context.addUtil("fromUtf8");
return t.callExpression(t.memberExpression(t.identifier("JSON"), t.identifier("parse")), [
t.callExpression(t.identifier("fromUtf8"), [t.identifier("e")]),
]);
},
wasmByteCode(args) {
args.context.addUtil("toBase64");
return t.callExpression(t.identifier("toBase64"), [
t.identifier("e")
]);
},
bytes(args) {
// bytes [RawContractMessage]
if (args.field.options?.["(gogoproto.casttype)"] === "RawContractMessage") {
return arrayTypes.rawBytes(args);
}
// bytes [WASMByteCode]
if (args.field.options?.["(gogoproto.customname)"] === "WASMByteCode") {
return arrayTypes.wasmByteCode(args);
}
//default
args.context.addUtil("base64FromBytes");
return t.callExpression(t.identifier("base64FromBytes"), [
t.identifier("e"),
]);
},
enum() {
return arrayTypes.scalar();
},
anyType(args) {
const { propName, origName } = getFieldNames(args.field);
const interfaceName = args.field.options['(cosmos_proto.accepts_interface)'];
const interfaceFnName = getInterfaceToAminoName(interfaceName);
const isGlobalRegistry = args.context.options.interfaces?.useGlobalDecoderRegistry;
let aminoFuncExpr = t.callExpression(t.identifier(interfaceFnName), [
t.tsAsExpression(t.identifier('e'), t.tsTypeReference(t.identifier('Any'))),
...(args.context.options.interfaces.enabled && args.context.options.interfaces.useUseInterfacesParams ? [
t.identifier('useInterfaces')
] : []),
]);
if (isGlobalRegistry) {
aminoFuncExpr = t.callExpression(t.memberExpression(t.identifier('GlobalDecoderRegistry'), t.identifier('toAminoMsg')), [
t.identifier('e')
]);
}
return t.conditionalExpression(t.identifier('e'), aminoFuncExpr, t.identifier('undefined'));
},
protoType(args) {
const name = args.context.getTypeName(args.field);
return t.conditionalExpression(t.identifier('e'), t.callExpression(t.memberExpression(t.identifier(name), t.identifier('toAmino')), [
t.identifier('e'),
...(args.context.options.interfaces.enabled && args.context.options.interfaces.useUseInterfacesParams ? [
t.identifier('useInterfaces')
] : []),
]), t.identifier('undefined'));
},
type(args) {
if (!args.context.options.aminoEncoding.useLegacyInlineEncoding &&
args.context.options.interfaces.enabled &&
args.field.type === 'google.protobuf.Any' &&
args.field.options['(cosmos_proto.accepts_interface)']) {
return arrayTypes.anyType(args);
}
return arrayTypes.protoType(args);
}
};
export const toAminoMessages = {
anyType() {
return [
t.variableDeclaration('const', [
t.variableDeclarator(identifier('obj', t.tsTypeAnnotation(t.tsAnyKeyword())), t.objectExpression([]))
]),
t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier('obj'), t.identifier('type')), t.memberExpression(t.identifier('message'), t.identifier('typeUrl')))),
t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier('obj'), t.identifier('value')), t.memberExpression(t.identifier('message'), t.identifier('value')))),
t.returnStatement(t.identifier('obj'))
];
},
timestamp(context, name, proto) {
context.addUtil('fromTimestamp');
return t.returnStatement(t.callExpression(t.memberExpression(t.callExpression(t.memberExpression(t.callExpression(t.identifier('fromTimestamp'), [t.identifier('message')]), t.identifier('toISOString')), []), t.identifier('replace')), [
t.regExpLiteral('\\.\\d+Z$'),
t.stringLiteral('Z')
]));
},
duration(context, name, proto) {
const longType = TypeLong.getType(context);
switch (longType) {
case 'BigInt':
return t.returnStatement(t.callExpression(t.memberExpression(t.parenthesizedExpression(t.binaryExpression('+', t.binaryExpression('*', t.memberExpression(t.identifier('message'), t.identifier('seconds')), t.callExpression(t.identifier('BigInt'), [t.stringLiteral("1000000000")])), t.callExpression(t.identifier('BigInt'), [t.memberExpression(t.identifier('message'), t.identifier('nanos'))]))), t.identifier('toString')), []));
case 'Long':
default:
return t.returnStatement(t.callExpression(t.memberExpression(t.binaryExpression('+', t.binaryExpression('*', t.callExpression(t.memberExpression(t.memberExpression(t.identifier('message'), t.identifier('seconds')), t.identifier('toInt')), []), BILLION), t.memberExpression(t.identifier('message'), t.identifier('nanos'))), t.identifier('toString')), []));
}
}
};