import { SetMetadata, Type } from '@nestjs/common'; import { isFunction, isObject } from '@nestjs/common/utils/shared.utils'; import { ReturnTypeFunc, FieldMiddleware, Complexity, GqlTypeReference, TypeMetadataStorage, BaseTypeOptions, } from '@nestjs/graphql'; import { FIELD_RESOLVER_MIDDLEWARE_METADATA } from '@nestjs/graphql/dist/graphql.constants'; import { LazyMetadataStorage } from '@nestjs/graphql/dist/schema-builder/storages/lazy-metadata.storage'; import { TypeOptions } from '@nestjs/graphql/dist/interfaces/type-options.interface'; import { reflectTypeFromMetadata } from '@nestjs/graphql/dist/utils/reflection.utilts'; import { LOADER_NAME_METADATA, LOADER_PROPERTY_METADATA } from '../constants'; import { LoaderMiddleware } from '../interfaces'; export interface ResolveLoaderOptions extends BaseTypeOptions { name?: string; description?: string; deprecationReason?: string; complexity?: Complexity; middleware?: LoaderMiddleware[]; opts?: { cache?: boolean; }; } export function ResolveLoader( typeFunc?: ReturnTypeFunc, options?: ResolveLoaderOptions, ): MethodDecorator; export function ResolveLoader( propertyName?: string, typeFunc?: ReturnTypeFunc, options?: ResolveLoaderOptions, ): MethodDecorator; export function ResolveLoader( propertyNameOrFunc?: string | ReturnTypeFunc, typeFuncOrOptions?: ReturnTypeFunc | ResolveLoaderOptions, resolveFieldOptions?: ResolveLoaderOptions, ): MethodDecorator { return ( target: Function | Record<string, any>, key: any, descriptor?: any, ) => { // eslint-disable-next-line prefer-const let [propertyName, typeFunc, options] = isFunction(propertyNameOrFunc) ? typeFuncOrOptions && typeFuncOrOptions.name ? [typeFuncOrOptions.name, propertyNameOrFunc, typeFuncOrOptions] : [undefined, propertyNameOrFunc, typeFuncOrOptions] : [propertyNameOrFunc, typeFuncOrOptions, resolveFieldOptions]; SetMetadata(LOADER_NAME_METADATA, propertyName)(target, key, descriptor); SetMetadata(LOADER_PROPERTY_METADATA, true)(target, key, descriptor); SetMetadata( FIELD_RESOLVER_MIDDLEWARE_METADATA, (options as ResolveLoaderOptions)?.middleware, )(target, key, descriptor); options = isObject(options) ? { name: propertyName as string, ...options, } : propertyName ? { name: propertyName as string } : {}; LazyMetadataStorage.store( target.constructor as Type<unknown>, function resolveLoader() { let typeOptions: TypeOptions, typeFn: (type?: any) => GqlTypeReference; try { const implicitTypeMetadata = reflectTypeFromMetadata({ metadataKey: 'design:returntype', prototype: target, propertyKey: key, explicitTypeFn: typeFunc as ReturnTypeFunc, typeOptions: options as any, }); typeOptions = implicitTypeMetadata.options; typeFn = implicitTypeMetadata.typeFn; } catch {} TypeMetadataStorage.addResolverPropertyMetadata({ kind: 'external', methodName: key, schemaName: options.name || key, target: target.constructor, typeFn, typeOptions, description: (options as ResolveLoaderOptions).description, deprecationReason: (options as ResolveLoaderOptions) .deprecationReason, complexity: (options as ResolveLoaderOptions).complexity, }); }, ); }; }