UNPKG

nexus

Version:

Scalable, strongly typed GraphQL schema development

185 lines (172 loc) 6.04 kB
import type { NexusFinalArgConfig } from '../definitions/args' import type { NexusInputFieldDef, NexusOutputFieldDef } from '../definitions/definitionBlocks' import { list } from '../definitions/list' import { nonNull } from '../definitions/nonNull' import { nullable } from '../definitions/nullable' import { plugin } from '../plugin' import { printedGenTyping } from '../utils' import { isNexusWrappingType } from '../definitions/wrapping' import { messages } from '../messages' interface DeclarativeWrappingApi { nullable?: boolean list?: true | boolean[] required?: boolean } const DeclarativeWrapping = [ printedGenTyping({ name: 'nullable', type: 'boolean', optional: true, description: `\ Whether the type can be null @default (depends on whether nullability is configured in type or schema) @see declarativeWrappingPlugin `, }), printedGenTyping({ name: 'list', type: 'true | boolean[]', optional: true, description: `\ Whether the type is list of values, or just a single value. If list is true, we assume the type is a list. If list is an array, we'll assume that it's a list with the depth. The boolean indicates whether the type is required (non-null), where true = nonNull, false = nullable. @see declarativeWrappingPlugin `, }), printedGenTyping({ name: 'required', type: 'boolean', optional: true, description: `\ Whether the type should be non null, \`required: true\` = \`nullable: false\` @default (depends on whether nullability is configured in type or schema) @see declarativeWrappingPlugin `, }), ] export type DeclarativeWrappingPluginConfig = { /** Whether we should warn, the default when not otherwise specified. */ shouldWarn?: boolean /** Whether we should completely disable the plugin, not install types, and throw when we encounter any issues. */ disable?: boolean } export const declarativeWrappingPlugin = (config: DeclarativeWrappingPluginConfig = {}) => { let hasWarned = false return plugin({ name: 'declarativeWrapping', fieldDefTypes: config.disable ? undefined : DeclarativeWrapping, argTypeDefTypes: config.disable ? undefined : DeclarativeWrapping, inputFieldDefTypes: config.disable ? undefined : DeclarativeWrapping, description: 'Provides a declarative nullable/list API, available by default pre-0.19', onAddOutputField(field) { return { ...field, type: maybeWrapType(field, config), } }, onAddInputField(field) { return { ...field, type: maybeWrapType(field, config), } }, onAddArg(arg) { return { ...arg, type: maybeWrapType(arg, config), } }, }) function maybeWrapType( field: NexusOutputFieldDef & DeclarativeWrappingApi, config: DeclarativeWrappingPluginConfig ): NexusOutputFieldDef['type'] function maybeWrapType( field: NexusInputFieldDef & DeclarativeWrappingApi, config: DeclarativeWrappingPluginConfig ): NexusInputFieldDef['type'] function maybeWrapType( field: NexusFinalArgConfig & DeclarativeWrappingApi, config: DeclarativeWrappingPluginConfig ): NexusFinalArgConfig['type'] function maybeWrapType( field: (NexusOutputFieldDef | NexusInputFieldDef | NexusFinalArgConfig) & DeclarativeWrappingApi, config: DeclarativeWrappingPluginConfig ) { if (field.list == null && field.nullable == null && field.required == null) { return field.type } const used: string[] = [] if (field.list != null) { used.push('list') } if (field.nullable != null) { used.push('nullable') } if (field.required != null) { used.push('required') } if (config.disable || config.shouldWarn) { const d = field as NexusFinalArgConfig | NexusOutputFieldDef | NexusInputFieldDef let location = d.configFor === 'arg' ? `'${d.parentType}.${d.fieldName}' field's '${d.argName}' argument` : `'${d.parentType}.${d.type}' field` if (config.disable) { throw new Error(messages.removedDeclarativeWrapping(location, used)) } else { if (hasWarned) { console.warn(messages.removedDeclarativeWrappingShort(location, used)) } else { console.warn(messages.removedDeclarativeWrapping(location, used)) } } } if ( isNexusWrappingType(field.type) && (field.list != null || field.nullable != null || field.required != null) ) { let errorStr = field.configFor === 'arg' ? `the arg '${field.argName}' of the field '${field.parentType}.${field.fieldName}'.` : `the field '${field.parentType}.${field.name}'.` let usedProp = field.list != null ? 'list' : field.nullable != null ? 'nullable' : 'required' throw new Error( `[declarativeWrappingPlugin] It looks like you used \`${usedProp}\` and wrapped the type of ` + errorStr + ' You should only do one or the other' ) } let type = field.type if (field.list === true) { if (field.nullable === false || field.required === true) { type = nonNull(type) } type = list(type) } else if (Array.isArray(field.list)) { for (const isNonNull of field.list) { if (isNonNull === true) { type = list(nonNull(type)) } else { type = list(nullable(type)) } } } /* istanbul ignore if */ if (field.required != null && field.nullable != null) { let errorSuffix = field.configFor === 'arg' ? ` on ${field.parentType}.${field.fieldName} arg ${field.argName}` : ` on ${field.parentType}.${field.name}` throw new Error(`Cannot set both required & nullable wrapping modifiers on ${errorSuffix}`) } if (field.nullable === true || field.required === false) { type = nullable(type) } else if (field.nullable === false || field.required === true) { type = nonNull(type) } return type } }