@cosmology/ast
Version:
Cosmos TypeScript AST generation
478 lines (477 loc) • 17.8 kB
JavaScript
"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.getDefaultTSTypeFromAminoTypeDefault = exports.getDefaultTSTypeFromProtoType = exports.getTSTypeForProto = exports.getTSTypeForAmino = exports.getTSTypeFromGoogleType = exports.getTSAminoType = exports.getTSType = exports.getFieldAminoTypeReference = exports.getFieldTypeReference = exports.SymbolNames = exports.getFieldNames = void 0;
const t = __importStar(require("@babel/types"));
const types_1 = require("@cosmology/types");
const utils_1 = require("../utils");
const proto_1 = require("./proto");
const getFieldNames = (field) => {
const propName = field.options?.['(telescope:name)'] ?? field.name;
const origName = field.options?.['(telescope:orig)'] ?? field.name;
return {
propName,
origName
};
};
exports.getFieldNames = getFieldNames;
const getSymbolName = (name, type = 'Msg') => {
let typeNameSuffix;
switch (type) {
case 'ProtoMsg':
typeNameSuffix = 'ProtoMsg';
break;
case 'AminoMsg':
typeNameSuffix = 'AminoMsg';
break;
case 'Amino':
typeNameSuffix = 'Amino';
break;
case 'SDKType':
typeNameSuffix = 'SDKType';
break;
case 'Encoded':
typeNameSuffix = 'Encoded';
break;
case 'Msg':
default:
}
return [name, typeNameSuffix].filter(Boolean).join('');
};
exports.SymbolNames = {
Msg: (name) => getSymbolName(name, 'Msg'),
SDKType: (name) => getSymbolName(name, 'SDKType'),
ProtoMsg: (name) => getSymbolName(name, 'ProtoMsg'),
AminoMsg: (name) => getSymbolName(name, 'AminoMsg'),
Amino: (name) => getSymbolName(name, 'Amino'),
Encoded: (name) => getSymbolName(name, 'Encoded'),
};
const getFieldTypeReference = (context, field, type = 'Msg') => {
let ast = null;
let typ = null;
if (proto_1.SCALAR_TYPES.includes(field.type)) {
// return on scalar
typ = (0, exports.getTSTypeForProto)(context, field);
return {
ast: typ
};
}
else if (proto_1.GOOGLE_TYPES.includes(field.type)) {
typ = (0, exports.getTSTypeFromGoogleType)(context, field.type, type);
}
else {
const propName = (0, utils_1.getProtoFieldTypeName)(context, field);
const MsgName = field.parsedType?.type === 'Enum' ? propName : exports.SymbolNames[type](propName);
typ = t.tsTypeReference(t.identifier(MsgName));
}
const implementsAcceptsAny = context.pluginValue('interfaces.enabled');
const lookupInterface = field.options?.['(cosmos_proto.accepts_interface)'];
const isAnyType = field.parsedType?.type === 'Type' && field.parsedType?.name === 'Any';
const isArray = field.rule === 'repeated';
const isBaseType = type === 'Msg';
const isEncodedType = type === 'ProtoMsg';
const isSDKType = type === 'SDKType';
// 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 >= types_1.TelescopeLogLevel.Warn) {
console.warn(`[WARN] ${lookupInterface} is accepted but not implemented`);
}
}
if (!isBaseType) {
if (['ProtoMsg', 'SDKType'].includes(type)) {
symbols?.forEach(s => {
context.addImportDerivative({
type,
symbol: s
});
});
}
// main type could be Any
if (['SDKType'].includes(type) &&
// no derivatives for Enums!
field.parsedType.type === 'Type') {
context.addImportDerivative({
type,
symbol: {
ref: context.ref.filename,
readAs: field.parsedType.name,
source: field.import,
symbolName: field.parsedType.name,
type: 'import'
},
});
}
}
// cast Any types!
const isAnyInterface = isAnyType && lookupInterface && implementsAcceptsAny && symbols;
const isTypeCastable = isAnyInterface && isBaseType;
const isProtoTypeCastable = isAnyInterface && isEncodedType;
const isSDKTypeCastable = isAnyInterface && isSDKType;
const isNonArrayNullableType = field.parsedType?.type === 'Type' &&
field.rule !== 'repeated' &&
context.pluginValue('prototypes.allowUndefinedTypes');
if (isTypeCastable) {
const tp = symbols.map(a => t.tsTypeReference(t.identifier(a.readAs)));
tp.push(typ);
if (context.pluginValue('interfaces.useUnionTypes')) {
if (!isArray) {
tp.push(t.tsUndefinedKeyword());
}
ast = t.tsUnionType(tp);
}
else {
// intersections
if (isArray) {
ast = t.tsIntersectionType(tp);
}
else {
ast = t.tsUnionType([
t.tsIntersectionType(tp),
t.tsUndefinedKeyword()
]);
}
}
}
else if (isProtoTypeCastable) {
const tp = symbols.map(a => t.tsTypeReference(t.identifier(exports.SymbolNames.ProtoMsg(a.readAs))));
symbols.forEach(a => {
context.addImportDerivative({
type: 'ProtoMsg',
symbol: a
});
});
tp.push(typ);
if (!isArray) {
tp.push(t.tsUndefinedKeyword());
}
ast = t.tsUnionType(tp);
}
else if (isSDKTypeCastable) {
const tp = symbols.map(a => t.tsTypeReference(t.identifier(exports.SymbolNames.SDKType(a.readAs))));
symbols.forEach(a => {
context.addImportDerivative({
type: 'SDKType',
symbol: a
});
});
tp.push(typ);
if (!isArray) {
tp.push(t.tsUndefinedKeyword());
}
ast = t.tsUnionType(tp);
}
else if (isNonArrayNullableType) {
// regular types!
ast = t.tsUnionType([
typ,
t.tsUndefinedKeyword()
]);
}
else {
ast = typ;
}
return { ast, isTypeCastableAnyType: isTypeCastable };
};
exports.getFieldTypeReference = getFieldTypeReference;
const getFieldAminoTypeReference = (context, field) => {
let ast = null;
let typ = null;
if (proto_1.SCALAR_TYPES.includes(field.type)) {
// return on scalar
typ = (0, exports.getTSTypeForAmino)(context, field);
return typ;
}
else if (proto_1.GOOGLE_TYPES.includes(field.type)) {
typ = (0, exports.getTSTypeFromGoogleType)(context, field.type, 'Amino');
}
else {
const propName = (0, utils_1.getProtoFieldTypeName)(context, field);
// enums don't need suffixes, etc.
const MsgName = field.parsedType?.type === 'Enum' ? propName : exports.SymbolNames.Amino(propName);
typ = t.tsTypeReference(t.identifier(MsgName));
}
if (field.parsedType?.type === 'Type') {
const typeName = field.isNestedMsg ? field.importedName : field.parsedType.name;
context.addImportDerivative({
type: 'Amino',
symbol: {
ref: context.ref.filename,
readAs: typeName,
source: field.import,
symbolName: typeName,
type: 'import'
},
});
}
if (field.parsedType?.type === 'Type' &&
field.rule !== 'repeated' &&
context.pluginValue('prototypes.allowUndefinedTypes')) {
// NOTE: unfortunately bc of defaults...
ast = t.tsUnionType([
typ,
t.tsUndefinedKeyword()
]);
}
else {
ast = typ;
}
return ast;
};
exports.getFieldAminoTypeReference = getFieldAminoTypeReference;
const getTSType = (context, type) => {
switch (type) {
case 'string':
return t.tsStringKeyword();
case 'double':
case 'float':
case 'int32':
case 'uint32':
case 'sint32':
case 'fixed32':
case 'sfixed32':
return t.tsNumberKeyword();
case 'int64':
case 'uint64':
case 'sint64':
case 'fixed64':
case 'sfixed64':
utils_1.TypeLong.addUtil(context);
return t.tsTypeReference(utils_1.TypeLong.getPropIdentifier(context));
case 'bytes':
return t.tsTypeReference(t.identifier('Uint8Array'));
case 'bool':
return t.tsBooleanKeyword();
default:
throw new Error('getTSType() type not found');
}
;
};
exports.getTSType = getTSType;
const getTSAminoType = (context, type, options) => {
switch (type) {
case 'string':
return t.tsStringKeyword();
case 'double':
case 'float':
case 'int32':
case 'uint32':
case 'sint32':
case 'fixed32':
case 'sfixed32':
return t.tsNumberKeyword();
case 'int64':
case 'uint64':
case 'sint64':
case 'fixed64':
case 'sfixed64':
return t.tsStringKeyword();
case 'bytes':
// (gogoproto.customname) = "WASMByteCode",
if (options?.["(gogoproto.customname)"] === "WASMByteCode") {
return t.tsStringKeyword();
}
return t.tsTypeReference(t.identifier('Uint8Array'));
case 'bool':
return t.tsBooleanKeyword();
default:
throw new Error('getTSType() type not found');
}
;
};
exports.getTSAminoType = getTSAminoType;
const getTSTypeFromGoogleType = (context, type, options = 'Msg') => {
const identifier = (str) => {
return t.identifier(exports.SymbolNames[options](str));
};
switch (type) {
case 'google.protobuf.Timestamp':
if (options === 'Amino' || options === 'AminoMsg') {
return t.tsStringKeyword();
}
switch (context.pluginValue('prototypes.typingsFormat.timestamp')) {
case 'timestamp':
return t.tsTypeReference(identifier('Timestamp'));
case 'date':
default:
return t.tsTypeReference(t.identifier('Date'));
}
case 'google.protobuf.Duration':
switch (context.pluginValue('prototypes.typingsFormat.duration')) {
case 'duration':
return t.tsTypeReference(identifier('Duration'));
case 'string':
default:
return t.tsStringKeyword();
}
case 'google.protobuf.Any':
return t.tsTypeReference(identifier('Any'));
default:
throw new Error('getTSTypeFromGoogleType() type not found');
}
;
};
exports.getTSTypeFromGoogleType = getTSTypeFromGoogleType;
const getTSTypeForAmino = (context, field) => {
switch (field.type) {
case 'bytes':
if (field.options?.["(gogoproto.casttype)"] === "RawContractMessage") {
return t.tsAnyKeyword();
}
else {
return t.tsStringKeyword();
}
default:
return (0, exports.getTSAminoType)(context, field.type);
}
;
};
exports.getTSTypeForAmino = getTSTypeForAmino;
const getTSTypeForProto = (context, field) => {
return (0, exports.getTSType)(context, field.type);
};
exports.getTSTypeForProto = getTSTypeForProto;
const getDefaultTSTypeFromProtoType = (context, field, isOneOf, useNullForOptionals = false) => {
const isOptional = (0, proto_1.getFieldOptionalityForDefaults)(context, field, isOneOf);
const setDefaultCustomTypesToUndefined = context.pluginValue('prototypes.typingsFormat.setDefaultCustomTypesToUndefined');
if (field.rule === 'repeated') {
return t.arrayExpression([]);
}
if (field.keyType) {
return t.objectExpression([]);
}
if (isOptional) {
return useNullForOptionals ? t.nullLiteral() : t.identifier('undefined');
}
if (field.parsedType?.type === 'Enum') {
const enumDefault = getPreferredEnumDefault(context, field);
return t.numericLiteral(enumDefault);
}
switch (field.type) {
case 'string':
return t.stringLiteral('');
case 'double':
case 'float':
case 'int32':
case 'uint32':
case 'sint32':
case 'fixed32':
case 'sfixed32':
return t.numericLiteral(0);
case 'uint64':
utils_1.TypeLong.addUtil(context);
return utils_1.TypeLong.getUZero(context);
case 'int64':
case 'sint64':
case 'fixed64':
case 'sfixed64':
utils_1.TypeLong.addUtil(context);
return utils_1.TypeLong.getZero(context);
case 'bytes':
return t.newExpression(t.identifier('Uint8Array'), []);
case 'bool':
return t.booleanLiteral(false);
// OTHER TYPES
case 'google.protobuf.Timestamp':
if (setDefaultCustomTypesToUndefined) {
return t.identifier('undefined');
}
else {
const timestampType = context.pluginValue('prototypes.typingsFormat.timestamp');
switch (timestampType) {
case 'timestamp':
return t.callExpression(t.memberExpression(t.identifier('Timestamp'), t.identifier('fromPartial')), [t.objectExpression([])]);
case 'date':
return t.newExpression(t.identifier('Date'), []);
default:
return t.identifier('undefined');
}
}
// TODO: add cases for this later on
case 'google.protobuf.Duration':
if (setDefaultCustomTypesToUndefined) {
return t.identifier('undefined');
}
else {
return getDefaultTSTypeFromProtoTypeDefault(context, field);
}
case 'google.protobuf.Any':
if (setDefaultCustomTypesToUndefined) {
return t.identifier('undefined');
}
else {
return getDefaultTSTypeFromProtoTypeDefault(context, field);
}
case 'cosmos.base.v1beta1.Coin':
if (setDefaultCustomTypesToUndefined) {
return t.identifier('undefined');
}
else {
return getDefaultTSTypeFromProtoTypeDefault(context, field);
}
case 'cosmos.base.v1beta1.Coins':
return t.arrayExpression([]);
default:
if (!field.type) {
console.warn('Undefined! Can\'t get field of type:', field);
return t.identifier('undefined');
}
else {
return getDefaultTSTypeFromProtoTypeDefault(context, field);
}
}
;
};
exports.getDefaultTSTypeFromProtoType = getDefaultTSTypeFromProtoType;
const getDefaultTSTypeFromAminoTypeDefault = (context, field) => {
const typeName = (0, utils_1.getProtoFieldTypeName)(context, field);
return t.callExpression(t.memberExpression(t.identifier(typeName), t.identifier("toAmino")), [
t.callExpression(t.memberExpression(t.identifier((0, utils_1.getProtoFieldTypeName)(context, field)), t.identifier("fromPartial")), [t.objectExpression([])]),
]);
};
exports.getDefaultTSTypeFromAminoTypeDefault = getDefaultTSTypeFromAminoTypeDefault;
function getDefaultTSTypeFromProtoTypeDefault(context, field) {
return t.callExpression(t.memberExpression(t.identifier((0, utils_1.getProtoFieldTypeName)(context, field)), t.identifier('fromPartial')), [t.objectExpression([])]);
}
function getPreferredEnumDefault(context, field) {
const autoFixUndefinedEnumDefault = context.pluginValue('prototypes.typingsFormat.autoFixUndefinedEnumDefault');
if (autoFixUndefinedEnumDefault) {
const typeName = (0, utils_1.getProtoFieldTypeName)(context, field);
return context.getDefaultOrExistingSmallestEnumValue(getPackage(field), typeName);
}
else {
if (context.ref.proto?.syntax === 'proto2') {
return types_1.ENUM_PROTO2_DEFAULT;
}
else {
return types_1.ENUM_PROTO3_DEFAULT;
}
}
}
function getPackage(field) {
const pkgs = field.scope?.flat();
return pkgs ? pkgs[0] : '';
}