diff --git a/packages/ast/src/encoding/proto/interface/proto.ts b/packages/ast/src/encoding/proto/interface/proto.ts index 4fa2ae49c..72fad28a6 100644 --- a/packages/ast/src/encoding/proto/interface/proto.ts +++ b/packages/ast/src/encoding/proto/interface/proto.ts @@ -213,6 +213,49 @@ export const createProtoType = ( return declaration; }; +export const cloneAndWrapFieldsWithComputedRef = ( + originalDeclaration: t.ExportNamedDeclaration + ): t.ExportNamedDeclaration => { + // Extract the interface declaration + const interfaceDeclaration = originalDeclaration.declaration as t.TSInterfaceDeclaration; + const originalFields = (interfaceDeclaration.body as t.TSInterfaceBody).body; + + // Clone and modify each field + const newFields = originalFields.map((field) => { + if (t.isTSPropertySignature(field) && field.typeAnnotation) { + // Get the original field type + const originalType = field.typeAnnotation.typeAnnotation; + + // Create the new type: ComputedRef + const computedRefType = t.tsTypeReference( + t.identifier('ComputedRef'), + t.tsTypeParameterInstantiation([originalType]) + ); + + // Clone the field and replace its type + return tsPropertySignature( + field.key, + t.tsTypeAnnotation(computedRefType), + field.optional + ); + } + // Keep the field unchanged if it's not a TSPropertySignature + return field; + }); + + const newInterfaceId = t.identifier(`Reactive${interfaceDeclaration.id.name}`); + // Create a new interface declaration + const newInterfaceDeclaration = t.tsInterfaceDeclaration( + newInterfaceId, + interfaceDeclaration.typeParameters, + interfaceDeclaration.extends, + t.tsInterfaceBody(newFields) + ); + + // Wrap it as a new ExportNamedDeclaration + return t.exportNamedDeclaration(newInterfaceDeclaration); +} + export const createProtoTypeType = ( context: ProtoParseContext, name: string, diff --git a/packages/ast/src/state/vue-query/vue-query.ts b/packages/ast/src/state/vue-query/vue-query.ts index 04aa578f4..3c77f8af3 100644 --- a/packages/ast/src/state/vue-query/vue-query.ts +++ b/packages/ast/src/state/vue-query/vue-query.ts @@ -28,7 +28,7 @@ const rpcHookMethod = ( } // add import - context.addUtil('useQuery'); + context.addUtil('useVueQuery'); return t.variableDeclaration('const', [ t.variableDeclarator( @@ -205,7 +205,7 @@ const rpcHookMethod = ( } -export const createVueRpcQueryHooks = ( +export const createRpcVueQueryHooks = ( context: GenericParseContext, service: ProtoService ) => { @@ -227,8 +227,6 @@ export const createVueRpcQueryHooks = ( ) }); - console.log('methods>>', methods.length) - const methodNames = Object.keys(service.methods ?? {}) .map(key => { const name = camelRpcMethods ? camel(key) : key; @@ -292,7 +290,83 @@ export const createVueRpcQueryHooks = ( ); }; +const rpcVueHookMethodInterface = ( + context: GenericParseContext, + name: string, + svc: ProtoServiceMethod +) => { + const requestType = svc.requestType; + const responseType = svc.responseType; + const fieldNames = Object.keys(svc.fields ?? {}) + const hasParams = fieldNames.length > 0; + let optional = false; + // if no params, then let's default to empty object for cleaner API + if (!hasParams) { + optional = true; + } else if (hasParams && fieldNames.length === 1 && fieldNames.includes('pagination')) { + // if only argument "required" is pagination + // also default to empty + optional = true; + } + + // import VueQueryParams in the generated file. + context.addUtil('VueQueryParams'); + + return t.exportNamedDeclaration(t.tsInterfaceDeclaration( + t.identifier(makeUseHookTypeName(name)), + t.tsTypeParameterDeclaration([ + t.tsTypeParameter(null, null, 'TData') + ]), + [ + t.tsExpressionWithTypeArguments( + t.identifier('VueQueryParams'), + t.tsTypeParameterInstantiation([ + t.tsTypeReference(t.identifier(responseType)), + t.tsTypeReference(t.identifier('TData')) + ]) + ) + ], + t.tsInterfaceBody([ + tsPropertySignature( + t.identifier('request'), + t.tsTypeAnnotation( + t.tsTypeReference( + t.identifier(requestType) + ) + ), + optional + ) + ]) + )); +} + +/** + * Create ASTs for all the methods of a proto service. + * @param {Object=} context - context of generating the file + * @param {Object=} service - service details + * @returns {ParseResult} created AST + */ +export const createRpcVueQueryHookInterfaces = ( + context: GenericParseContext, + service: ProtoService +) => { + + + const camelRpcMethods = context.pluginValue('rpcClients.camelCase'); + + const methods = Object.keys(service.methods ?? {}) + .map(key => { + const name = camelRpcMethods ? camel(key) : key; + const method = service.methods[key]; + return { + name, + method + }; + }); + + return methods.map(method => rpcVueHookMethodInterface(context, method.name, method.method)); +}; function astToCodeWithBabel(ast: any): string { const { code } = generate(ast, { retainLines: true }); @@ -300,12 +374,11 @@ function astToCodeWithBabel(ast: any): string { return code; } -export const createVueRpcQueryHookClientMap = ( +export const createRpcVueQueryHookClientMap = ( context: GenericParseContext, service: ProtoService ) => { const name = service.name + 'ClientImpl'; - console.log('hahah.') // const ast = createClientMap(name) // astToCodeWithBabel(ast[0]) diff --git a/packages/ast/types/encoding/proto/interface/proto.d.ts b/packages/ast/types/encoding/proto/interface/proto.d.ts index 974851b09..15c7729ba 100644 --- a/packages/ast/types/encoding/proto/interface/proto.d.ts +++ b/packages/ast/types/encoding/proto/interface/proto.d.ts @@ -3,6 +3,7 @@ import { ProtoType } from '@cosmology/types'; import { ProtoParseContext } from '../../context'; import { TelescopeBaseTypes } from '../../types'; export declare const createProtoType: (context: ProtoParseContext, name: string, proto: ProtoType, type?: TelescopeBaseTypes) => t.ExportNamedDeclaration; +export declare const cloneAndWrapFieldsWithComputedRef: (originalDeclaration: t.ExportNamedDeclaration) => t.ExportNamedDeclaration; export declare const createProtoTypeType: (context: ProtoParseContext, name: string, proto: ProtoType) => t.ExportNamedDeclaration; export declare const createProtoInterfaceEncodedType: (context: ProtoParseContext, name: string, proto: ProtoType) => t.ExportNamedDeclaration; export declare const createCreateProtoType: (context: ProtoParseContext, name: string, proto: ProtoType) => t.FunctionDeclaration; diff --git a/packages/ast/types/state/vue-query/vue-query.d.ts b/packages/ast/types/state/vue-query/vue-query.d.ts index 9e8381cbe..8c15b0f5b 100644 --- a/packages/ast/types/state/vue-query/vue-query.d.ts +++ b/packages/ast/types/state/vue-query/vue-query.d.ts @@ -1,8 +1,15 @@ import * as t from '@babel/types'; import { ProtoService } from '@cosmology/types'; import { GenericParseContext } from '../../encoding'; -export declare const createVueRpcQueryHooks: (context: GenericParseContext, service: ProtoService) => t.ExportNamedDeclaration; -export declare const createVueRpcQueryHookClientMap: (context: GenericParseContext, service: ProtoService) => { +export declare const createRpcVueQueryHooks: (context: GenericParseContext, service: ProtoService) => t.ExportNamedDeclaration; +/** + * Create ASTs for all the methods of a proto service. + * @param {Object=} context - context of generating the file + * @param {Object=} service - service details + * @returns {ParseResult} created AST + */ +export declare const createRpcVueQueryHookInterfaces: (context: GenericParseContext, service: ProtoService) => t.ExportNamedDeclaration[]; +export declare const createRpcVueQueryHookClientMap: (context: GenericParseContext, service: ProtoService) => { type: string; declarations: { type: string; diff --git a/packages/telescope/src/build.ts b/packages/telescope/src/build.ts index a695373bf..787edecca 100644 --- a/packages/telescope/src/build.ts +++ b/packages/telescope/src/build.ts @@ -10,6 +10,7 @@ import { createProtoEnumToJSON, createProtoEnumFromJSON, createProtoType, + cloneAndWrapFieldsWithComputedRef, createProtoInterfaceEncodedType, createProtoTypeType, createSDKType, @@ -79,7 +80,17 @@ export const buildBaseTypeScriptInterface = ( obj: any ) => { - context.body.push(createProtoType(context.proto, name, obj)); + const protoType = createProtoType(context.proto, name, obj) + context.body.push(protoType); + + if (context.options.vueQuery?.enabled) { + // create interfaces with fields of ComputedRef + // eg: interface QueryBalanceRequest { + // address: ComputedRef; + // denom: ComputedRef; + // } + context.body.push(cloneAndWrapFieldsWithComputedRef(protoType)) + } if (context.options.aminoEncoding?.enabled && !context.options.aminoEncoding?.useLegacyInlineEncoding || context.options.prototypes?.methods?.fromProto || context.options.prototypes?.methods?.toProto) { context.body.push(createProtoTypeType(context.proto, name, obj)); diff --git a/packages/telescope/src/generators/create-rpc-query-clients.ts b/packages/telescope/src/generators/create-rpc-query-clients.ts index 4171f378d..babac6a6d 100644 --- a/packages/telescope/src/generators/create-rpc-query-clients.ts +++ b/packages/telescope/src/generators/create-rpc-query-clients.ts @@ -5,10 +5,11 @@ import { createRpcClientClass, createRpcClientInterface, createRpcQueryHookInterfaces, + createRpcVueQueryHookInterfaces, createRpcQueryHookClientMap, - createVueRpcQueryHookClientMap, + createRpcVueQueryHookClientMap, createRpcQueryHooks, - createVueRpcQueryHooks, + createRpcVueQueryHooks, // grpc-gateway: createGRPCGatewayQueryClass, createGRPCGatewayWrapperClass, @@ -233,11 +234,12 @@ export const plugin = ( if (includeReactQueryHooks) { [].push.apply(asts, createRpcQueryHookInterfaces(ctx.generic, svc)); + } // enable getQueryService for plugins using it. if (includeReactQueryHooks || includeMobxHooks) { - // [].push.apply(asts, createRpcQueryHookClientMap(ctx.generic, svc)); + [].push.apply(asts, createRpcQueryHookClientMap(ctx.generic, svc)); } // see if current file has been vueQuery enabled and included @@ -245,9 +247,13 @@ export const plugin = ( c.ref, c.proto.pluginValue('vueQuery.include') ) - // enable vue useQueryService for plugins using it. + + if (includeVueQueryHooks) { + [].push.apply(asts, createRpcVueQueryHookInterfaces(ctx.generic, svc)); + } + if (includeVueQueryHooks) { - [].push.apply(asts, createVueRpcQueryHookClientMap(ctx.generic, svc)); + [].push.apply(asts, createRpcVueQueryHookClientMap(ctx.generic, svc)); } @@ -260,15 +266,17 @@ export const plugin = ( // export const createRpcQueryHooks = ... // TODO use the imports and make separate files if (includeReactQueryHooks) { - // asts.push(createRpcQueryHooks(ctx.generic, proto[svcKey])); - asts.push(createVueRpcQueryHooks(ctx.generic, proto[svcKey])); - + asts.push(createRpcQueryHooks(ctx.generic, proto[svcKey])); + // get all query methods const patterns = c.proto.pluginValue('reactQuery.instantExport.include.patterns'); - bundlerFile.instantExportedMethods = getQueryMethodNames(bundlerFile.package, Object.keys(proto[svcKey].methods ?? {}), patterns).map((key)=> proto[svcKey].methods[key]); + bundlerFile.instantExportedMethods = getQueryMethodNames(bundlerFile.package, Object.keys(proto[svcKey].methods ?? {}), patterns).map((key) => proto[svcKey].methods[key]); reactQueryBundlerFiles.push(bundlerFile); } + if (includeVueQueryHooks) { + asts.push(createRpcVueQueryHooks(ctx.generic, proto[svcKey])); + } // whether mobx plugin is enabled has been dealt with inside createMobxQueryStores const mobxQueryStoreAst = createMobxQueryStores( diff --git a/packages/telescope/src/generators/create-types.ts b/packages/telescope/src/generators/create-types.ts index 0dfbe7bd7..509805e5a 100644 --- a/packages/telescope/src/generators/create-types.ts +++ b/packages/telescope/src/generators/create-types.ts @@ -173,6 +173,17 @@ export const plugin = ( .concat(importStmts) ; + if (context.proto.pluginValue('vueQuery.enabled')) { + // import { ComputedRef } from 'vue' + const importVueStatement = t.importDeclaration( + [ + t.importSpecifier(t.identifier('ComputedRef'), t.identifier('ComputedRef')), // 指定导入的名称 + ], + t.stringLiteral('vue') + ); + prog.push(importVueStatement) + } + // package var if (context.proto.pluginValue('prototypes.includePackageVar')) { prog.push(t.exportNamedDeclaration(t.variableDeclaration('const', [ diff --git a/packages/telescope/src/utils/index.ts b/packages/telescope/src/utils/index.ts index 5abf1f8f6..f4acffe9f 100644 --- a/packages/telescope/src/utils/index.ts +++ b/packages/telescope/src/utils/index.ts @@ -71,10 +71,20 @@ export const UTILS: { [key: string]: UtilValue } = { toTimestamp: '__helpers__', toUtf8: '@cosmjs/encoding', useQuery: '@tanstack/react-query', + useVueQuery: { + type: 'import', + name: 'useQuery', + path: '@tanstack/vue-query' + }, useRpcEndpoint: '__react-query__', useRpcClient: '__react-query__', useTendermintClient: '__react-query__', ReactQueryParams: '__react-query__', + VueQueryParams: { + type: 'import', + name: 'VueQueryParams', + path: '../../../vue-query' + }, buildUseMutation:'__react-query__', UseQueryParams:'__react-query__', buildUseQuery:'__react-query__',