import { GraphQLFieldResolver } from 'graphql' import { AllInputTypes, FieldResolver, GetGen, GetGen3, HasGen3, NeedsResolver } from '../typegenTypeHelpers' import { ArgsRecord } from './args' import { AllNexusInputTypeDefs, AllNexusOutputTypeDefs, NexusWrapKind } from './wrapping' import { BaseScalars } from './_types' import { messages } from '../messages' export interface CommonFieldConfig { /** * The description to annotate the GraphQL SDL */ description?: string | null /** * Info about a field deprecation. Formatted as a string and provided with the * deprecated directive on field/enum types and as a comment on input fields. */ deprecation?: string // | DeprecationInfo; } export type CommonOutputFieldConfig = CommonFieldConfig & { /** * Arguments for the field */ args?: ArgsRecord } & NexusGenPluginFieldConfig export type CommonInputFieldConfig = CommonFieldConfig & { /** * The default value for the field, if any */ default?: GetGen3<'inputTypes', TypeName, FieldName> } & NexusGenPluginFieldConfig /** * Deprecated, prefer core.CommonInputFieldConfig * * TODO(tim): Remove at 1.0 */ export interface ScalarInputFieldConfig extends CommonInputFieldConfig { default?: T } export interface OutputScalarConfig extends CommonOutputFieldConfig { /** * Resolve method for the field */ resolve?: FieldResolver } export interface NexusOutputFieldConfig extends OutputScalarConfig { type: GetGen<'allOutputTypes', string> | AllNexusOutputTypeDefs } export type NexusOutputFieldDef = NexusOutputFieldConfig & { name: string configFor: 'outputField' parentType: string subscribe?: GraphQLFieldResolver wrapping?: NexusWrapKind[] } // prettier-ignore export type ScalarOutSpread = NeedsResolver extends true ? [ScalarOutConfig] : HasGen3<'argTypes', TypeName, FieldName> extends true ? [ScalarOutConfig] : [ScalarOutConfig] | [] // prettier-ignore export type ScalarOutConfig = NeedsResolver extends true ? OutputScalarConfig & { resolve: FieldResolver } : OutputScalarConfig export type FieldOutConfig = NeedsResolver< TypeName, FieldName > extends true ? NexusOutputFieldConfig & { resolve: FieldResolver } : NexusOutputFieldConfig export interface OutputDefinitionBuilder { typeName: string addField(config: NexusOutputFieldDef): void addDynamicOutputMembers(block: OutputDefinitionBlock, wrapping?: NexusWrapKind[]): void warn(msg: string): void } export interface InputDefinitionBuilder { typeName: string addField(config: NexusInputFieldDef): void addDynamicInputFields(block: InputDefinitionBlock, wrapping?: NexusWrapKind[]): void warn(msg: string): void } // prettier-ignore export interface OutputDefinitionBlock extends NexusGenCustomOutputMethods, NexusGenCustomOutputProperties {} /** * The output definition block is passed to the "definition" * function property of the "objectType" / "interfaceType" */ export class OutputDefinitionBlock { readonly typeName: string constructor(protected typeBuilder: OutputDefinitionBuilder, protected wrapping?: NexusWrapKind[]) { this.typeName = typeBuilder.typeName this.typeBuilder.addDynamicOutputMembers(this, this.wrapping) } get list() { return this._wrapClass('List') } get nonNull(): Omit, 'nonNull' | 'nullable'> { return this._wrapClass('NonNull') } get nullable(): Omit, 'nonNull' | 'nullable'> { return this._wrapClass('Null') } string(fieldName: FieldName, ...opts: ScalarOutSpread) { this.addScalarField(fieldName, 'String', opts) } int(fieldName: FieldName, ...opts: ScalarOutSpread) { this.addScalarField(fieldName, 'Int', opts) } boolean(fieldName: FieldName, ...opts: ScalarOutSpread) { this.addScalarField(fieldName, 'Boolean', opts) } id(fieldName: FieldName, ...opts: ScalarOutSpread) { this.addScalarField(fieldName, 'ID', opts) } float(fieldName: FieldName, ...opts: ScalarOutSpread) { this.addScalarField(fieldName, 'Float', opts) } field(name: FieldName, fieldConfig: FieldOutConfig) { this.typeBuilder.addField({ name, ...fieldConfig, configFor: 'outputField', wrapping: this.wrapping, parentType: this.typeName, } as any) } protected _wrapClass(kind: NexusWrapKind): OutputDefinitionBlock { const previousWrapping = this.wrapping?.[0] if ( (kind === 'NonNull' || kind === 'Null') && (previousWrapping === 'NonNull' || previousWrapping === 'Null') ) { return new OutputDefinitionBlock(this.typeBuilder, this.wrapping || []) } return new OutputDefinitionBlock(this.typeBuilder, [kind].concat(this.wrapping || [])) } protected addScalarField( fieldName: string, typeName: BaseScalars, opts: [] | ScalarOutSpread ) { let config: NexusOutputFieldDef = { name: fieldName, type: typeName, parentType: this.typeName, configFor: 'outputField', } /* istanbul ignore if */ if (typeof opts[0] === 'function') { config.resolve = opts[0] as any console.warn(messages.removedFunctionShorthand(typeName, fieldName)) } else { config = { ...config, ...opts[0] } } this.typeBuilder.addField({ ...config, wrapping: this.wrapping, }) } } export interface NexusInputFieldConfig extends CommonInputFieldConfig { type: AllInputTypes | AllNexusInputTypeDefs } export type NexusInputFieldDef = NexusInputFieldConfig & { configFor: 'inputField' name: string wrapping?: NexusWrapKind[] parentType: string } export interface InputDefinitionBlock extends NexusGenCustomInputMethods {} export class InputDefinitionBlock { readonly typeName: string constructor(protected typeBuilder: InputDefinitionBuilder, protected wrapping?: NexusWrapKind[]) { this.typeName = typeBuilder.typeName this.typeBuilder.addDynamicInputFields(this, this.wrapping) } get list() { return this._wrapClass('List') } get nonNull(): Omit, 'nonNull' | 'nullable'> { return this._wrapClass('NonNull') } get nullable(): Omit, 'nonNull' | 'nullable'> { return this._wrapClass('Null') } string(fieldName: FieldName, opts?: CommonInputFieldConfig) { this.addScalarField(fieldName, 'String', opts) } int(fieldName: FieldName, opts?: CommonInputFieldConfig) { this.addScalarField(fieldName, 'Int', opts) } boolean( fieldName: FieldName, opts?: CommonInputFieldConfig ) { this.addScalarField(fieldName, 'Boolean', opts) } id(fieldName: FieldName, opts?: CommonInputFieldConfig) { this.addScalarField(fieldName, 'ID', opts) } float(fieldName: FieldName, opts?: CommonInputFieldConfig) { this.addScalarField(fieldName, 'Float', opts) } field( fieldName: FieldName, fieldConfig: NexusInputFieldConfig ) { this.typeBuilder.addField({ name: fieldName, ...fieldConfig, wrapping: this.wrapping, parentType: this.typeName, configFor: 'inputField', }) } protected _wrapClass(kind: NexusWrapKind) { const previousWrapping = this.wrapping?.[0] if ( (kind === 'NonNull' || kind === 'Null') && (previousWrapping === 'NonNull' || previousWrapping === 'Null') ) { return new InputDefinitionBlock(this.typeBuilder, this.wrapping || []) } return new InputDefinitionBlock(this.typeBuilder, [kind].concat(this.wrapping || [])) } protected addScalarField( fieldName: string, typeName: BaseScalars, opts: CommonInputFieldConfig = {} ) { this.typeBuilder.addField({ name: fieldName, type: typeName, ...opts, wrapping: this.wrapping, parentType: this.typeName, configFor: 'inputField', }) } }