@cosmology/ast
Version:
Cosmos TypeScript AST generation
934 lines (889 loc) • 51.7 kB
text/typescript
import { GenericParseContext } from '../../../../encoding';
import { ProtoService, ProtoServiceMethod } from '@cosmology/types';
import { arrowFunctionExpression, classDeclaration, classMethod, classProperty, commentBlock, identifier, tsMethodSignature } from '../../../../utils';
import { camel } from '@cosmology/utils';
import { processRpcComment, returnReponseType, optionalBool } from '../utils/rpc';
import { metadata, bindThis, makeComment, getRpcClassName } from './utils'
import * as t from '@babel/types'
const gRPCWebMethodDefinition = (
name: string,
svc: ProtoServiceMethod,
trailingComments?: t.CommentBlock[],
leadingComments?: t.CommentBlock[]
) => {
const requestType = svc.requestType;
const responseType = svc.responseType;
const fieldNames = Object.keys(svc.fields ?? {})
const hasParams = fieldNames.length > 0;
const optional = optionalBool(hasParams, fieldNames);
const methodArgs: t.Identifier = identifier(
'request',
t.tsTypeAnnotation(
t.tsTypeReference(
t.identifier('DeepPartial'),
t.tsTypeParameterInstantiation(
[
t.tsTypeReference(
t.identifier(requestType)
)
]
)
)
),
optional
);
const metadataArgs: t.Identifier = metadata
return tsMethodSignature(
t.identifier(name),
null,
[
methodArgs,
metadataArgs
],
returnReponseType(responseType),
trailingComments,
leadingComments
);
}
export const createGrpcWebMsgInterface = (
context: GenericParseContext,
service: ProtoService
) => {
const camelRpcMethods = context.pluginValue('rpcClients.camelCase');
const keys = Object.keys(service.methods ?? {});
const methods = keys
.map((key) => {
const method = service.methods[key];
const name = camelRpcMethods ? camel(key) : key;
const leadingComments = method.comment ? [commentBlock(processRpcComment(method))] : [];
let trailingComments = [];
return gRPCWebMethodDefinition(
name,
method,
trailingComments,
leadingComments
)
});
const obj = t.exportNamedDeclaration(
t.tsInterfaceDeclaration(
t.identifier(service.name),
null,
[],
t.tsInterfaceBody(
[
...methods
]
)
)
);
if (service.comment) {
obj.leadingComments = [commentBlock(`* ${service.comment} `)];
}
return obj;
};
const rpcClassConstructor = (
context: GenericParseContext,
methods: string[]
) => {
let bound = [];
if (!context.pluginValue('classesUseArrowFunctions')) {
bound = methods.map(method => bindThis(method));
}
return classMethod(
'constructor',
t.identifier('constructor'),
[
identifier(
'rpc',
t.tsTypeAnnotation(
t.tsTypeReference(
t.identifier('Rpc')
)
)
)
],
t.blockStatement([
t.expressionStatement(
t.assignmentExpression(
'=',
t.memberExpression(
t.thisExpression(),
t.identifier('rpc')
),
t.identifier('rpc')
)
),
/// methods
...bound
])
);
};
const rpcClassMethod = (
context: GenericParseContext,
name: string,
msg: string,
svc: ProtoServiceMethod,
packageImport: string
) => {
let partialName = 'DeepPartial';
let optional = false;
const requestType = svc.requestType;
const responseType = svc.responseType;
const comment = svc.comment ?? svc.name;
let methodArgs: t.Identifier | t.AssignmentPattern = identifier(
'request',
t.tsTypeAnnotation(
t.tsTypeReference(
t.identifier(partialName),
t.tsTypeParameterInstantiation(
[
t.tsTypeReference(
t.identifier(requestType)
)
]
)
)
)
);
const metadataArgs: t.Identifier = metadata
const fieldNames = Object.keys(svc.fields ?? {})
const hasParams = fieldNames.length > 0;
// if no params, then let's default to empty object for cleaner API
if (!hasParams) {
methodArgs = t.assignmentPattern(
methodArgs,
t.objectExpression([])
)
} else if (hasParams && fieldNames.length === 1 && fieldNames.includes('pagination')) {
const paginationDefaultFromPartial = context.pluginValue('prototypes.paginationDefaultFromPartial');
// if only argument "required" is pagination
// also default to empty
methodArgs = t.assignmentPattern(
methodArgs,
t.objectExpression([
t.objectProperty(
t.identifier('pagination'),
paginationDefaultFromPartial ? t.callExpression(
t.memberExpression(t.identifier("PageRequest"), t.identifier("fromPartial")),
[t.objectExpression([])]
) :
t.identifier('undefined'),
false,
false
)
])
)
}
const body = t.blockStatement([
t.returnStatement(
t.callExpression(
t.memberExpression(
t.memberExpression(
t.thisExpression(),
t.identifier('rpc')
),
t.identifier('unary')
),
[
//No Desc field so we need to modify it
t.identifier(requestType.concat('Desc')),
t.callExpression(
t.memberExpression(
t.identifier(requestType),
t.identifier('fromPartial')
),
[
t.identifier('request')
]
),
t.identifier('metadata')
]
)
)
]);
if (context.pluginValue('classesUseArrowFunctions')) {
return classProperty(
t.identifier(name),
arrowFunctionExpression(
[methodArgs],
body,
returnReponseType(responseType),
true
),
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
makeComment(comment) as t.CommentLine[],
);
}
return classMethod(
'method',
t.identifier(name),
[
methodArgs,
metadataArgs
],
body,
returnReponseType(responseType)
);
};
export const createGrpcWebMsgClass = (
context: GenericParseContext,
service: ProtoService
) => {
context.addUtil('UnaryMethodDefinitionish');
context.addUtil('_m0');
context.addUtil('DeepPartial');
context.addUtil('grpc');
const camelRpcMethods = context.pluginValue('rpcClients.camelCase');
const name = getRpcClassName(service);
const implementsName = service.name;
const methodNames = Object.keys(service.methods ?? {})
.map(key => {
return camelRpcMethods ? camel(key) : key
});
const methods = Object.keys(service.methods ?? {})
.map(key => {
const method = service.methods[key];
const name = camelRpcMethods ? camel(key) : key;
return rpcClassMethod(
context,
name,
key,
method,
context.ref.proto.package + '.' + service.name
)
});
return t.exportNamedDeclaration(
classDeclaration(
t.identifier(name),
null,
t.classBody([
classProperty(
t.identifier('rpc'),
null,
t.tsTypeAnnotation(
t.tsTypeReference(
t.identifier('Rpc')
)
),
null,
false,
false,
true,
'private'
),
// CONSTRUCTOR
rpcClassConstructor(context, methodNames),
// METHODS
...methods
]),
null,
[
t.tsExpressionWithTypeArguments(
t.identifier(implementsName)
)
]
)
);
};
const rpcMethodDef = (
svc: ProtoServiceMethod,
) => {
const requestType = svc.requestType;
const responseType = svc.responseType;
const methodName = svc.name;
return {
methodName,
requestType,
responseType
}
}
//return array of method Desc
export const getMethodDesc = (context: GenericParseContext, service: ProtoService) => {
//get all method that need Desc
const methods = Object.keys(service.methods ?? {})
.map(key => {
return rpcMethodDef(
service.methods[key]
)
});
const methodsDesc = []
//check if service name is 'service' if it is then ommit it because proto method doesn't contain service prefix in it methods
let service_name = service.name
if (service_name == 'Service') {
service_name = ''
}
for (let i = 0; i < methods.length; i++) {
const methodName = methods[i].methodName
const requestType = methods[i].requestType
const responseType = methods[i].responseType
const methodDesc = t.exportNamedDeclaration(
t.variableDeclaration(
'const',
[
t.variableDeclarator(
t.identifier(service_name + methodName + 'Desc: UnaryMethodDefinitionish'),
t.objectExpression(
[
t.objectProperty(
t.identifier('methodName'),
t.stringLiteral(methodName)
),
t.objectProperty(
t.identifier('service'),
t.identifier(service.name + 'Desc')
),
t.objectProperty(
t.identifier('requestStream'),
t.booleanLiteral(false)
),
t.objectProperty(
t.identifier('responseStream'),
t.booleanLiteral(false)
),
t.objectProperty(
t.identifier('requestType'),
t.tsAsExpression(
t.objectExpression(
[
t.objectMethod(
'method',
t.identifier('serializeBinary'),
[],
t.blockStatement(
[
t.returnStatement(
t.callExpression(
t.memberExpression(
t.callExpression(
t.memberExpression(
t.identifier(requestType),
t.identifier('encode')
),
[
t.thisExpression()
]
),
t.identifier('finish')
),
[]
)
)
]
)
)
]
),
t.tsAnyKeyword()
)
),
t.objectProperty(
t.identifier('responseType'),
t.tsAsExpression(
t.objectExpression(
[
t.objectMethod(
'method',
t.identifier('deserializeBinary'),
[
identifier('data', t.tsTypeAnnotation(
t.tsTypeReference(
t.identifier('Uint8Array')
)
))
],
t.blockStatement(
[
t.returnStatement(
t.objectExpression(
[
t.spreadElement(
t.callExpression(
t.memberExpression(
t.identifier(responseType),
t.identifier('decode')
),
[t.identifier('data')]
)
),
t.objectMethod(
'method',
t.identifier('toObject'),
[],
t.blockStatement(
[
t.returnStatement(
t.thisExpression()
)
]
)
)
]
)
)
]
)
)
]
),
t.tsAnyKeyword()
)
),
]
)
)
]
)
)
methodsDesc.push(methodDesc)
}
return methodsDesc
}
export const GetDesc = (context: GenericParseContext, service: ProtoService) => {
const descName = service.name + 'Desc'
const serviceName = context.ref.proto.package + '.' + service.name
return t.exportNamedDeclaration(
t.variableDeclaration(
'const',
[
t.variableDeclarator(
t.identifier(descName),
t.objectExpression(
[
t.objectProperty(
t.identifier('serviceName'),
t.stringLiteral(serviceName)
)
]
)
)
]
)
)
}
export const grpcWebRpcInterface = () => {
return t.exportNamedDeclaration(
t.tsInterfaceDeclaration(
t.identifier('Rpc'),
null,
[],
t.tsInterfaceBody(
[
t.tsMethodSignature(
t.identifier('unary'),
t.tsTypeParameterDeclaration(
[
t.tsTypeParameter(
t.tsTypeReference(
t.identifier('UnaryMethodDefinitionish')
),
null,
'T'
)
]
),
[
identifier('methodDesc',
t.tsTypeAnnotation(
t.tsTypeReference(
t.identifier('T')
)
)),
identifier('request',
t.tsTypeAnnotation(
t.tsAnyKeyword()
)),
identifier('metadata',
t.tsTypeAnnotation(
t.tSUnionType(
[
t.tsTypeReference(
t.tsQualifiedName(
t.identifier('grpc'),
t.identifier('Metadata')
)
),
t.tsUndefinedKeyword()
]
)
)),
],
t.tsTypeAnnotation(
t.tSTypeReference(
t.identifier('Promise'),
t.tsTypeParameterInstantiation(
[
t.tsAnyKeyword()
]
)
)
)
)
]
)
))
}
//you might not want to look at this
export const getGrpcWebImpl = (context: GenericParseContext) => {
context.addUtil('BrowserHeaders');
return t.exportNamedDeclaration(
t.classDeclaration(
t.identifier('GrpcWebImpl'),
null,
t.classBody(
[
t.classProperty(
t.identifier('host'),
null,
t.tsTypeAnnotation(
t.tsStringKeyword()
)
),
t.classProperty(
t.identifier('options'),
null,
t.tsTypeAnnotation(
t.tsTypeLiteral(
[
t.tsPropertySignature(
t.identifier('transport?'),
t.tsTypeAnnotation(
t.tsTypeReference(
t.tsQualifiedName(
t.identifier('grpc'),
t.identifier('TransportFactory')
)
)
)
),
t.tsPropertySignature(
t.identifier('debug?'),
t.tsTypeAnnotation(
t.tsBooleanKeyword()
)
),
t.tsPropertySignature(
t.identifier('metadata?'),
t.tsTypeAnnotation(
t.tsTypeReference(
t.tsQualifiedName(
t.identifier('grpc'),
t.identifier('Metadata')
)
)
)
)
]
)
)
),
t.classMethod(
"constructor",
t.identifier('constructor'),
[
identifier("host",
t.tsTypeAnnotation(
t.tsStringKeyword()
)),
identifier("options",
t.tsTypeAnnotation(
t.tsTypeLiteral(
[
t.tsPropertySignature(
t.identifier('transport?'),
t.tsTypeAnnotation(
t.tsTypeReference(
t.tsQualifiedName(
t.identifier('grpc'),
t.identifier('TransportFactory')
)
)
)
),
t.tsPropertySignature(
t.identifier('debug?'),
t.tsTypeAnnotation(
t.tsBooleanKeyword()
)
),
t.tsPropertySignature(
t.identifier('metadata?'),
t.tsTypeAnnotation(
t.tsTypeReference(
t.tsQualifiedName(
t.identifier('grpc'),
t.identifier('Metadata')
)
)
)
)
]
)
))
],
t.blockStatement(
[
t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(
t.thisExpression(),
t.identifier('host')
),
t.identifier('host')
)
),
t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(
t.thisExpression(),
t.identifier('options')
),
t.identifier('options')
)
),
]
),
),
t.classMethod(
"method",
t.identifier('unary<T extends UnaryMethodDefinitionish>'),
[
identifier('methodDesc', t.tsTypeAnnotation(t.tsTypeReference(t.identifier('T')))),
identifier('_request', t.tsTypeAnnotation(t.tsAnyKeyword())),
identifier('metadata', t.tsTypeAnnotation(t.tsUnionType([t.tsTypeReference(t.tsQualifiedName(t.identifier('grpc'), t.identifier('Metadata'))), t.tsUndefinedKeyword()])))
],
t.blockStatement(
[
t.variableDeclaration(
"const",
[
t.variableDeclarator(
t.identifier('request'),
t.objectExpression([
t.spreadElement(t.identifier('_request')),
t.spreadElement(t.memberExpression(t.identifier('methodDesc'), t.identifier('requestType')))
])
)
]
),
t.variableDeclaration(
"const",
[
t.variableDeclarator(
t.identifier('maybeCombinedMetadata'),
t.conditionalExpression(
t.logicalExpression(
"&&",
t.identifier('metadata'),
t.memberExpression(
t.memberExpression(
t.thisExpression(),
t.identifier('options')
),
t.identifier('metadata')
)
),
t.newExpression(
t.identifier('BrowserHeaders'),
[
t.objectExpression(
[
t.spreadElement(
t.optionalMemberExpression(
t.optionalMemberExpression(
t.memberExpression(
t.thisExpression(),
t.identifier('options'),
false
),
t.identifier('metadata'),
false,
true
),
t.identifier('headersMap'),
false,
false
)
),
t.spreadElement(
t.optionalMemberExpression(
t.identifier('metadata'),
t.identifier('headersMap'),
false,
true
)
)
]
)
]
),
t.logicalExpression(
"||",
t.identifier('metadata'),
t.memberExpression(
t.memberExpression(
t.thisExpression(),
t.identifier('options')
),
t.identifier('metadata')
)
)
)
)
]
),
t.returnStatement(
t.newExpression(
t.identifier('Promise'),
[
t.arrowFunctionExpression(
[
t.identifier('resolve'),
t.identifier('reject')
],
t.blockStatement(
[
t.expressionStatement(
t.callExpression(
t.memberExpression(
t.identifier('grpc'),
t.identifier('unary')
),
[
t.identifier('methodDesc'),
t.objectExpression(
[
t.objectProperty(
t.identifier('request'),
t.identifier('request'),
false,
true
),
t.objectProperty(
t.identifier('host'),
t.memberExpression(
t.thisExpression(),
t.identifier('host')
)
),
t.objectProperty(
t.identifier('metadata'),
t.identifier('maybeCombinedMetadata')
),
t.objectProperty(
t.identifier('transport'),
t.memberExpression(
t.memberExpression(
t.thisExpression(),
t.identifier('options')
),
t.identifier('transport')
)
),
t.objectProperty(
t.identifier('debug'),
t.memberExpression(
t.memberExpression(
t.thisExpression(),
t.identifier('options')
),
t.identifier('debug')
)
),
t.objectProperty(
t.identifier('onEnd'),
t.functionExpression(
null,
[
t.identifier('response')
],
t.blockStatement(
[
t.ifStatement(
t.binaryExpression(
"===",
t.memberExpression(
t.identifier('response'),
t.identifier('status')
),
t.memberExpression(
t.memberExpression(
t.identifier('grpc'),
t.identifier('Code')
),
t.identifier('OK'),
)
),
t.blockStatement(
[
t.expressionStatement(
t.callExpression(
t.identifier('resolve'),
[
t.memberExpression(
t.identifier('response'),
t.identifier('message')
)
]
)
)
]
),
t.blockStatement(
[
t.variableDeclaration(
"const",
[
t.variableDeclarator(
t.identifier('err'),
t.tsAsExpression(
t.newExpression(
t.identifier('Error'),
[
t.memberExpression(
t.identifier('response'),
t.identifier('statusMessage')
)
]
),
t.tsAnyKeyword()
)
)
]
),
t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(
t.identifier('err'),
t.identifier('code')
),
t.memberExpression(
t.identifier('response'),
t.identifier('status')
)
)
),
t.expressionStatement(
t.assignmentExpression(
"=",
t.memberExpression(
t.identifier('err'),
t.identifier('metadata')
),
t.memberExpression(
t.identifier('response'),
t.identifier('trailers')
)
)
),
t.expressionStatement(
t.callExpression(
t.identifier('reject'),
[