import { inspect } from '../jsutils/inspect' ; import { invariant } from '../jsutils/invariant' ; import { keyMap } from '../jsutils/keyMap' ; import type { Maybe } from '../jsutils/Maybe' ; import type { ObjMap } from '../jsutils/ObjMap' ; import type { ValueNode } from '../language/ast' ; import { Kind } from '../language/kinds' ; import type { GraphQLInputType } from '../type/definition' ; import { isInputObjectType , isLeafType , isListType , isNonNullType , } from '../type/definition' ; /**
 * Produces a JavaScript value given a GraphQL Value AST.
 *
 * A GraphQL type must be provided, which will be used to interpret different
 * GraphQL Value literals.
 *
 * Returns `undefined` when the value could not be validly coerced according to
 * the provided type.
 *
 * | GraphQL Value        | JSON Value    |
 * | -------------------- | ------------- |
 * | Input Object         | Object        |
 * | List                 | Array         |
 * | Boolean              | Boolean       |
 * | String               | String        |
 * | Int / Float          | Number        |
 * | Enum Value           | Unknown       |
 * | NullValue            | null          |
 *
 */ export function valueFromAST ( valueNode , type , variables ? ) { if ( ! valueNode ) { return ; } if ( valueNode . kind === Kind . VARIABLE ) { const variableName = valueNode . name . value ; if ( variables == null || variables [ variableName ] === undefined ) { return ; } const variableValue = variables [ variableName ] ; if ( variableValue === null && isNonNullType ( type ) ) { return ; } return variableValue ; } if ( isNonNullType ( type ) ) { if ( valueNode . kind === Kind . NULL ) { return ; } return valueFromAST ( valueNode , type . ofType , variables ) ; } if ( valueNode . kind === Kind . NULL ) { return null ; } if ( isListType ( type ) ) { const itemType = type . ofType ; if ( valueNode . kind === Kind . LIST ) { const coercedValues = [ ] ; for ( const itemNode of valueNode . values ) { if ( isMissingVariable ( itemNode , variables ) ) { if ( isNonNullType ( itemType ) ) { return ; } coercedValues . push ( null ) ; } else { const itemValue = valueFromAST ( itemNode , itemType , variables ) ; if ( itemValue === undefined ) { return ; } coercedValues . push ( itemValue ) ; } } return coercedValues ; } const coercedValue = valueFromAST ( valueNode , itemType , variables ) ; if ( coercedValue === undefined ) { return ; } return [ coercedValue ] ; } if ( isInputObjectType ( type ) ) { if ( valueNode . kind !== Kind . OBJECT ) { return ; } const coercedObj = Object . create ( null ) ; const fieldNodes = keyMap ( valueNode . fields , ( field ) => field . name . value ) ; for ( const field of Object . values ( type . getFields ( ) ) ) { const fieldNode = fieldNodes [ field . name ] ; if ( ! fieldNode || isMissingVariable ( fieldNode . value , variables ) ) { if ( field . defaultValue !== undefined ) { coercedObj [ field . name ] = field . defaultValue ; } else if ( isNonNullType ( field . type ) ) { return ; } continue ; } const fieldValue = valueFromAST ( fieldNode . value , field . type , variables ) ; if ( fieldValue === undefined ) { return ; } coercedObj [ field . name ] = fieldValue ; } return coercedObj ; } if ( isLeafType ( type ) ) { let result ; try { result = type . parseLiteral ( valueNode , variables ) ; } catch ( _error ) { return ; } if ( result === undefined ) { return ; } return result ; } invariant ( false , 'Unexpected input type: ' + inspect ( type ) ) ; } function isMissingVariable ( valueNode , variables ) { return ( valueNode . kind === Kind . VARIABLE && ( variables == null || variables [ valueNode . name . value ] === undefined ) ) ; }