@cosmology/ast
Version:
Cosmos TypeScript AST generation
140 lines (139 loc) • 5.47 kB
JavaScript
import * as t from '@babel/types';
import { identifier, makeCommentLineWithBlocks, objectProperty, newExpression } from '../../utils';
import { isRefIncluded } from '@cosmology/utils';
import { camel } from '@cosmology/utils';
import { pascal } from 'case';
const makeQueryStoreName = (name) => {
return `Query${pascal(name)}Store`;
};
/**
* Entry for building stores.
* @param {Object=} context - context of generating the file
* @param {Object=} service - method details
*/
export const createMobxQueryStores = (context, service) => {
const isIncluded = context.pluginValue('mobx.enabled') &&
isRefIncluded(context.ref, context.pluginValue('mobx.include'));
if (isIncluded) {
// before this, make sure:
// 1. refactor adding getQueryService part into helpers.
// 2. add new query store class to helpers.
// build whole ast, don't forget to add utils.
return buildRpcStores(context, service);
}
return null;
};
/**
* Create an AST to generate creating store functions.
* eg:
* export const createRpcQueryMobxStores = (rpc: ProtobufRpcClient | undefined) => {
* const queryService = getQueryService(rpc);
*
* class QueryCertificatesStore {
* ...
* }
*
* return {
* QueryCertificatesStore
* };
* };
* @param {Object=} context - context of generating the file
* @param {Object=} service - method details
* @returns {ParseResult} created AST
*/
export const buildRpcStores = (context, service) => {
// add imports
context.addUtil('ProtobufRpcClient');
//TODO: add util for getQueryService
const isCamelRpcMethods = context.pluginValue('rpcClients.camelCase');
const storeNames = [];
const stores = Object.keys(service.methods ?? {}).map((key) => {
const method = service.methods[key];
const name = isCamelRpcMethods ? camel(key) : key;
storeNames.push({
name,
comment: method.comment
});
return buildStore(context, name, method);
});
return t.exportNamedDeclaration(t.variableDeclaration('const', [
t.variableDeclarator(t.identifier('createRpcQueryMobxStores'), t.arrowFunctionExpression([
identifier('rpc', t.tsTypeAnnotation(t.tsUnionType([
t.tsTypeReference(t.identifier('ProtobufRpcClient')),
t.tsUndefinedKeyword()
])))
],
// body
t.blockStatement([
// query service
t.variableDeclaration('const', [
t.variableDeclarator(t.identifier('queryService'), t.callExpression(t.identifier('getQueryService'), [
t.identifier('rpc')
]))
]),
...stores,
// return the methods...
t.returnStatement(t.objectExpression(storeNames.map(({ name, comment }) => {
const id = t.identifier(makeQueryStoreName(name));
return objectProperty(id, id, false, true, null, makeCommentLineWithBlocks(comment));
})))
])
// end body
))
]));
};
/**
* Create an AST of inherited store.
* eg:
* class QueryCertificatesStore {
* store = new QueryStore<QueryCertificatesRequest, QueryCertificatesResponse>(queryService?.certificates);
* certificates(request: QueryCertificatesRequest) {
* return this.store.getData(request);
* }
* }
* @param {Object=} context - context of generating the file
* @param {string} name - name of the store
* @param {Object=} serviceMethod - method details
* @returns {ParseResult} created AST
*/
const buildStore = (context, name, serviceMethod) => {
//add util for QueryStore
context.addUtil('QueryStore');
//add util for MobxResponse
context.addUtil('MobxResponse');
//add util for makeObservable
context.addUtil('makeObservable');
//add util for override
context.addUtil('override');
const requestType = serviceMethod.requestType;
const responseType = serviceMethod.responseType;
const fieldNames = Object.keys(serviceMethod.fields ?? {});
const hasParams = fieldNames.length > 0;
let isOptional = false;
// // if no params, then let's default to empty object for cleaner API
if (!hasParams) {
isOptional = true;
}
else if (hasParams &&
fieldNames.length === 1 &&
fieldNames.includes('pagination')) {
// if only argument "required" is pagination
// also default to empty
isOptional = true;
}
const storeClassName = makeQueryStoreName(name);
const storeQueryClass = t.classDeclaration(t.identifier(storeClassName), null, t.classBody([
t.classProperty(t.identifier('store'), newExpression(t.identifier('QueryStore'), [
t.optionalMemberExpression(t.identifier('queryService'), t.identifier(name), false, true)
], t.tsTypeParameterInstantiation([
t.tsTypeReference(t.identifier(requestType)),
t.tsTypeReference(t.identifier(responseType))
]))),
t.classMethod('method', t.identifier(name), [
identifier('request', t.tsTypeAnnotation(t.tsTypeReference(t.identifier(requestType))))
], t.blockStatement([
t.returnStatement(t.callExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier('store')), t.identifier('getData')), [t.identifier('request')]))
], []))
]));
return storeQueryClass;
};