import { GraphQLSchema , GraphQLType , GraphQLObjectType , GraphQLCompositeType , DocumentNode } from 'graphql' ; import { compileToIR , CompilerContext , SelectionSet , Field , FragmentSpread } from './' ; import { collectFragmentsReferenced } from './visitors/collectFragmentsReferenced' ; import { generateOperationId } from './visitors/generateOperationId' ; import { typeCaseForSelectionSet } from './visitors/typeCase' ; import { collectAndMergeFields } from './visitors/collectAndMergeFields' ; import '../utilities/array' ; import Maybe from 'graphql/tsutils/Maybe' ; export interface CompilerOptions { addTypename ? : boolean ; mergeInFieldsFromFragmentSpreads ? : boolean ; passthroughCustomScalars ? : boolean ; customScalarsPrefix ? : string ; namespace ? : string ; generateOperationIds ? : boolean ; } export interface LegacyCompilerContext { schema : GraphQLSchema ; operations : { [ operationName ] : LegacyOperation ; } ; fragments : { [ fragmentName ] : LegacyFragment ; } ; typesUsed : GraphQLType [ ] ; options : CompilerOptions ; } export interface LegacyOperation { filePath ? : string ; operationName : string ; operationId ? : string ; operationType : string ; rootType : GraphQLObjectType ; variables : { name : string ; type : GraphQLType ; } [ ] ; source : string ; sourceWithFragments ? : string ; fields : LegacyField [ ] ; fragmentSpreads ? : string [ ] ; inlineFragments ? : LegacyInlineFragment [ ] ; fragmentsReferenced : string [ ] ; } export interface LegacyFragment { filePath ? : string ; fragmentName : string ; source : string ; typeCondition : GraphQLCompositeType ; possibleTypes : GraphQLObjectType [ ] ; fields : LegacyField [ ] ; fragmentSpreads : string [ ] ; inlineFragments : LegacyInlineFragment [ ] ; } export interface LegacyInlineFragment { typeCondition : GraphQLObjectType ; possibleTypes : GraphQLObjectType [ ] ; fields : LegacyField [ ] ; fragmentSpreads : string [ ] ; } export interface LegacyField { responseName : string ; fieldName : string ; args ? : Argument [ ] ; type : GraphQLType ; description ? : Maybe < string > ; isConditional ? : boolean ; conditions ? : BooleanCondition [ ] ; isDeprecated ? : boolean ; deprecationReason ? : string ; fields ? : LegacyField [ ] ; fragmentSpreads ? : string [ ] ; inlineFragments ? : LegacyInlineFragment [ ] ; } export interface BooleanCondition { variableName : string ; inverted : boolean ; } export interface Argument { name : string ; value : any ; } export function compileToLegacyIR ( schema , document , options = { mergeInFieldsFromFragmentSpreads : true } ) { const context = compileToIR ( schema , document , options ) ; const transformer = new LegacyIRTransformer ( context , options ) ; return transformer . transformIR ( ) ; } class LegacyIRTransformer { constructor ( public context , public options = { mergeInFieldsFromFragmentSpreads : true } ) { } transformIR ( ) { const operations = Object . create ( { } ) ; for ( const [ operationName , operation ] of Object . entries ( this . context . operations ) ) { const { filePath , operationType , rootType , variables , source , selectionSet } = operation ; const fragmentsReferenced = collectFragmentsReferenced ( selectionSet , this . context . fragments ) ; const { sourceWithFragments , operationId } = generateOperationId ( operation , this . context . fragments , fragmentsReferenced ) ; operations [ operationName ] = { filePath , operationName , operationType , rootType , variables , source , ... this . transformSelectionSetToLegacyIR ( selectionSet ) , fragmentsReferenced : Array . from ( fragmentsReferenced ) , sourceWithFragments , operationId , } ; } const fragments = Object . create ( { } ) ; for ( const [ fragmentName , fragment ] of Object . entries ( this . context . fragments ) ) { const { selectionSet , type , ... fragmentWithoutSelectionSet } = fragment ; fragments [ fragmentName ] = { typeCondition : type , possibleTypes : selectionSet . possibleTypes , ... fragmentWithoutSelectionSet , ... this . transformSelectionSetToLegacyIR ( selectionSet ) , } ; } const legacyContext = { schema : this . context . schema , operations , fragments , typesUsed : this . context . typesUsed , options : this . options , } ; return legacyContext ; } transformSelectionSetToLegacyIR ( selectionSet ) { const typeCase = typeCaseForSelectionSet ( selectionSet , this . options . mergeInFieldsFromFragmentSpreads ) ; const fields = this . transformFieldsToLegacyIR ( collectAndMergeFields ( typeCase . default , false ) ) ; const inlineFragments = typeCase . variants . flatMap ( variant => { const fields = this . transformFieldsToLegacyIR ( collectAndMergeFields ( variant , false ) ) ; if ( selectionSet . possibleTypes . every ( type => variant . possibleTypes . includes ( type ) ) && fields . length < 1 ) return undefined ; const fragmentSpreads = this . collectFragmentSpreads ( selectionSet , variant . possibleTypes ) . map ( ( fragmentSpread ) => fragmentSpread . fragmentName ) ; return variant . possibleTypes . map ( possibleType => { return { typeCondition : possibleType , possibleTypes : [ possibleType ] , fields , fragmentSpreads , } as LegacyInlineFragment ; } ) ; } ) ; for ( const inlineFragment of inlineFragments ) { inlineFragments [ inlineFragment . typeCondition . name as any ] = inlineFragment ; } const fragmentSpreads = this . collectFragmentSpreads ( selectionSet ) . map ( ( fragmentSpread ) => fragmentSpread . fragmentName ) ; return { fields , fragmentSpreads , inlineFragments , } ; } transformFieldsToLegacyIR ( fields ) { return fields . map ( field => { const { args , type , isConditional , description , isDeprecated , deprecationReason , selectionSet } = field ; const conditions = field . conditions && field . conditions . length > 0 ? field . conditions . map ( ( { kind , variableName , inverted } ) => { return { kind , variableName , inverted , } ; } ) : undefined ; return { responseName : field . alias || field . name , fieldName : field . name , type , args , isConditional , conditions , description , isDeprecated , deprecationReason , ... ( selectionSet ? this . transformSelectionSetToLegacyIR ( selectionSet ) : { } ) , } as LegacyField ; } ) ; } collectFragmentSpreads ( selectionSet , possibleTypes = selectionSet . possibleTypes ) { const fragmentSpreads = [ ] ; for ( const selection of selectionSet . selections ) { switch ( selection . kind ) { case 'FragmentSpread' : fragmentSpreads . push ( selection ) ; break ; case 'TypeCondition' : if ( possibleTypes . every ( type => selection . selectionSet . possibleTypes . includes ( type ) ) ) { fragmentSpreads . push ( ... this . collectFragmentSpreads ( selection . selectionSet , possibleTypes ) ) ; } break ; case 'BooleanCondition' : fragmentSpreads . push ( ... this . collectFragmentSpreads ( selection . selectionSet , possibleTypes ) ) ; break ; } } return Array . from ( new Set ( fragmentSpreads ) ) ; } }