import { ParsedConfig , RawConfig , BaseVisitor , BaseVisitorConvertOptions } from './base-visitor' ; import autoBind from 'auto-bind' ; import { DEFAULT_SCALARS } from './scalars' ; import { NormalizedScalarsMap , EnumValuesMap , ParsedEnumValuesMap , DeclarationKind , ConvertOptions , AvoidOptionalsConfig , } from './types' ; import { DeclarationBlock , DeclarationBlockConfig , indent , getBaseTypeNode , getConfigValue , stripMapperTypeInterpolation , OMIT_TYPE , REQUIRE_FIELDS_TYPE , wrapTypeWithModifiers , buildScalarsFromConfig , } from './utils' ; import { NameNode , ListTypeNode , NamedTypeNode , FieldDefinitionNode , ObjectTypeDefinitionNode , GraphQLSchema , NonNullTypeNode , UnionTypeDefinitionNode , ScalarTypeDefinitionNode , InterfaceTypeDefinitionNode , isObjectType , isInterfaceType , isNonNullType , isUnionType , GraphQLNamedType , isEnumType , DirectiveDefinitionNode , GraphQLObjectType , InputValueDefinitionNode , EnumTypeDefinitionNode , ASTNode , } from 'graphql' ; import { OperationVariablesToObject } from './variables-to-object' ; import { ParsedMapper , parseMapper , transformMappers , ExternalParsedMapper , buildMapperImport } from './mappers' ; import { parseEnumValues } from './enum-values' ; import { ApolloFederation , getBaseType } from '@graphql-codegen/plugin-helpers' ; import { getRootTypeNames } from '@graphql-tools/utils' ; export interface ParsedResolversConfig extends ParsedConfig { contextType : ParsedMapper ; fieldContextTypes : Array < string > ; rootValueType : ParsedMapper ; mappers : { [ typeName ] : ParsedMapper ; } ; defaultMapper : ParsedMapper | null ; avoidOptionals : AvoidOptionalsConfig ; addUnderscoreToArgsType : boolean ; enumValues : ParsedEnumValuesMap ; resolverTypeWrapperSignature : string ; federation : boolean ; enumPrefix : boolean ; optionalResolveType : boolean ; immutableTypes : boolean ; namespacedImportName : string ; resolverTypeSuffix : string ; allResolversTypeName : string ; internalResolversPrefix : string ; onlyResolveTypeForInterfaces : boolean ; directiveResolverMappings : Record < string , string > ; } export interface RawResolversConfig extends RawConfig { description exampleMarkdown addUnderscoreToArgsType ? : boolean ; description exampleMarkdown contextType ? : string ; description exampleMarkdown fieldContextTypes ? : Array < string > ; description exampleMarkdown rootValueType ? : string ; description exampleMarkdown mapperTypeSuffix ? : string ; description exampleMarkdown mappers ? : { [ typeName ] : string ; } ; description exampleMarkdown defaultMapper ? : string ; description default exampleMarkdown avoidOptionals ? : boolean | AvoidOptionalsConfig ; description default exampleMarkdown showUnusedMappers ? : boolean ; description enumValues ? : EnumValuesMap ; default description resolverTypeWrapperSignature ? : string ; default description federation ? : boolean ; default description exampleMarkdown enumPrefix ? : boolean ; default description optionalResolveType ? : boolean ; default description immutableTypes ? : boolean ; default description namespacedImportName ? : string ; default description resolverTypeSuffix ? : string ; default description allResolversTypeName ? : string ; type string default description internalResolversPrefix ? : string ; type boolean default description onlyResolveTypeForInterfaces ? : boolean ; ignore directiveResolverMappings ? : Record < string , string > ; } export type ResolverTypes = { [ gqlType ] : string ; } ; export type ResolverParentTypes = { [ gqlType ] : string ; } ; export type GroupedMappers = Record < string , { identifier : string ; asDefault ? : boolean ; } [ ] > ; type FieldContextTypeMap = Record < string , ParsedMapper > ; export class BaseResolversVisitor < TRawConfig extends RawResolversConfig = RawResolversConfig , TPluginConfig extends ParsedResolversConfig = ParsedResolversConfig > extends BaseVisitor < TRawConfig , TPluginConfig > { protected _parsedConfig : TPluginConfig ; protected _declarationBlockConfig : DeclarationBlockConfig = { } ; protected _collectedResolvers : { [ key ] : string ; } = { } ; protected _collectedDirectiveResolvers : { [ key ] : string ; } = { } ; protected _variablesTransformer : OperationVariablesToObject ; protected _usedMappers : { [ key ] : boolean ; } = { } ; protected _resolversTypes : ResolverTypes = { } ; protected _resolversParentTypes : ResolverParentTypes = { } ; protected _rootTypeNames = new Set < string > ( ) ; protected _globalDeclarations = new Set < string > ( ) ; protected _federation : ApolloFederation ; protected _hasScalars = false ; protected _hasFederation = false ; protected _fieldContextTypeMap : FieldContextTypeMap ; private _directiveResolverMappings : Record < string , string > ; constructor ( rawConfig , additionalConfig , private _schema , defaultScalars = DEFAULT_SCALARS ) { super ( rawConfig , { immutableTypes : getConfigValue ( rawConfig . immutableTypes , false ) , optionalResolveType : getConfigValue ( rawConfig . optionalResolveType , false ) , enumPrefix : getConfigValue ( rawConfig . enumPrefix , true ) , federation : getConfigValue ( rawConfig . federation , false ) , resolverTypeWrapperSignature : getConfigValue ( rawConfig . resolverTypeWrapperSignature , 'Promise<T> | T' ) , enumValues : parseEnumValues ( { schema : _schema , mapOrStr : rawConfig . enumValues , } ) , addUnderscoreToArgsType : getConfigValue ( rawConfig . addUnderscoreToArgsType , false ) , onlyResolveTypeForInterfaces : getConfigValue ( rawConfig . onlyResolveTypeForInterfaces , false ) , contextType : parseMapper ( rawConfig . contextType || 'any' , 'ContextType' ) , fieldContextTypes : getConfigValue ( rawConfig . fieldContextTypes , [ ] ) , resolverTypeSuffix : getConfigValue ( rawConfig . resolverTypeSuffix , 'Resolvers' ) , allResolversTypeName : getConfigValue ( rawConfig . allResolversTypeName , 'Resolvers' ) , rootValueType : parseMapper ( rawConfig . rootValueType || '{}' , 'RootValueType' ) , namespacedImportName : getConfigValue ( rawConfig . namespacedImportName , '' ) , avoidOptionals : getConfigValue ( rawConfig . avoidOptionals , false ) , defaultMapper : rawConfig . defaultMapper ? parseMapper ( rawConfig . defaultMapper || 'any' , 'DefaultMapperType' ) : null , mappers : transformMappers ( rawConfig . mappers || { } , rawConfig . mapperTypeSuffix ) , scalars : buildScalarsFromConfig ( _schema , rawConfig , defaultScalars ) , internalResolversPrefix : getConfigValue ( rawConfig . internalResolversPrefix , '__' ) , ... ( additionalConfig || { } ) , } as TPluginConfig ) ; autoBind ( this ) ; this . _federation = new ApolloFederation ( { enabled : this . config . federation , schema : this . schema } ) ; this . _rootTypeNames = getRootTypeNames ( _schema ) ; this . _variablesTransformer = new OperationVariablesToObject ( this . scalars , this . convertName , this . config . namespacedImportName ) ; this . _resolversTypes = this . createResolversFields ( type => this . applyResolverTypeWrapper ( type ) , type => this . clearResolverTypeWrapper ( type ) , name => this . getTypeToUse ( name ) ) ; this . _resolversParentTypes = this . createResolversFields ( type => type , type => type , name => this . getParentTypeToUse ( name ) , namedType => ! isEnumType ( namedType ) ) ; this . _fieldContextTypeMap = this . createFieldContextTypeMap ( ) ; this . _directiveResolverMappings = rawConfig . directiveResolverMappings ?? { } ; } public getResolverTypeWrapperSignature ( ) { return `export type ResolverTypeWrapper<T> = ${ this . config . resolverTypeWrapperSignature };` ; } protected shouldMapType ( type , checkedBefore = { } , duringCheck = [ ] ) { if ( checkedBefore [ type . name ] !== undefined ) { return checkedBefore [ type . name ] ; } if ( type . name . startsWith ( '__' ) || this . config . scalars [ type . name ] ) { return false ; } if ( this . config . mappers [ type . name ] ) { return true ; } if ( isObjectType ( type ) || isInterfaceType ( type ) ) { const fields = type . getFields ( ) ; return Object . keys ( fields ) . filter ( fieldName => { const field = fields [ fieldName ] ; const fieldType = getBaseType ( field . type ) ; return ! duringCheck . includes ( fieldType . name ) ; } ) . some ( fieldName => { const field = fields [ fieldName ] ; const fieldType = getBaseType ( field . type ) ; if ( checkedBefore [ fieldType . name ] !== undefined ) { return checkedBefore [ fieldType . name ] ; } if ( this . config . mappers [ type . name ] ) { return true ; } duringCheck . push ( type . name ) ; const innerResult = this . shouldMapType ( fieldType , checkedBefore , duringCheck ) ; return innerResult ; } ) ; } return false ; } public convertName ( node , options ? , applyNamespacedImport = false ) { const sourceType = super . convertName ( node , options ) ; return `${ applyNamespacedImport && this . config . namespacedImportName ? this . config . namespacedImportName + '.' : '' }${ sourceType }` ; } protected createResolversFields ( applyWrapper , clearWrapper , getTypeToUse , shouldInclude ? ) { const allSchemaTypes = this . _schema . getTypeMap ( ) ; const nestedMapping = { } ; const typeNames = this . _federation . filterTypeNames ( Object . keys ( allSchemaTypes ) ) ; typeNames . forEach ( typeName => { const schemaType = allSchemaTypes [ typeName ] ; nestedMapping [ typeName ] = this . shouldMapType ( schemaType , nestedMapping ) ; } ) ; return typeNames . reduce ( ( prev , typeName ) => { const schemaType = allSchemaTypes [ typeName ] ; if ( typeName . startsWith ( '__' ) || ( shouldInclude && ! shouldInclude ( schemaType ) ) ) { return prev ; } let shouldApplyOmit = false ; const isRootType = this . _rootTypeNames . has ( typeName ) ; const isMapped = this . config . mappers [ typeName ] ; const isScalar = this . config . scalars [ typeName ] ; const hasDefaultMapper = ! ! ( this . config . defaultMapper && this . config . defaultMapper . type ) ; if ( isRootType ) { prev [ typeName ] = applyWrapper ( this . config . rootValueType . type ) ; return prev ; } else if ( isMapped && this . config . mappers [ typeName ] . type ) { this . markMapperAsUsed ( typeName ) ; prev [ typeName ] = applyWrapper ( this . config . mappers [ typeName ] . type ) ; } else if ( isInterfaceType ( schemaType ) ) { const allTypesMap = this . _schema . getTypeMap ( ) ; const implementingTypes = [ ] ; for ( const graphqlType of Object . values ( allTypesMap ) ) { if ( graphqlType instanceof GraphQLObjectType ) { const allInterfaces = graphqlType . getInterfaces ( ) ; if ( allInterfaces . some ( int => int . name === schemaType . name ) ) { implementingTypes . push ( graphqlType . name ) ; } } } const possibleTypes = implementingTypes . map ( name => getTypeToUse ( name ) ) . join ( ' | ' ) || 'never' ; prev [ typeName ] = possibleTypes ; return prev ; } else if ( isEnumType ( schemaType ) && this . config . enumValues [ typeName ] ) { prev [ typeName ] = this . config . enumValues [ typeName ] . sourceIdentifier || this . convertName ( this . config . enumValues [ typeName ] . typeIdentifier ) ; } else if ( hasDefaultMapper && ! hasPlaceholder ( this . config . defaultMapper . type ) ) { prev [ typeName ] = applyWrapper ( this . config . defaultMapper . type ) ; } else if ( isScalar ) { prev [ typeName ] = applyWrapper ( this . _getScalar ( typeName ) ) ; } else if ( isUnionType ( schemaType ) ) { prev [ typeName ] = schemaType . getTypes ( ) . map ( type => getTypeToUse ( type . name ) ) . join ( ' | ' ) ; } else { shouldApplyOmit = true ; prev [ typeName ] = this . convertName ( typeName , { useTypesPrefix : this . config . enumPrefix } , true ) ; } if ( shouldApplyOmit && prev [ typeName ] !== 'any' && isObjectType ( schemaType ) ) { const fields = schemaType . getFields ( ) ; const relevantFields = this . _federation . filterFieldNames ( Object . keys ( fields ) ) . filter ( fieldName => { const field = fields [ fieldName ] ; const baseType = getBaseType ( field . type ) ; if ( shouldInclude && ! shouldInclude ( baseType ) ) { return false ; } return true ; } ) . map ( fieldName => { const field = fields [ fieldName ] ; const baseType = getBaseType ( field . type ) ; const isUnion = isUnionType ( baseType ) ; if ( ! this . config . mappers [ baseType . name ] && ! isUnion && ! nestedMapping [ baseType . name ] ) { return null ; } const addOptionalSign = ! this . config . avoidOptionals && ! isNonNullType ( field . type ) ; return { addOptionalSign , fieldName , replaceWithType : wrapTypeWithModifiers ( getTypeToUse ( baseType . name ) , field . type , { wrapOptional : this . applyMaybe , wrapArray : this . wrapWithArray , } ) , } ; } ) . filter ( a => a ) ; if ( relevantFields . length > 0 ) { prev [ typeName ] = applyWrapper ( this . replaceFieldsInType ( prev [ typeName ] , relevantFields ) ) ; } else { prev [ typeName ] = applyWrapper ( prev [ typeName ] ) ; } } if ( isMapped && hasPlaceholder ( prev [ typeName ] ) ) { prev [ typeName ] = replacePlaceholder ( prev [ typeName ] , typeName ) ; } if ( ! isMapped && hasDefaultMapper && hasPlaceholder ( this . config . defaultMapper . type ) ) { const name = clearWrapper ( isScalar ? this . _getScalar ( typeName ) : prev [ typeName ] ) ; const replaced = replacePlaceholder ( this . config . defaultMapper . type , name ) ; if ( isUnionType ( schemaType ) ) { prev [ typeName ] = replaced ; } else { prev [ typeName ] = applyWrapper ( replacePlaceholder ( this . config . defaultMapper . type , name ) ) ; } } return prev ; } , { } as ResolverTypes ) ; } protected replaceFieldsInType ( typeName , relevantFields ) { this . _globalDeclarations . add ( OMIT_TYPE ) ; return `Omit<${ typeName }, ${ relevantFields . map ( f => `'${ f . fieldName }'` ) . join ( ' | ' ) }> & { ${ relevantFields . map ( f => `${ f . fieldName }${ f . addOptionalSign ? '?' : '' }: ${ f . replaceWithType }` ) . join ( ', ' ) } }` ; } protected applyMaybe ( str ) { const namespacedImportPrefix = this . config . namespacedImportName ? this . config . namespacedImportName + '.' : '' ; return `${ namespacedImportPrefix }Maybe<${ str }>` ; } protected applyResolverTypeWrapper ( str ) { return `ResolverTypeWrapper<${ this . clearResolverTypeWrapper ( str ) }>` ; } protected clearMaybe ( str ) { const namespacedImportPrefix = this . config . namespacedImportName ? this . config . namespacedImportName + '.' : '' ; if ( str . startsWith ( `${ namespacedImportPrefix }Maybe<` ) ) { const maybeRe = new RegExp ( `${ namespacedImportPrefix . replace ( '.' , '\\.' ) }Maybe<(.*?)>$` ) ; return str . replace ( maybeRe , '$1' ) ; } return str ; } protected clearResolverTypeWrapper ( str ) { if ( str . startsWith ( 'ResolverTypeWrapper<' ) ) { return str . replace ( /ResolverTypeWrapper<(.*?)>$/ , '$1' ) ; } return str ; } protected wrapWithArray ( t ) { if ( this . config . immutableTypes ) { return `ReadonlyArray<${ t }>` ; } return `Array<${ t }>` ; } protected createFieldContextTypeMap ( ) { return this . config . fieldContextTypes . reduce < FieldContextTypeMap > ( ( prev , fieldContextType ) => { const items = fieldContextType . split ( '#' ) ; if ( items . length === 3 ) { const [ path , source , contextTypeName ] = items ; return { ... prev , [ path ] : parseMapper ( `${ source }#${ contextTypeName }` ) } ; } const [ path , contextType ] = items ; return { ... prev , [ path ] : parseMapper ( contextType ) } ; } , { } ) ; } public buildResolversTypes ( ) { const declarationKind = 'type' ; return new DeclarationBlock ( this . _declarationBlockConfig ) . export ( ) . asKind ( declarationKind ) . withName ( this . convertName ( 'ResolversTypes' ) ) . withComment ( 'Mapping between all available schema types and the resolvers types' ) . withBlock ( Object . keys ( this . _resolversTypes ) . map ( typeName => indent ( `${ typeName }: ${ this . _resolversTypes [ typeName ] }${ this . getPunctuation ( declarationKind ) }` ) ) . join ( '\n' ) ) . string ; } public buildResolversParentTypes ( ) { const declarationKind = 'type' ; return new DeclarationBlock ( this . _declarationBlockConfig ) . export ( ) . asKind ( declarationKind ) . withName ( this . convertName ( 'ResolversParentTypes' ) ) . withComment ( 'Mapping between all available schema types and the resolvers parents' ) . withBlock ( Object . keys ( this . _resolversParentTypes ) . map ( typeName => indent ( `${ typeName }: ${ this . _resolversParentTypes [ typeName ] }${ this . getPunctuation ( declarationKind ) }` ) ) . join ( '\n' ) ) . string ; } public get schema ( ) : GraphQLSchema { return this . _schema ; } public get defaultMapperType ( ) : string { return this . config . defaultMapper . type ; } public get unusedMappers ( ) { return Object . keys ( this . config . mappers ) . filter ( name => ! this . _usedMappers [ name ] ) ; } public get globalDeclarations ( ) : string [ ] { return Array . from ( this . _globalDeclarations ) ; } protected isMapperImported ( groupedMappers , identifier , source ) { const exists = ! groupedMappers [ source ] ? false : ! ! groupedMappers [ source ] . find ( m => m . identifier === identifier ) ; const existsFromEnums = ! ! Object . keys ( this . config . enumValues ) . map ( key => this . config . enumValues [ key ] ) . find ( o => o . sourceFile === source && o . typeIdentifier === identifier ) ; return exists || existsFromEnums ; } public get mappersImports ( ) : string [ ] { const groupedMappers = { } ; const addMapper = ( source , identifier , asDefault ) => { if ( ! this . isMapperImported ( groupedMappers , identifier , source ) ) { if ( ! groupedMappers [ source ] ) { groupedMappers [ source ] = [ ] ; } groupedMappers [ source ] . push ( { identifier , asDefault } ) ; } } ; Object . keys ( this . config . mappers ) . map ( gqlTypeName => ( { gqlType : gqlTypeName , mapper : this . config . mappers [ gqlTypeName ] } ) ) . filter ( ( { mapper } ) => mapper . isExternal ) . forEach ( ( { mapper } ) => { const externalMapper = mapper as ExternalParsedMapper ; const identifier = stripMapperTypeInterpolation ( externalMapper . import ) ; addMapper ( externalMapper . source , identifier , externalMapper . default ) ; } ) ; if ( this . config . contextType . isExternal ) { addMapper ( this . config . contextType . source , this . config . contextType . import , this . config . contextType . default ) ; } if ( this . config . rootValueType . isExternal ) { addMapper ( this . config . rootValueType . source , this . config . rootValueType . import , this . config . rootValueType . default ) ; } if ( this . config . defaultMapper && this . config . defaultMapper . isExternal ) { const identifier = stripMapperTypeInterpolation ( this . config . defaultMapper . import ) ; addMapper ( this . config . defaultMapper . source , identifier , this . config . defaultMapper . default ) ; } Object . values ( this . _fieldContextTypeMap ) . forEach ( parsedMapper => { if ( parsedMapper . isExternal ) { addMapper ( parsedMapper . source , parsedMapper . import , parsedMapper . default ) ; } } ) ; return Object . keys ( groupedMappers ) . map ( source => buildMapperImport ( source , groupedMappers [ source ] , this . config . useTypeImports ) ) . filter ( Boolean ) ; } setDeclarationBlockConfig ( config ) { this . _declarationBlockConfig = config ; } setVariablesTransformer ( variablesTransfomer ) { this . _variablesTransformer = variablesTransfomer ; } public hasScalars ( ) { return this . _hasScalars ; } public hasFederation ( ) { return this . _hasFederation ; } public getRootResolver ( ) { const name = this . convertName ( this . config . allResolversTypeName ) ; const declarationKind = 'type' ; const contextType = `<ContextType = ${ this . config . contextType . type }>` ; return [ new DeclarationBlock ( this . _declarationBlockConfig ) . export ( ) . asKind ( declarationKind ) . withName ( name , contextType ) . withBlock ( Object . keys ( this . _collectedResolvers ) . map ( schemaTypeName => { const resolverType = this . _collectedResolvers [ schemaTypeName ] ; return indent ( this . formatRootResolver ( schemaTypeName , resolverType , declarationKind ) ) ; } ) . join ( '\n' ) ) . string , ] . join ( '\n' ) ; } protected formatRootResolver ( schemaTypeName , resolverType , declarationKind ) { return `${ schemaTypeName }${ this . config . avoidOptionals ? '' : '?' }: ${ resolverType }${ this . getPunctuation ( declarationKind ) }` ; } public getAllDirectiveResolvers ( ) { if ( Object . keys ( this . _collectedDirectiveResolvers ) . length ) { const declarationKind = 'type' ; const name = this . convertName ( 'DirectiveResolvers' ) ; const contextType = `<ContextType = ${ this . config . contextType . type }>` ; return [ new DeclarationBlock ( this . _declarationBlockConfig ) . export ( ) . asKind ( declarationKind ) . withName ( name , contextType ) . withBlock ( Object . keys ( this . _collectedDirectiveResolvers ) . map ( schemaTypeName => { const resolverType = this . _collectedDirectiveResolvers [ schemaTypeName ] ; return indent ( this . formatRootResolver ( schemaTypeName , resolverType , declarationKind ) ) ; } ) . join ( '\n' ) ) . string , ] . join ( '\n' ) ; } return '' ; } Name ( node ) { return node . value ; } ListType ( node ) { const asString = node . type as any as string ; return this . wrapWithArray ( asString ) ; } protected _getScalar ( name ) { return `${ this . config . namespacedImportName ? this . config . namespacedImportName + '.' : '' }Scalars['${ name }']` ; } NamedType ( node ) { const nameStr = node . name as any as string ; if ( this . config . scalars [ nameStr ] ) { return this . _getScalar ( nameStr ) ; } return this . convertName ( node , null , true ) ; } NonNullType ( node ) { const asString = node . type as any as string ; return asString ; } protected markMapperAsUsed ( name ) { this . _usedMappers [ name ] = true ; } protected getTypeToUse ( name ) { const resolversType = this . convertName ( 'ResolversTypes' ) ; return `${ resolversType }['${ name }']` ; } protected getParentTypeToUse ( name ) { const resolversType = this . convertName ( 'ResolversParentTypes' ) ; return `${ resolversType }['${ name }']` ; } protected getParentTypeForSignature ( _node ) { return 'ParentType' ; } protected transformParentGenericType ( parentType ) { return `ParentType extends ${ parentType } = ${ parentType }` ; } FieldDefinition ( node , key , parent ) { const hasArguments = node . arguments && node . arguments . length > 0 ; const declarationKind = 'type' ; return ( parentName ) => { const original = parent [ key ] ; const baseType = getBaseTypeNode ( original . type ) ; const realType = baseType . name . value ; const parentType = this . schema . getType ( parentName ) ; if ( this . _federation . skipField ( { fieldNode : original , parentType : parentType } ) ) { return null ; } const typeToUse = this . getTypeToUse ( realType ) ; const mappedType = this . _variablesTransformer . wrapAstTypeWithModifiers ( typeToUse , original . type ) ; const subscriptionType = this . _schema . getSubscriptionType ( ) ; const isSubscriptionType = subscriptionType && subscriptionType . name === parentName ; let argsType = hasArguments ? this . convertName ( parentName + ( this . config . addUnderscoreToArgsType ? '_' : '' ) + this . convertName ( node . name , { useTypesPrefix : false , useTypesSuffix : false , } ) + 'Args' , { useTypesPrefix : true , } , true ) : null ; if ( argsType !== null ) { const argsToForceRequire = original . arguments . filter ( arg => ! ! arg . defaultValue || arg . type . kind === 'NonNullType' ) ; if ( argsToForceRequire . length > 0 ) { argsType = this . applyRequireFields ( argsType , argsToForceRequire ) ; } else if ( original . arguments . length > 0 ) { argsType = this . applyOptionalFields ( argsType , original . arguments ) ; } } const parentTypeSignature = this . _federation . transformParentType ( { fieldNode : original , parentType , parentTypeSignature : this . getParentTypeForSignature ( node ) , } ) ; const mappedTypeKey = isSubscriptionType ? `${ mappedType }, "${ node . name }"` : mappedType ; const directiveMappings = node . directives ?. map ( directive => this . _directiveResolverMappings [ directive . name as any ] ) . filter ( Boolean ) . reverse ( ) ?? [ ] ; const resolverType = isSubscriptionType ? 'SubscriptionResolver' : directiveMappings [ 0 ] ?? 'Resolver' ; const signature = { name : node . name as any , modifier : this . config . avoidOptionals ? '' : '?' , type : resolverType , genericTypes : [ mappedTypeKey , parentTypeSignature , this . _fieldContextTypeMap [ `${ parentName }.${ node . name }` ] ? this . _fieldContextTypeMap [ `${ parentName }.${ node . name }` ] . type : 'ContextType' , argsType , ] . filter ( f => f ) , } ; if ( this . _federation . isResolveReferenceField ( node ) ) { this . _hasFederation = true ; signature . type = 'ReferenceResolver' ; if ( signature . genericTypes . length >= 3 ) { signature . genericTypes = signature . genericTypes . slice ( 0 , 3 ) ; } } return indent ( `${ signature . name }${ signature . modifier }: ${ signature . type }<${ signature . genericTypes . join ( ', ' ) }>${ this . getPunctuation ( declarationKind ) }` ) ; } ; } protected applyRequireFields ( argsType , fields ) { this . _globalDeclarations . add ( REQUIRE_FIELDS_TYPE ) ; return `RequireFields<${ argsType }, ${ fields . map ( f => `'${ f . name . value }'` ) . join ( ' | ' ) }>` ; } protected applyOptionalFields ( argsType , _fields ) { this . _globalDeclarations . add ( REQUIRE_FIELDS_TYPE ) ; return `RequireFields<${ argsType }, never>` ; } ObjectTypeDefinition ( node ) { const declarationKind = 'type' ; const name = this . convertName ( node , { suffix : this . config . resolverTypeSuffix , } ) ; const typeName = node . name as any as string ; const parentType = this . getParentTypeToUse ( typeName ) ; const isRootType = [ this . schema . getQueryType ( ) ?. name , this . schema . getMutationType ( ) ?. name , this . schema . getSubscriptionType ( ) ?. name , ] . includes ( typeName ) ; const fieldsContent = node . fields . map ( ( f ) => f ( node . name ) ) ; if ( ! isRootType ) { fieldsContent . push ( indent ( `${ this . config . internalResolversPrefix }isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>${ this . getPunctuation ( declarationKind ) }` ) ) ; } const block = new DeclarationBlock ( this . _declarationBlockConfig ) . export ( ) . asKind ( declarationKind ) . withName ( name , `<ContextType = ${ this . config . contextType . type }, ${ this . transformParentGenericType ( parentType ) }>` ) . withBlock ( fieldsContent . join ( '\n' ) ) ; this . _collectedResolvers [ node . name as any ] = name + '<ContextType>' ; return block . string ; } UnionTypeDefinition ( node , key , parent ) { const declarationKind = 'type' ; const name = this . convertName ( node , { suffix : this . config . resolverTypeSuffix , } ) ; const originalNode = parent [ key ] as UnionTypeDefinitionNode ; const possibleTypes = originalNode . types . map ( node => node . name . value ) . map ( f => `'${ f }'` ) . join ( ' | ' ) ; this . _collectedResolvers [ node . name as any ] = name + '<ContextType>' ; const parentType = this . getParentTypeToUse ( node . name as any as string ) ; return new DeclarationBlock ( this . _declarationBlockConfig ) . export ( ) . asKind ( declarationKind ) . withName ( name , `<ContextType = ${ this . config . contextType . type }, ${ this . transformParentGenericType ( parentType ) }>` ) . withBlock ( indent ( `${ this . config . internalResolversPrefix }resolveType${ this . config . optionalResolveType ? '?' : '' }: TypeResolveFn<${ possibleTypes }, ParentType, ContextType>${ this . getPunctuation ( declarationKind ) }` ) ) . string ; } ScalarTypeDefinition ( node ) { const nameAsString = node . name as any as string ; const baseName = this . getTypeToUse ( nameAsString ) ; if ( this . _federation . skipScalar ( nameAsString ) ) { return null ; } this . _hasScalars = true ; this . _collectedResolvers [ node . name as any ] = 'GraphQLScalarType' ; return new DeclarationBlock ( { ... this . _declarationBlockConfig , blockTransformer ( block ) { return block ; } , } ) . export ( ) . asKind ( 'interface' ) . withName ( this . convertName ( node , { suffix : 'ScalarConfig' , } ) , ` extends GraphQLScalarTypeConfig<${ baseName }, any>` ) . withBlock ( indent ( `name: '${ node . name }'${ this . getPunctuation ( 'interface' ) }` ) ) . string ; } DirectiveDefinition ( node , key , parent ) { if ( this . _federation . skipDirective ( node . name as any ) ) { return null ; } const directiveName = this . convertName ( node , { suffix : 'DirectiveResolver' , } ) ; const sourceNode = parent [ key ] as DirectiveDefinitionNode ; const hasArguments = sourceNode . arguments && sourceNode . arguments . length > 0 ; this . _collectedDirectiveResolvers [ node . name as any ] = directiveName + '<any, any, ContextType>' ; const directiveArgsTypeName = this . convertName ( node , { suffix : 'DirectiveArgs' , } ) ; return [ new DeclarationBlock ( { ... this . _declarationBlockConfig , blockTransformer ( block ) { return block ; } , } ) . export ( ) . asKind ( 'type' ) . withName ( directiveArgsTypeName ) . withContent ( hasArguments ? `{\n${ this . _variablesTransformer . transform < InputValueDefinitionNode > ( sourceNode . arguments ) }\n}` : '{ }' ) . string , new DeclarationBlock ( { ... this . _declarationBlockConfig , blockTransformer ( block ) { return block ; } , } ) . export ( ) . asKind ( 'type' ) . withName ( directiveName , `<Result, Parent, ContextType = ${ this . config . contextType . type }, Args = ${ directiveArgsTypeName }>` ) . withContent ( `DirectiveResolverFn<Result, Parent, ContextType, Args>` ) . string , ] . join ( '\n' ) ; } protected buildEnumResolverContentBlock ( _node , _mappedEnumType ) { throw new Error ( `buildEnumResolverContentBlock is not implemented!` ) ; } protected buildEnumResolversExplicitMappedValues ( _node , _valuesMapping ) { throw new Error ( `buildEnumResolversExplicitMappedValues is not implemented!` ) ; } EnumTypeDefinition ( node ) { const rawTypeName = node . name as any ; if ( ! this . config . mappers [ rawTypeName ] && ! this . config . enumValues [ rawTypeName ] ) { return null ; } const name = this . convertName ( node , { suffix : this . config . resolverTypeSuffix } ) ; this . _collectedResolvers [ rawTypeName ] = name ; const hasExplicitValues = this . config . enumValues [ rawTypeName ] && this . config . enumValues [ rawTypeName ] . mappedValues ; return new DeclarationBlock ( this . _declarationBlockConfig ) . export ( ) . asKind ( 'type' ) . withName ( name ) . withContent ( hasExplicitValues ? this . buildEnumResolversExplicitMappedValues ( node , this . config . enumValues [ rawTypeName ] . mappedValues ) : this . buildEnumResolverContentBlock ( node , this . getTypeToUse ( rawTypeName ) ) ) . string ; } InterfaceTypeDefinition ( node ) { const name = this . convertName ( node , { suffix : this . config . resolverTypeSuffix , } ) ; const declarationKind = 'type' ; const allTypesMap = this . _schema . getTypeMap ( ) ; const implementingTypes = [ ] ; this . _collectedResolvers [ node . name as any ] = name + '<ContextType>' ; for ( const graphqlType of Object . values ( allTypesMap ) ) { if ( graphqlType instanceof GraphQLObjectType ) { const allInterfaces = graphqlType . getInterfaces ( ) ; if ( allInterfaces . find ( int => int . name === ( node . name as any as string ) ) ) { implementingTypes . push ( graphqlType . name ) ; } } } const parentType = this . getParentTypeToUse ( node . name as any as string ) ; const possibleTypes = implementingTypes . map ( name => `'${ name }'` ) . join ( ' | ' ) || 'null' ; const fields = this . config . onlyResolveTypeForInterfaces ? [ ] : node . fields || [ ] ; return new DeclarationBlock ( this . _declarationBlockConfig ) . export ( ) . asKind ( declarationKind ) . withName ( name , `<ContextType = ${ this . config . contextType . type }, ${ this . transformParentGenericType ( parentType ) }>` ) . withBlock ( [ indent ( `${ this . config . internalResolversPrefix }resolveType${ this . config . optionalResolveType ? '?' : '' }: TypeResolveFn<${ possibleTypes }, ParentType, ContextType>${ this . getPunctuation ( declarationKind ) }` ) , ... fields . map ( ( f ) => f ( node . name ) ) , ] . join ( '\n' ) ) . string ; } SchemaDefinition ( ) { return null ; } } function replacePlaceholder ( pattern , typename ) { return pattern . replace ( /\{T\}/g , typename ) ; } function hasPlaceholder ( pattern ) { return pattern . includes ( '{T}' ) ; }