diff --git a/src/builder.ts b/src/builder.ts index ee84aa46..30eac51d 100644 --- a/src/builder.ts +++ b/src/builder.ts @@ -420,7 +420,7 @@ export class SchemaBuilder { constructor(protected config: BuilderConfig) { this.nonNullDefaults = { input: false, - output: true, + output: false, ...config.nonNullDefaults, } this.plugins = config.plugins || [fieldAuthorizePlugin()] @@ -1147,7 +1147,7 @@ export class SchemaBuilder { protected decorateList(type: T, list: true | boolean[]): T { let finalType = type if (!Array.isArray(list)) { - return GraphQLList(GraphQLNonNull(type)) as T + return GraphQLList(type) as T } if (Array.isArray(list)) { for (let i = 0; i < list.length; i++) { diff --git a/src/definitions/_types.ts b/src/definitions/_types.ts index d7b8ccf2..e90c2d4b 100644 --- a/src/definitions/_types.ts +++ b/src/definitions/_types.ts @@ -72,7 +72,7 @@ export interface NonNullConfig { * otherField: [String!]! * } * - * @default true + * @default false */ output?: boolean /** diff --git a/src/plugins/nullabilityGuardPlugin.ts b/src/plugins/nullabilityGuardPlugin.ts index ccc4546e..b8434daf 100644 --- a/src/plugins/nullabilityGuardPlugin.ts +++ b/src/plugins/nullabilityGuardPlugin.ts @@ -59,7 +59,7 @@ const fieldDefTypes = printedGenTyping({ optional: true, type: 'boolean', description: ` - The nullability guard can be helpful, but is also a pottentially expensive operation for lists. + The nullability guard can be helpful, but is also a potentially expensive operation for lists. We need to iterate the entire list to check for null items to guard against. Set this to true to skip the null guard on a specific field if you know there's no potential for unsafe types. `, diff --git a/tests/__snapshots__/definitions.spec.ts.snap b/tests/__snapshots__/definitions.spec.ts.snap index cb5e7a52..0082bfed 100644 --- a/tests/__snapshots__/definitions.spec.ts.snap +++ b/tests/__snapshots__/definitions.spec.ts.snap @@ -16,6 +16,6 @@ Array [ exports[`inputObjectType should output lists properly, #33 1`] = ` "input AddToBasketInput { - extras: [ExtraBasketInput!] + extras: [ExtraBasketInput] }" `; diff --git a/tests/__snapshots__/makeSchema.spec.ts.snap b/tests/__snapshots__/makeSchema.spec.ts.snap index ae44219d..07fc5111 100644 --- a/tests/__snapshots__/makeSchema.spec.ts.snap +++ b/tests/__snapshots__/makeSchema.spec.ts.snap @@ -7,7 +7,7 @@ exports[`makeSchema shouldExitAfterGenerateArtifacts accepts a customPrintSchema type Query { # Example boolean field - ok: Boolean! + ok: Boolean } " `; diff --git a/tests/__snapshots__/plugin.spec.ts.snap b/tests/__snapshots__/plugin.spec.ts.snap index 770cbb80..acb59aaa 100644 --- a/tests/__snapshots__/plugin.spec.ts.snap +++ b/tests/__snapshots__/plugin.spec.ts.snap @@ -26,26 +26,26 @@ Array [ exports[`plugin has an onMissingType, which will be called in order when we encounter a missing type 1`] = ` "type User { - id: ID! + id: ID } type Query { - users: UserConnection! + users: UserConnection } type UserConnection { - edges: [UserEdge!]! - connectionInfo: ConnectionInfo! + edges: [UserEdge] + connectionInfo: ConnectionInfo } type UserEdge { - cursor: String! - node: User! + cursor: String + node: User } type ConnectionInfo { - hasNextPage: Boolean! - hasPrevPage: Boolean! + hasNextPage: Boolean + hasPrevPage: Boolean } " `; diff --git a/tests/__snapshots__/plugins.spec.ts.snap b/tests/__snapshots__/plugins.spec.ts.snap index 442bc267..dd1044e7 100644 --- a/tests/__snapshots__/plugins.spec.ts.snap +++ b/tests/__snapshots__/plugins.spec.ts.snap @@ -2,7 +2,7 @@ exports[`onInstall plugins does not have access to inline types 1`] = ` "type Query { - bar(inline: Inline): String! + bar(inline: Inline): String } input Inline { @@ -20,7 +20,7 @@ exports[`onInstall plugins does not see fallback ok-query 1`] = ` exports[`onInstall plugins has access to top-level types 1`] = ` "type foo { - bar: String! + bar: String } type Query { @@ -31,7 +31,7 @@ type Query { exports[`onInstall plugins may contribute types 1`] = ` "type Query { - something: String! + something: String } " `; diff --git a/tests/definitions.spec.ts b/tests/definitions.spec.ts index b4500e29..eab00046 100644 --- a/tests/definitions.spec.ts +++ b/tests/definitions.spec.ts @@ -142,7 +142,7 @@ describe('objectType', () => { const typeMap = buildTypes<{ Account: GraphQLObjectType }>([Account]) const fields = typeMap.Account.getFields() expect(Object.keys(fields).sort()).toEqual(['email', 'id', 'name', 'nestedList']) - expect(fields.nestedList.type.toString()).toEqual('[[String]!]!') + expect(fields.nestedList.type.toString()).toEqual('[[String]!]') }) }) diff --git a/tests/integration/_app.typegen.ts b/tests/integration/_app.typegen.ts index 03ad6583..817c39fd 100644 --- a/tests/integration/_app.typegen.ts +++ b/tests/integration/_app.typegen.ts @@ -3,20 +3,20 @@ * Do not make changes to this file directly */ -import { core } from "../.."; +import { core } from '../..' declare global { interface NexusGenCustomInputMethods { - title(...args: any): void; + title(...args: any): void } } declare global { interface NexusGenCustomOutputMethods { - title(options: { escape: boolean }): void; + title(options: { escape: boolean }): void } } declare global { interface NexusGenCustomOutputProperties { - body: any; + body: any } } @@ -27,136 +27,128 @@ declare global { export interface NexusGenInputs { PostSearchInput: { // input type - body?: string | null; // String - title?: string | null; // String - }; + body?: string | null // String + title?: string | null // String + } } export interface NexusGenEnums {} export interface NexusGenScalars { - String: string; - Int: number; - Float: number; - Boolean: boolean; - ID: string; + String: string + Int: number + Float: number + Boolean: boolean + ID: string } export interface NexusGenRootTypes { - Mutation: {}; + Mutation: {} Post: { // root type - body: string; // String! - title: string; // String! - }; - Query: {}; - User: { firstName: string; lastName: string }; + body?: string | null // String + title?: string | null // String + } + Query: {} + User: { firstName: string; lastName: string } } export interface NexusGenAllTypes extends NexusGenRootTypes { - PostSearchInput: NexusGenInputs["PostSearchInput"]; - String: NexusGenScalars["String"]; - Int: NexusGenScalars["Int"]; - Float: NexusGenScalars["Float"]; - Boolean: NexusGenScalars["Boolean"]; - ID: NexusGenScalars["ID"]; + PostSearchInput: NexusGenInputs['PostSearchInput'] + String: NexusGenScalars['String'] + Int: NexusGenScalars['Int'] + Float: NexusGenScalars['Float'] + Boolean: NexusGenScalars['Boolean'] + ID: NexusGenScalars['ID'] } export interface NexusGenFieldTypes { Mutation: { // field return type - createUser: NexusGenRootTypes["User"]; // User! - }; + createUser: NexusGenRootTypes['User'] | null // User + } Post: { // field return type - body: string; // String! - title: string; // String! - }; + body: string | null // String + title: string | null // String + } Query: { // field return type - foo: string; // String! - searchPosts: NexusGenRootTypes["Post"][]; // [Post!]! - user: NexusGenRootTypes["User"]; // User! - }; + foo: string | null // String + searchPosts: Array | null // [Post] + user: NexusGenRootTypes['User'] | null // User + } User: { // field return type - firstName: string; // String! - lastName: string; // String! - }; + firstName: string | null // String + lastName: string | null // String + } } export interface NexusGenArgTypes { Mutation: { createUser: { // args - firstName?: string | null; // String - lastName?: string | null; // String - }; - }; + firstName?: string | null // String + lastName?: string | null // String + } + } Query: { searchPosts: { // args - input?: NexusGenInputs["PostSearchInput"] | null; // PostSearchInput - }; + input?: NexusGenInputs['PostSearchInput'] | null // PostSearchInput + } user: { // args - id?: string | null; // ID - }; - }; + id?: string | null // ID + } + } } export interface NexusGenAbstractResolveReturnTypes {} export interface NexusGenInheritedFields {} -export type NexusGenObjectNames = "Mutation" | "Post" | "Query" | "User"; +export type NexusGenObjectNames = 'Mutation' | 'Post' | 'Query' | 'User' -export type NexusGenInputNames = "PostSearchInput"; +export type NexusGenInputNames = 'PostSearchInput' -export type NexusGenEnumNames = never; +export type NexusGenEnumNames = never -export type NexusGenInterfaceNames = never; +export type NexusGenInterfaceNames = never -export type NexusGenScalarNames = "Boolean" | "Float" | "ID" | "Int" | "String"; +export type NexusGenScalarNames = 'Boolean' | 'Float' | 'ID' | 'Int' | 'String' -export type NexusGenUnionNames = never; +export type NexusGenUnionNames = never export interface NexusGenTypes { - context: any; - inputTypes: NexusGenInputs; - rootTypes: NexusGenRootTypes; - argTypes: NexusGenArgTypes; - fieldTypes: NexusGenFieldTypes; - allTypes: NexusGenAllTypes; - inheritedFields: NexusGenInheritedFields; - objectNames: NexusGenObjectNames; - inputNames: NexusGenInputNames; - enumNames: NexusGenEnumNames; - interfaceNames: NexusGenInterfaceNames; - scalarNames: NexusGenScalarNames; - unionNames: NexusGenUnionNames; - allInputTypes: - | NexusGenTypes["inputNames"] - | NexusGenTypes["enumNames"] - | NexusGenTypes["scalarNames"]; + context: any + inputTypes: NexusGenInputs + rootTypes: NexusGenRootTypes + argTypes: NexusGenArgTypes + fieldTypes: NexusGenFieldTypes + allTypes: NexusGenAllTypes + inheritedFields: NexusGenInheritedFields + objectNames: NexusGenObjectNames + inputNames: NexusGenInputNames + enumNames: NexusGenEnumNames + interfaceNames: NexusGenInterfaceNames + scalarNames: NexusGenScalarNames + unionNames: NexusGenUnionNames + allInputTypes: NexusGenTypes['inputNames'] | NexusGenTypes['enumNames'] | NexusGenTypes['scalarNames'] allOutputTypes: - | NexusGenTypes["objectNames"] - | NexusGenTypes["enumNames"] - | NexusGenTypes["unionNames"] - | NexusGenTypes["interfaceNames"] - | NexusGenTypes["scalarNames"]; - allNamedTypes: - | NexusGenTypes["allInputTypes"] - | NexusGenTypes["allOutputTypes"]; - abstractTypes: NexusGenTypes["interfaceNames"] | NexusGenTypes["unionNames"]; - abstractResolveReturn: NexusGenAbstractResolveReturnTypes; + | NexusGenTypes['objectNames'] + | NexusGenTypes['enumNames'] + | NexusGenTypes['unionNames'] + | NexusGenTypes['interfaceNames'] + | NexusGenTypes['scalarNames'] + allNamedTypes: NexusGenTypes['allInputTypes'] | NexusGenTypes['allOutputTypes'] + abstractTypes: NexusGenTypes['interfaceNames'] | NexusGenTypes['unionNames'] + abstractResolveReturn: NexusGenAbstractResolveReturnTypes } declare global { interface NexusGenPluginTypeConfig {} - interface NexusGenPluginFieldConfig< - TypeName extends string, - FieldName extends string - > {} + interface NexusGenPluginFieldConfig {} interface NexusGenPluginSchemaConfig {} } diff --git a/tests/plugins/__snapshots__/fieldAuthorizePlugin.spec.ts.snap b/tests/plugins/__snapshots__/fieldAuthorizePlugin.spec.ts.snap index a077b369..5f8b9639 100644 --- a/tests/plugins/__snapshots__/fieldAuthorizePlugin.spec.ts.snap +++ b/tests/plugins/__snapshots__/fieldAuthorizePlugin.spec.ts.snap @@ -86,7 +86,7 @@ export interface NexusGenAllTypes extends NexusGenRootTypes { export interface NexusGenFieldTypes { Query: { // field return type - ok: boolean; // Boolean! + ok: boolean | null; // Boolean } } @@ -159,6 +159,6 @@ Array [ exports[`fieldAuthorizePlugin warns when a field has a non-function authorize prop 1`] = ` Array [ - [Error: The authorize property provided to incorrectFieldConfig with type User! should be a function, saw number], + [Error: The authorize property provided to incorrectFieldConfig with type User should be a function, saw number], ] `; diff --git a/tests/plugins/__snapshots__/queryComplexityPlugin.spec.ts.snap b/tests/plugins/__snapshots__/queryComplexityPlugin.spec.ts.snap index 0ff67514..45011a17 100644 --- a/tests/plugins/__snapshots__/queryComplexityPlugin.spec.ts.snap +++ b/tests/plugins/__snapshots__/queryComplexityPlugin.spec.ts.snap @@ -44,7 +44,7 @@ export interface NexusGenAllTypes extends NexusGenRootTypes { export interface NexusGenFieldTypes { Query: { // field return type - ok: boolean; // Boolean! + ok: boolean | null; // Boolean } } diff --git a/tests/plugins/fieldAuthorizePlugin.spec.ts b/tests/plugins/fieldAuthorizePlugin.spec.ts index 9f0930e6..09a43f67 100644 --- a/tests/plugins/fieldAuthorizePlugin.spec.ts +++ b/tests/plugins/fieldAuthorizePlugin.spec.ts @@ -72,6 +72,9 @@ describe('fieldAuthorizePlugin', () => { const testSchema = makeSchema({ outputs: false, types: schemaTypes, + nonNullDefaults: { + output: true, + }, plugins: [ fieldAuthorizePlugin({ formatError({ error, root, args, ctx, info }) { @@ -169,6 +172,9 @@ describe('fieldAuthorizePlugin', () => { const schema = makeSchema({ outputs: false, types: schemaTypes, + nonNullDefaults: { + output: true, + }, plugins: [fieldAuthorizePlugin()], }) const { data, errors = [] } = await testField('userFalse', false, schema) @@ -181,6 +187,9 @@ describe('fieldAuthorizePlugin', () => { test('always throws an error, even when formatError is wrong', async () => { const schema = makeSchema({ outputs: false, + nonNullDefaults: { + output: true, + }, types: schemaTypes, plugins: [ // @ts-ignore @@ -222,6 +231,9 @@ describe('fieldAuthorizePlugin', () => { test('warns and adds the authorize plugin when a schema has an authorize prop but no plugins', async () => { const schema = makeSchema({ outputs: false, + nonNullDefaults: { + output: true, + }, types: [ schemaTypes, queryField('shouldWarn', { diff --git a/tests/plugins/nullabilityGuardPlugin.spec.ts b/tests/plugins/nullabilityGuardPlugin.spec.ts index 60e891d8..236623e1 100644 --- a/tests/plugins/nullabilityGuardPlugin.spec.ts +++ b/tests/plugins/nullabilityGuardPlugin.spec.ts @@ -92,12 +92,12 @@ const types = [ }), queryField('intList', { type: 'Int', - list: true, + list: [true], resolve: () => [1, 2, null], }), queryField('userList', { type: 'User', - list: true, + list: [true], resolve: () => [null, Promise.resolve(null), null], }), queryField('interfaceType', { @@ -122,6 +122,9 @@ const defaultSchema = makeSchema({ types, plugins: [nullPlugin()], outputs: false, + nonNullDefaults: { + output: true, + }, }) describe('nullabilityGuardPlugin', () => { @@ -230,6 +233,9 @@ describe('nullabilityGuardPlugin', () => { const { errors = [], data } = await graphql( makeSchema({ outputs: false, + nonNullDefaults: { + output: true, + }, types: [ types, unionType({ @@ -290,6 +296,9 @@ describe('nullabilityGuardPlugin', () => { const warnSpy = jest.spyOn(console, 'warn').mockImplementationOnce(() => {}) const schema = makeSchema({ outputs: false, + nonNullDefaults: { + output: true, + }, types, plugins: [nullPlugin({ onGuarded: undefined })], }) @@ -312,6 +321,9 @@ describe('nullabilityGuardPlugin', () => { it('should not catch by default unless the env is production', async () => { const schema = makeSchema({ types, + nonNullDefaults: { + output: true, + }, plugins: [nullPlugin({ shouldGuard: undefined })], }) const { errors = [], data } = await graphql( @@ -330,6 +342,9 @@ describe('nullabilityGuardPlugin', () => { process.env.NODE_ENV = 'production' const schema2 = makeSchema({ types, + nonNullDefaults: { + output: true, + }, plugins: [nullPlugin({ shouldGuard: undefined })], }) const { errors: errors2 = [], data: data2 } = await graphql( @@ -402,7 +417,7 @@ describe('nullabilityGuardPlugin', () => { types: [ queryField('nullableList', { type: 'String', - list: true, + list: [true], nullable: true, resolve: async () => null, }), diff --git a/tests/subscriptionField.spec.ts b/tests/subscriptionField.spec.ts index 8b1c0cdd..e859f463 100644 --- a/tests/subscriptionField.spec.ts +++ b/tests/subscriptionField.spec.ts @@ -22,7 +22,7 @@ it('defines a field on the mutation type as shorthand', async () => { } type Subscription { - someField: Int! + someField: Int } " `) @@ -77,7 +77,7 @@ it('can be defined as a thunk', async () => { } type Subscription { - someField: Int! + someField: Int } " `) diff --git a/tests/subscriptionType.spec.ts b/tests/subscriptionType.spec.ts index 6f99dd29..b5084140 100644 --- a/tests/subscriptionType.spec.ts +++ b/tests/subscriptionType.spec.ts @@ -64,12 +64,12 @@ it('defines a field on the mutation type as shorthand', async () => { expect(GQL.printSchema(schema)).toMatchInlineSnapshot(` "type Subscription { - someField: Int! - someInt: Int! - someString: String! - someFloat: Float! - someBoolean: Boolean! - someID: ID! + someField: Int + someInt: Int + someString: String + someFloat: Float + someBoolean: Boolean + someID: ID } type Query {