import { inspect } from '../jsutils/inspect' ; import { invariant } from '../jsutils/invariant' ; import { isIterableObject } from '../jsutils/isIterableObject' ; import { isObjectLike } from '../jsutils/isObjectLike' ; import type { Maybe } from '../jsutils/Maybe' ; import type { ObjectFieldNode , ValueNode } from '../language/ast' ; import { Kind } from '../language/kinds' ; import type { GraphQLInputType } from '../type/definition' ; import { isEnumType , isInputObjectType , isLeafType , isListType , isNonNullType , } from '../type/definition' ; import { GraphQLID } from '../type/scalars' ; /**
 * Produces a GraphQL Value AST given a JavaScript object.
 * Function will match JavaScript/JSON values to GraphQL AST schema format
 * by using suggested GraphQLInputType. For example:
 *
 *     astFromValue("value", GraphQLString)
 *
 * A GraphQL type must be provided, which will be used to interpret different
 * JavaScript values.
 *
 * | JSON Value    | GraphQL Value        |
 * | ------------- | -------------------- |
 * | Object        | Input Object         |
 * | Array         | List                 |
 * | Boolean       | Boolean              |
 * | String        | String / Enum Value  |
 * | Number        | Int / Float          |
 * | Unknown       | Enum Value           |
 * | null          | NullValue            |
 *
 */ export function astFromValue ( value , type ) { if ( isNonNullType ( type ) ) { const astValue = astFromValue ( value , type . ofType ) ; if ( astValue ?. kind === Kind . NULL ) { return null ; } return astValue ; } if ( value === null ) { return { kind : Kind . NULL } ; } if ( value === undefined ) { return null ; } if ( isListType ( type ) ) { const itemType = type . ofType ; if ( isIterableObject ( value ) ) { const valuesNodes = [ ] ; for ( const item of value ) { const itemNode = astFromValue ( item , itemType ) ; if ( itemNode != null ) { valuesNodes . push ( itemNode ) ; } } return { kind : Kind . LIST , values : valuesNodes } ; } return astFromValue ( value , itemType ) ; } if ( isInputObjectType ( type ) ) { if ( ! isObjectLike ( value ) ) { return null ; } const fieldNodes = [ ] ; for ( const field of Object . values ( type . getFields ( ) ) ) { const fieldValue = astFromValue ( value [ field . name ] , field . type ) ; if ( fieldValue ) { fieldNodes . push ( { kind : Kind . OBJECT_FIELD , name : { kind : Kind . NAME , value : field . name } , value : fieldValue , } ) ; } } return { kind : Kind . OBJECT , fields : fieldNodes } ; } if ( isLeafType ( type ) ) { const serialized = type . serialize ( value ) ; if ( serialized == null ) { return null ; } if ( typeof serialized === 'boolean' ) { return { kind : Kind . BOOLEAN , value : serialized } ; } if ( typeof serialized === 'number' && Number . isFinite ( serialized ) ) { const stringNum = String ( serialized ) ; return integerStringRegExp . test ( stringNum ) ? { kind : Kind . INT , value : stringNum } : { kind : Kind . FLOAT , value : stringNum } ; } if ( typeof serialized === 'string' ) { if ( isEnumType ( type ) ) { return { kind : Kind . ENUM , value : serialized } ; } if ( type === GraphQLID && integerStringRegExp . test ( serialized ) ) { return { kind : Kind . INT , value : serialized } ; } return { kind : Kind . STRING , value : serialized , } ; } throw new TypeError ( `Cannot convert value to AST: ${ inspect ( serialized ) }.` ) ; } invariant ( false , 'Unexpected input type: ' + inspect ( type ) ) ; } /**
 * IntValue:
 *   - NegativeSign? 0
 *   - NegativeSign? NonZeroDigit ( Digit+ )?
 */ const integerStringRegExp = /^-?(?:0|[1-9][0-9]*)$/ ;