@cosmology/ast
Version:
Cosmos TypeScript AST generation
469 lines (468 loc) • 22.6 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, checkValExpr) => {
const { propName, origName } = getFieldNames(args.field);
const omitEmpty = shouldOmitEmpty(args.context, args.field);
valExpr =
valExpr ??
t.memberExpression(t.identifier("message"), t.identifier(propName));
checkValExpr = checkValExpr ?? valExpr;
if (omitEmpty) {
valExpr = t.conditionalExpression(t.binaryExpression("===", checkValExpr, 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;
let checkValExpr;
const useCosmosSDKDec = args.context.pluginValue("aminoEncoding.customTypes.useCosmosSDKDec");
const goPackage = args.context.ref.proto.options?.["go_package"];
if (useCosmosSDKDec) {
const isCosmosSDKDec = (goPackage == "github.com/cosmos/cosmos-sdk/types" &&
args.field.options?.["(gogoproto.customtype)"] == "Dec") ||
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("Decimal");
const { propName } = getFieldNames(args.field);
valueExpr = t.memberExpression(t.callExpression(t.memberExpression(t.identifier("Decimal"), t.identifier("fromUserInput")), [
t.memberExpression(t.identifier("message"), t.identifier(propName)),
t.numericLiteral(18),
]), t.identifier("atomics"));
checkValExpr = t.memberExpression(t.identifier("message"), t.identifier(propName));
}
}
return setValue(args, valueExpr, checkValExpr);
},
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 accepts_interface = args.field.options["(cosmos_proto.accepts_interface)"];
const interfaceName = accepts_interface.split(",")[0];
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("Decimal");
return t.memberExpression(t.callExpression(t.memberExpression(t.identifier("Decimal"), t.identifier("fromUserInput")), [
t.identifier("e"),
t.numericLiteral(18),
]), t.identifier("atomics"));
}
}
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 accepts_interface = args.field.options["(cosmos_proto.accepts_interface)"];
const interfaceName = accepts_interface.split(",")[0];
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")), []));
}
},
};