import { GraphQLInputType, GraphQLList, GraphQLNamedType, GraphQLNonNull, GraphQLType, isWrappingType, isListType, isNonNullType, } from 'graphql' import { DynamicInputMethodDef, DynamicOutputMethodDef } from '../dynamicMethod' import { DynamicOutputPropertyDef } from '../dynamicProperty' import { NexusPlugin } from '../plugin' import { AllInputTypes, GetGen } from '../typegenTypeHelpers' import { PrintedGenTyping, PrintedGenTypingImport, Unreachable } from '../utils' import { NexusArgDef, arg } from './args' import { NexusEnumTypeDef } from './enumType' import { NexusExtendInputTypeDef } from './extendInputType' import { NexusExtendTypeDef } from './extendType' import { NexusInputObjectTypeDef } from './inputObjectType' import { NexusInterfaceTypeDef } from './interfaceType' import { list, NexusListDef } from './list' import { NexusNonNullDef, nonNull } from './nonNull' import { NexusNullDef, nullable } from './nullable' import { NexusObjectTypeDef } from './objectType' import { NexusScalarTypeDef } from './scalarType' import { NexusUnionTypeDef } from './unionType' import { NexusTypes, NexusWrappedSymbol } from './_types' export type AllNexusNamedInputTypeDefs = | NexusInputObjectTypeDef | NexusEnumTypeDef | NexusScalarTypeDef | Exclude | GraphQLNonNull> export type AllNexusInputTypeDefs = | AllNexusNamedInputTypeDefs | NexusListDef | NexusNonNullDef | NexusNullDef | GraphQLList | GraphQLNonNull export type AllNexusNamedOutputTypeDefs = | NexusObjectTypeDef | NexusInterfaceTypeDef | NexusUnionTypeDef | NexusEnumTypeDef | NexusScalarTypeDef export type AllNexusOutputTypeDefs = | AllNexusNamedOutputTypeDefs | NexusListDef | NexusNonNullDef | NexusNullDef export type AllNexusNamedTypeDefs = AllNexusNamedInputTypeDefs | AllNexusNamedOutputTypeDefs export type AllNexusTypeDefs = AllNexusOutputTypeDefs | AllNexusInputTypeDefs export type NexusListableTypes = | AllNamedTypeDefs | NexusArgDef | NexusListDef | NexusNonNullDef | NexusNullDef | GraphQLType export type NexusNonNullableTypes = AllNamedTypeDefs | NexusListDef | NexusArgDef export type NexusNullableTypes = AllNamedTypeDefs | NexusListDef | NexusArgDef export type AllNamedTypeDefs = GetGen<'allNamedTypes', string> | AllNexusNamedTypeDefs export type AllNexusNamedArgsDefs = | T | NexusArgDef | AllNexusNamedInputTypeDefs | GraphQLInputType export type AllNexusArgsDefs = | AllNexusNamedArgsDefs | NexusListDef | NexusNonNullDef | NexusNullDef | 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 { return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.List } export function isNexusNonNullTypeDef(obj: any): obj is NexusNonNullDef { return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.NonNull } export function isNexusNullTypeDef(obj: any): obj is NexusNullDef { return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.Null } export function isNexusWrappingType( obj: any ): obj is NexusListDef | NexusNullDef | NexusNonNullDef { return isNexusListTypeDef(obj) || isNexusNullTypeDef(obj) || isNexusNonNullTypeDef(obj) } export function isNexusExtendInputTypeDef(obj: any): obj is NexusExtendInputTypeDef { return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.ExtendInputObject } export function isNexusExtendTypeDef(obj: any): obj is NexusExtendTypeDef { return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.ExtendObject } export function isNexusEnumTypeDef(obj: any): obj is NexusEnumTypeDef { return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.Enum } export function isNexusInputObjectTypeDef(obj: any): obj is NexusInputObjectTypeDef { return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.InputObject } export function isNexusObjectTypeDef(obj: any): obj is NexusObjectTypeDef { return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.Object } export function isNexusScalarTypeDef(obj: any): obj is NexusScalarTypeDef { return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.Scalar } export function isNexusUnionTypeDef(obj: any): obj is NexusUnionTypeDef { return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.Union } export function isNexusInterfaceTypeDef(obj: any): obj is NexusInterfaceTypeDef { return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.Interface } export function isNexusArgDef(obj: any): obj is NexusArgDef { return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.Arg } export function isNexusDynamicOutputProperty(obj: any): obj is DynamicOutputPropertyDef { return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.DynamicOutputProperty } export function isNexusDynamicOutputMethod(obj: any): obj is DynamicOutputMethodDef { return isNexusStruct(obj) && obj[NexusWrappedSymbol] === NexusTypes.DynamicOutputMethod } export function isNexusDynamicInputMethod(obj: any): obj is DynamicInputMethodDef { 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 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 | string ): { namedType: AllNexusNamedTypeDefs | AllNexusArgsDefs | GraphQLNamedType | string wrapping: NexusWrapKind[] } { const wrapping: NexusWrapKind[] = [] let namedType = typeDef while (isNexusWrappingType(namedType) || isWrappingType(namedType)) { 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 = GraphQLList(finalType) } else if (wrap === 'NonNull') { finalType = 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 { 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, 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 }