repo_description: Code-First, Type-Safe, GraphQL Schema Construction
repo_topics: graphql graphql-nexus graphql-schema nexus typescript
code: import {
    GraphQLInputType,
    GraphQLList,
    GraphQLNamedType,
    GraphQLNonNull,
    GraphQLType,
    isWrappingType,
    isListType,
    isNonNullType,
  } from 'graphql'
  import type { DynamicInputMethodDef, DynamicOutputMethodDef } from '../dynamicMethod'
  import type { DynamicOutputPropertyDef } from '../dynamicProperty'
  import type { NexusPlugin } from '../plugin'
  import type { AllInputTypes, GetGen } from '../typegenTypeHelpers'
  import { PrintedGenTyping, PrintedGenTypingImport, Unreachable } from '../utils'
  import { NexusArgDef, arg } from './args'
  import type { NexusEnumTypeDef } from './enumType'
  import type { NexusExtendInputTypeDef } from './extendInputType'
  import type { NexusExtendTypeDef } from './extendType'
  import type { NexusInputObjectTypeDef } from './inputObjectType'
  import type { NexusInterfaceTypeDef } from './interfaceType'
  import { list, NexusListDef } from './list'
  import { NexusNonNullDef, nonNull } from './nonNull'
  import { NexusNullDef, nullable } from './nullable'
  import type { NexusObjectTypeDef } from './objectType'
  import type { NexusScalarTypeDef } from './scalarType'
  import { isNexusMetaType, NexusMetaType, resolveNexusMetaType } from './nexusMeta'
  import type { NexusUnionTypeDef } from './unionType'
  import { NexusTypes, NexusWrappedSymbol } from './_types'
  import type { NexusDirectiveDef } from './directive'
  import type { NexusDirectiveUse } from '../core'
  
  /** Input(named): Nexus only */
  export type AllNexusNamedInputTypeDefs<T extends string = any> =
    | NexusInputObjectTypeDef<T>
    | NexusEnumTypeDef<T>
    | NexusScalarTypeDef<T>
  
  /** Input(named): Nexus + GraphQLInput */
  export type AllNamedInputTypeDefs<T extends string = any> =
    | AllNexusNamedInputTypeDefs<T>
    | Exclude<GraphQLInputType, GraphQLList<any> | GraphQLNonNull<any>>
  
  /** Input(all): Nexus + GraphQL */
  export type AllNexusInputTypeDefs<T extends string = any> =
    | AllNamedInputTypeDefs<T>
    | NexusListDef<any>
    | NexusNonNullDef<any>
    | NexusNullDef<any>
    | GraphQLList<any>
    | GraphQLNonNull<any>
  
  /** Output(named): Nexus only */
  export type AllNexusNamedOutputTypeDefs =
    | NexusObjectTypeDef<any>
    | NexusInterfaceTypeDef<any>
    | NexusUnionTypeDef<any>
    | NexusEnumTypeDef<any>
    | NexusScalarTypeDef<any>
  
  /** Output(all): Nexus only */
  export type AllNexusOutputTypeDefs =
    | AllNexusNamedOutputTypeDefs
    | NexusListDef<any>
    | NexusNonNullDef<any>
    | NexusNullDef<any>
  
  /** Input + output(named): Nexus only */
  export type AllNexusNamedTypeDefs = AllNexusNamedInputTypeDefs | AllNexusNamedOutputTypeDefs
  
  /** Input + output(all): Nexus only */
  export type AllNexusTypeDefs = AllNexusOutputTypeDefs | AllNexusInputTypeDefs
  
  /** Input + output(all): Nexus only + Name */
  export type AllNamedTypeDefs = AllNexusNamedTypeDefs | GraphQLNamedType
  
  /** All inputs to list(...) */
  export type NexusListableTypes =
    | GetGen<'allNamedTypes', string>
    | AllNamedTypeDefs
    | NexusArgDef<any>
    | NexusListDef<NexusListableTypes>
    | NexusNonNullDef<NexusNonNullableTypes>
    | NexusNullDef<NexusNullableTypes>
    | GraphQLType
    | NexusMetaType
  
  /** All inputs to nonNull(...) */
  export type NexusNonNullableTypes =
    | GetGen<'allNamedTypes', string>
    | AllNamedTypeDefs
    | NexusListDef<NexusListableTypes>
    | NexusArgDef<any>
    | NexusMetaType
  
  /** All inputs to nullable(...) */
  export type NexusNullableTypes =
    | GetGen<'allNamedTypes', string>
    | AllNamedTypeDefs
    | NexusListDef<NexusListableTypes>
    | NexusArgDef<any>
    | NexusMetaType
  
  export type AllNexusNamedArgsDefs<T extends AllInputTypes = AllInputTypes> =
    | T
    | NexusArgDef<T>
    | AllNamedInputTypeDefs<T>
    | GraphQLInputType
  
  export type AllNexusArgsDefs =
    | AllNexusNamedArgsDefs
    | NexusListDef<any>
    | NexusNonNullDef<any>
    | NexusNullDef<any>
    | GraphQLInputType
  
  const NamedTypeDefs = new Set([
    NexusTypes.Enum,
    NexusTypes.Object,
    NexusTypes.Scalar,
    NexusTypes.Union,
    NexusTypes.Interface,
    NexusTypes.InputObject,
  ])
  
  export const isNexusTypeDef = (obj: any): obj is { [NexusWrappedSymbol]: NexusTypes } => {
    console.warn(`isNexusTypeDef is deprecated, use isNexusStruct`)
    return isNexusStruct(obj)
  }
  
  export function isNexusStruct(obj: any): obj is { [NexusWrappedSymbol]: NexusTypes } {
    return obj && Boolean(obj[NexusWrappedSymbol])
  }
  export function isNexusNamedTypeDef(obj: any): obj is AllNexusNamedTypeDefs {
    return isNexusStruct(obj) && NamedTypeDefs.has(obj[NexusWrappedSymbol]) && 'name' in obj
  }
  export function isNexusListTypeDef(obj: any): obj is NexusListDef<any> {
    return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.List
  }
  
  export function isNexusNonNullTypeDef(obj: any): obj is NexusNonNullDef<any> {
    return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.NonNull
  }
  
  export function isNexusNullTypeDef(obj: any): obj is NexusNullDef<any> {
    return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.Null
  }
  
  export function isNexusWrappingType(
    obj: any
  ): obj is NexusListDef<any> | NexusNullDef<any> | NexusNonNullDef<any> {
    return isNexusListTypeDef(obj) || isNexusNullTypeDef(obj) || isNexusNonNullTypeDef(obj)
  }
  
  export function isNexusExtendInputTypeDef(obj: any): obj is NexusExtendInputTypeDef<string> {
    return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.ExtendInputObject
  }
  export function isNexusExtendTypeDef(obj: any): obj is NexusExtendTypeDef<string> {
    return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.ExtendObject
  }
  
  export function isNexusEnumTypeDef(obj: any): obj is NexusEnumTypeDef<string> {
    return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.Enum
  }
  export function isNexusInputObjectTypeDef(obj: any): obj is NexusInputObjectTypeDef<string> {
    return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.InputObject
  }
  export function isNexusObjectTypeDef(obj: any): obj is NexusObjectTypeDef<string> {
    return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.Object
  }
  export function isNexusScalarTypeDef(obj: any): obj is NexusScalarTypeDef<string> {
    return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.Scalar
  }
  export function isNexusUnionTypeDef(obj: any): obj is NexusUnionTypeDef<string> {
    return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.Union
  }
  export function isNexusInterfaceTypeDef(obj: any): obj is NexusInterfaceTypeDef<string> {
    return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.Interface
  }
  export function isNexusArgDef(obj: any): obj is NexusArgDef<AllInputTypes> {
    return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.Arg
  }
  
  export function isNexusNamedOuputTypeDef(obj: any): obj is AllNexusNamedOutputTypeDefs {
    return isNexusNamedTypeDef(obj) && !isNexusInputObjectTypeDef(obj)
  }
  export function isNexusNamedInputTypeDef(obj: any): obj is AllNexusNamedInputTypeDefs {
    return isNexusNamedTypeDef(obj) && !isNexusObjectTypeDef(obj) && !isNexusInterfaceTypeDef(obj)
  }
  
  export function isNexusDynamicOutputProperty<T extends string>(obj: any): obj is DynamicOutputPropertyDef<T> {
    return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.DynamicOutputProperty
  }
  export function isNexusDynamicOutputMethod<T extends string>(obj: any): obj is DynamicOutputMethodDef<T> {
    return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.DynamicOutputMethod
  }
  export function isNexusDynamicInputMethod<T extends string>(obj: any): obj is DynamicInputMethodDef<T> {
    return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.DynamicInput
  }
  export function isNexusPrintedGenTyping(obj: any): obj is PrintedGenTyping {
    return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.PrintedGenTyping
  }
  export function isNexusPrintedGenTypingImport(obj: any): obj is PrintedGenTypingImport {
    return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.PrintedGenTypingImport
  }
  
  export function isNexusPlugin(obj: any): obj is NexusPlugin {
    return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.Plugin
  }
  
  export function isNexusDirective(obj: any): obj is NexusDirectiveDef {
    return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.Directive
  }
  export function isNexusDirectiveUse(obj: any): obj is NexusDirectiveUse {
    return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.DirectiveUse
  }
  
  export type NexusWrapKind = 'NonNull' | 'Null' | 'List'
  export type NexusFinalWrapKind = 'NonNull' | 'List'
  
  export function unwrapGraphQLDef(typeDef: GraphQLType): {
    namedType: GraphQLNamedType
    wrapping: NexusFinalWrapKind[]
  } {
    const wrapping: NexusFinalWrapKind[] = []
    let namedType = typeDef
    while (isWrappingType(namedType)) {
      if (isListType(namedType)) {
        wrapping.unshift('List')
      } else if (isNonNullType(namedType)) {
        wrapping.unshift('NonNull')
      } else {
        throw new Unreachable(namedType)
      }
      namedType = namedType.ofType
    }
    return { namedType, wrapping }
  }
  
  /** Unwraps any wrapped Nexus or GraphQL types, turning into a list of wrapping */
  export function unwrapNexusDef(
    typeDef: AllNexusTypeDefs | AllNexusArgsDefs | GraphQLType | NexusMetaType | string
  ): {
    namedType: AllNexusNamedTypeDefs | AllNexusArgsDefs | GraphQLNamedType | string
    wrapping: NexusWrapKind[]
  } {
    const wrapping: NexusWrapKind[] = []
    let namedType = typeDef
    while (isNexusWrappingType(namedType) || isWrappingType(namedType) || isNexusMetaType(namedType)) {
      if (isNexusMetaType(namedType)) {
        namedType = resolveNexusMetaType(namedType)
      } else if (isWrappingType(namedType)) {
        if (isListType(namedType)) {
          wrapping.unshift('List')
        } else if (isNonNullType(namedType)) {
          wrapping.unshift('NonNull')
        } else {
          throw new Unreachable(namedType)
        }
        namedType = namedType.ofType
      } else {
        if (isNexusNonNullTypeDef(namedType)) {
          wrapping.unshift('NonNull')
        }
        if (isNexusNullTypeDef(namedType)) {
          wrapping.unshift('Null')
        }
        if (isNexusListTypeDef(namedType)) {
          wrapping.unshift('List')
        }
        namedType = namedType.ofNexusType
      }
    }
    return { namedType, wrapping }
  }
  
  /** Takes the named type, and applies any of the NexusFinalWrapKind to create a properly wrapped GraphQL type. */
  export function rewrapAsGraphQLType(baseType: GraphQLNamedType, wrapping: NexusFinalWrapKind[]): GraphQLType {
    let finalType: GraphQLType = baseType
    wrapping.forEach((wrap) => {
      if (wrap === 'List') {
        finalType = new GraphQLList(finalType)
      } else if (wrap === 'NonNull') {
        if (!isNonNullType(finalType)) {
          finalType = new GraphQLNonNull(finalType)
        }
      } else {
        throw new Unreachable(wrap)
      }
    })
    return finalType
  }
  
  /**
   * Apply the wrapping consistently to the arg `type`
   *
   * NonNull(list(stringArg())) -> arg({ type: nonNull(list('String')) })
   */
  export function normalizeArgWrapping(argVal: AllNexusArgsDefs): NexusArgDef<AllInputTypes> {
    if (isNexusArgDef(argVal)) {
      return argVal
    }
    if (isNexusWrappingType(argVal)) {
      let { namedType, wrapping } = unwrapNexusDef(argVal)
      if (isNexusArgDef(namedType)) {
        const config = namedType.value
        return arg({ ...config, type: applyNexusWrapping(config.type, wrapping) })
      }
      return arg({ type: applyNexusWrapping(namedType, wrapping) })
    }
    return arg({ type: argVal })
  }
  
  /**
   * Applies the ['List', 'NonNull', 'Nullable']
   *
   * @param toWrap
   * @param wrapping
   */
  export function applyNexusWrapping(toWrap: any, wrapping: NexusWrapKind[]) {
    let finalType = toWrap
    wrapping.forEach((wrap) => {
      if (wrap === 'List') {
        finalType = list(finalType)
      } else if (wrap === 'NonNull') {
        finalType = nonNull(finalType)
      } else if (wrap === 'Null') {
        finalType = nullable(finalType)
      } else {
        throw new Unreachable(wrap)
      }
    })
    return finalType
  }
  
  /**
   * Takes the "nonNullDefault" value, the chained wrapping, and the field wrapping, to determine the proper
   * list of wrapping to apply to the field
   */
  export function finalizeWrapping(
    nonNullDefault: boolean,
    typeWrapping: NexusWrapKind[] | ReadonlyArray<NexusWrapKind>,
    chainWrapping?: NexusWrapKind[]
  ): NexusFinalWrapKind[] {
    let finalChain: NexusFinalWrapKind[] = []
    const allWrapping = typeWrapping.concat(chainWrapping ?? [])
    // Ensure the first item is wrapped, if we're not guarding
    if (nonNullDefault && (!allWrapping[0] || allWrapping[0] === 'List')) {
      allWrapping.unshift('NonNull')
    }
    for (let i = 0; i < allWrapping.length; i++) {
      const current = allWrapping[i]
      const next = allWrapping[i + 1]
      if (current === 'Null') {
        continue
      } else if (current === 'NonNull') {
        finalChain.push('NonNull')
      } else if (current === 'List') {
        finalChain.push('List')
        if (nonNullDefault && (next === 'List' || !next)) {
          finalChain.push('NonNull')
        }
      }
    }
    return finalChain
  }
  