import * as GraphQL from "graphql" ; import { DenormalizationResult , FieldNodeWithSelectionSet , Variables , ResponseObject , ResponseObject2 , ResponseObjectArray , RootFields , ResolveType } from "./types" ; import { expandFragments , getDocumentDefinitions , fieldNameWithArguments , shouldIncludeField , defaultResolveType } from "./functions" ; import { NormMap , NormKey } from "./norm-map" ; type Mutable < T > = { - readonly [ P in keyof T ] : T [ P ] ; } ; type MutableResponseObject = Mutable < ResponseObject > ; type MutableResponseObjectArray = Array < MutableResponseObject > ; type ParentResponseObjectOrArray = Mutable < ResponseObject2 > | ResponseObjectArray ; type ParentResponseKey = string | number | undefined ; type StackWorkItem = readonly [ FieldNodeWithSelectionSet , NormKey | ReadonlyArray < NormKey > , ParentResponseObjectOrArray , ParentResponseKey , NormKey , string ] ; param query param variables param normMap param resolveType export function denormalize ( query , variables , normMap , resolveType = defaultResolveType ) { const [ fragmentMap , rootFieldNode ] = getDocumentDefinitions ( query . definitions ) ; const stack = [ ] ; const response = { } ; const usedFieldsMap = { } ; stack . push ( [ rootFieldNode , "ROOT_QUERY" , response , undefined , "ROOT_QUERY" , "ROOT_QUERY" ] ) ; while ( stack . length > 0 ) { const [ fieldNode , idOrIdArray , parentObjectOrArray , parentResponseKey , parentNormKey , fieldNameInParent ] = stack . pop ( ) ! ; let responseObjectOrNewParentArray ; if ( idOrIdArray === null ) { responseObjectOrNewParentArray = null ; } else if ( ! Array . isArray ( idOrIdArray ) ) { const key = idOrIdArray as NormKey ; const normObj = normMap [ key ] ; if ( normObj === undefined ) { return { data : undefined , fields : { [ parentNormKey ] : new Set ( [ fieldNameInParent ] ) } } ; } let usedFields = usedFieldsMap [ key ] ; if ( usedFields === undefined ) { usedFields = new Set ( ) ; usedFieldsMap [ key ] = usedFields ; } if ( Array . isArray ( parentObjectOrArray ) ) { responseObjectOrNewParentArray = ( parentObjectOrArray as MutableResponseObjectArray ) [ parentResponseKey as number ] || Object . create ( null ) ; } else { responseObjectOrNewParentArray = ( parentObjectOrArray as MutableResponseObject ) [ parentResponseKey as string ] || Object . create ( null ) ; } const expandedSelections = expandFragments ( resolveType , normObj , fieldNode . selectionSet . selections , fragmentMap ) ; for ( const field of expandedSelections ) { const include = field . directives ? shouldIncludeField ( field . directives , variables ) : true ; if ( include ) { const fieldName = field . arguments && field . arguments . length > 0 ? fieldNameWithArguments ( field , variables ) : field . name . value ; usedFields . add ( fieldName ) ; const normObjValue = normObj [ fieldName ] ; if ( normObjValue !== null && field . selectionSet ) { stack . push ( [ field as FieldNodeWithSelectionSet , normObjValue as any , responseObjectOrNewParentArray as MutableResponseObject | MutableResponseObjectArray , ( field . alias && field . alias . value ) || field . name . value , key , fieldName ] ) ; } else { if ( normObjValue !== undefined ) { ( responseObjectOrNewParentArray as MutableResponseObject ) [ ( field . alias && field . alias . value ) || field . name . value ] = normObjValue ; } else { return { data : undefined , fields : { [ key ] : new Set ( [ fieldName ] ) } } ; } } } } } else { const idArray = idOrIdArray ; responseObjectOrNewParentArray = ( parentObjectOrArray as MutableResponseObject ) [ parentResponseKey as string ] || [ ] ; for ( let i = 0 ; i < idArray . length ; i ++ ) { const idArrayItem = idArray [ i ] ; stack . push ( [ fieldNode , idArrayItem , responseObjectOrNewParentArray as MutableResponseObject | MutableResponseObjectArray , i , parentNormKey , fieldNameInParent ] ) ; } } if ( Array . isArray ( parentObjectOrArray ) ) { const parentArray = parentObjectOrArray ; parentArray [ parentResponseKey as number ] = responseObjectOrNewParentArray as MutableResponseObject | MutableResponseObjectArray ; } else { const parentObject = parentObjectOrArray ; parentObject [ parentResponseKey || ( fieldNode . alias && fieldNode . alias . value ) || fieldNode . name . value ] = responseObjectOrNewParentArray ; } } interface GraphQLResponse { readonly data : RootFields ; } const data = ( response as GraphQLResponse ) . data ; return { data : data , fields : usedFieldsMap } ; }