improve: remove resolver shorthands (#592)
closes #582 As discussed. In most cases resolver shorthands are not a serious use of the API. BREAKING CHANGE: Resolver shorthand API is now removed. The following will now not typecheck: ```ts t.string('foo', () => ... ) ``` Instead use: ```ts t.string('foo', { resolve: () => ... }) ``` Runtime support is still intact but will result in a logged warning. Runtime support will be removed in the next Nexus release.
This commit is contained in:
parent
b19e83ebfc
commit
f60938079a
|
|
@ -1,31 +1,33 @@
|
|||
import { objectType } from "@nexus/schema";
|
||||
import dedent from "dedent";
|
||||
import dedent from 'dedent'
|
||||
import { objectType } from '@nexus/schema'
|
||||
|
||||
export const Launch = objectType({
|
||||
name: "Launch",
|
||||
name: 'Launch',
|
||||
definition: (t) => {
|
||||
t.id("id");
|
||||
t.string("site", { nullable: true });
|
||||
t.field("mission", { type: "Mission" });
|
||||
t.field("rocket", { type: "Rocket" });
|
||||
t.boolean("isBooked", (launch, _, { dataSources }) => {
|
||||
return dataSources.userAPI.isBookedOnLaunch({
|
||||
launchId: `${launch.id}`,
|
||||
});
|
||||
});
|
||||
t.id('id')
|
||||
t.string('site', { nullable: true })
|
||||
t.field('mission', { type: 'Mission' })
|
||||
t.field('rocket', { type: 'Rocket' })
|
||||
t.boolean('isBooked', {
|
||||
resolve: (launch, _, { dataSources }) => {
|
||||
return dataSources.userAPI.isBookedOnLaunch({
|
||||
launchId: `${launch.id}`,
|
||||
})
|
||||
},
|
||||
})
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
export const LaunchConnection = objectType({
|
||||
name: "LaunchConnection",
|
||||
name: 'LaunchConnection',
|
||||
description: dedent`
|
||||
Simple wrapper around our list of launches that contains a cursor to the
|
||||
last item in the list. Pass this cursor to the launches query to fetch results
|
||||
after these.
|
||||
`,
|
||||
definition: (t) => {
|
||||
t.string("cursor", { nullable: true });
|
||||
t.boolean("hasMore");
|
||||
t.field("launches", { type: "Launch", list: [false] });
|
||||
t.string('cursor', { nullable: true })
|
||||
t.boolean('hasMore')
|
||||
t.field('launches', { type: 'Launch', list: [false] })
|
||||
},
|
||||
});
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"name": "@graphql-nexus/example-kitchen-sink",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "ts-node --log-error src/index.ts",
|
||||
"debug": "ts-node-dev --inspect-brk --no-notify --transpileOnly --respawn ./src",
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
import { connectionFromArray } from 'graphql-relay'
|
||||
import _ from 'lodash'
|
||||
import {
|
||||
objectType,
|
||||
inputObjectType,
|
||||
interfaceType,
|
||||
unionType,
|
||||
arg,
|
||||
booleanArg,
|
||||
connectionPlugin,
|
||||
extendType,
|
||||
scalarType,
|
||||
intArg,
|
||||
idArg,
|
||||
inputObjectType,
|
||||
intArg,
|
||||
interfaceType,
|
||||
mutationField,
|
||||
mutationType,
|
||||
booleanArg,
|
||||
objectType,
|
||||
queryField,
|
||||
connectionPlugin,
|
||||
scalarType,
|
||||
unionType,
|
||||
} from '@nexus/schema'
|
||||
import _ from 'lodash'
|
||||
import { connectionFromArray } from 'graphql-relay'
|
||||
|
||||
const USERS_DATA = _.times(100, (i) => ({
|
||||
pk: i,
|
||||
|
|
@ -33,7 +33,7 @@ export const testArgs2 = {
|
|||
|
||||
export const Mutation = mutationType({
|
||||
definition(t) {
|
||||
t.boolean('ok', () => true)
|
||||
t.boolean('ok', { resolve: () => true })
|
||||
},
|
||||
})
|
||||
|
||||
|
|
@ -194,7 +194,7 @@ export const Query = objectType({
|
|||
},
|
||||
resolve: () => 'ok',
|
||||
})
|
||||
t.list.date('dateAsList', () => [])
|
||||
t.list.date('dateAsList', { resolve: () => [] })
|
||||
|
||||
t.connectionField('booleanConnection', {
|
||||
type: 'Boolean',
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"name": "@graphql-nexus/ts-ast-reader",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"start": "ts-node-dev --no-notify --transpileOnly --respawn ./src"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,90 +1,90 @@
|
|||
import { interfaceType, arg } from "@nexus/schema";
|
||||
import { SyntaxKind, JSDoc } from "typescript";
|
||||
import { allKnownNodes, syntaxKindFilter } from "./utils";
|
||||
import { JSDoc, SyntaxKind } from 'typescript'
|
||||
import { arg, interfaceType } from '@nexus/schema'
|
||||
import { allKnownNodes, syntaxKindFilter } from './utils'
|
||||
|
||||
const syntaxKindArgs = {
|
||||
skip: arg({ type: "SyntaxKind", list: true }),
|
||||
only: arg({ type: "SyntaxKind", list: true }),
|
||||
};
|
||||
skip: arg({ type: 'SyntaxKind', list: true }),
|
||||
only: arg({ type: 'SyntaxKind', list: true }),
|
||||
}
|
||||
|
||||
export const MaybeOptional = interfaceType({
|
||||
name: "MaybeOptional",
|
||||
name: 'MaybeOptional',
|
||||
definition(t) {
|
||||
t.field("questionToken", { type: "Token", nullable: true });
|
||||
t.resolveType((o) => o.kind as any);
|
||||
t.field('questionToken', { type: 'Token', nullable: true })
|
||||
t.resolveType((o) => o.kind as any)
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
export const Node = interfaceType({
|
||||
name: "Node",
|
||||
name: 'Node',
|
||||
definition(t) {
|
||||
t.int("pos");
|
||||
t.int("end");
|
||||
t.string("nameText", {
|
||||
t.int('pos')
|
||||
t.int('end')
|
||||
t.string('nameText', {
|
||||
nullable: true,
|
||||
resolve: (root) =>
|
||||
// @ts-ignore
|
||||
root.name ? root.name.escapedText : null,
|
||||
});
|
||||
t.field("name", { type: "DeclarationName", nullable: true });
|
||||
t.field("typeName", { type: "DeclarationName", nullable: true });
|
||||
t.field("kind", { type: "SyntaxKind" });
|
||||
t.int("kindCode", (o) => o.kind);
|
||||
t.field("flags", { type: "NodeFlags" });
|
||||
})
|
||||
t.field('name', { type: 'DeclarationName', nullable: true })
|
||||
t.field('typeName', { type: 'DeclarationName', nullable: true })
|
||||
t.field('kind', { type: 'SyntaxKind' })
|
||||
t.int('kindCode', { resolve: (o) => o.kind })
|
||||
t.field('flags', { type: 'NodeFlags' })
|
||||
// t.field('decorators', 'Decorator', {list: true, nullable: true})
|
||||
t.list.field("modifiers", {
|
||||
type: "Token",
|
||||
t.list.field('modifiers', {
|
||||
type: 'Token',
|
||||
nullable: true,
|
||||
args: syntaxKindArgs,
|
||||
async resolve(root, args) {
|
||||
if (!root.modifiers) {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
return syntaxKindFilter(args, Array.from(root.modifiers));
|
||||
return syntaxKindFilter(args, Array.from(root.modifiers))
|
||||
},
|
||||
});
|
||||
t.field("parent", { type: "Node" });
|
||||
t.string("rawText", {
|
||||
})
|
||||
t.field('parent', { type: 'Node' })
|
||||
t.string('rawText', {
|
||||
args: syntaxKindArgs,
|
||||
resolve(root, args, ctx) {
|
||||
const filtered = syntaxKindFilter(args, [root]);
|
||||
return filtered.length ? filtered[0].getText(ctx.source) : "";
|
||||
const filtered = syntaxKindFilter(args, [root])
|
||||
return filtered.length ? filtered[0].getText(ctx.source) : ''
|
||||
},
|
||||
});
|
||||
})
|
||||
t.resolveType((node, ctx, info) => {
|
||||
if (KeywordKinds.has(node.kind)) {
|
||||
return "KeywordTypeNode";
|
||||
return 'KeywordTypeNode'
|
||||
}
|
||||
if (allKnownNodes(info.schema).has(SyntaxKind[node.kind])) {
|
||||
return SyntaxKind[node.kind] as any;
|
||||
return SyntaxKind[node.kind] as any
|
||||
}
|
||||
return "UNKNOWN_NODE";
|
||||
});
|
||||
return 'UNKNOWN_NODE'
|
||||
})
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
export const JSDocInterface = interfaceType({
|
||||
name: "HasJSDoc",
|
||||
name: 'HasJSDoc',
|
||||
definition(t) {
|
||||
t.list.field("jsDoc", {
|
||||
type: "JSDoc",
|
||||
t.list.field('jsDoc', {
|
||||
type: 'JSDoc',
|
||||
nullable: true,
|
||||
resolve(root) {
|
||||
if ("jsDoc" in root) {
|
||||
if ('jsDoc' in root) {
|
||||
// https://github.com/Microsoft/TypeScript/issues/19856
|
||||
return ((root as unknown) as { jsDoc: JSDoc[] }).jsDoc;
|
||||
return ((root as unknown) as { jsDoc: JSDoc[] }).jsDoc
|
||||
}
|
||||
return null;
|
||||
return null
|
||||
},
|
||||
});
|
||||
})
|
||||
t.resolveType((node, ctx, info) => {
|
||||
if (allKnownNodes(info.schema).has(SyntaxKind[node.kind])) {
|
||||
return SyntaxKind[node.kind] as any;
|
||||
return SyntaxKind[node.kind] as any
|
||||
}
|
||||
return "UNKNOWN_NODE";
|
||||
});
|
||||
return 'UNKNOWN_NODE'
|
||||
})
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
const KeywordKinds = new Set([
|
||||
SyntaxKind.AnyKeyword,
|
||||
|
|
@ -100,4 +100,4 @@ const KeywordKinds = new Set([
|
|||
SyntaxKind.UndefinedKeyword,
|
||||
SyntaxKind.NullKeyword,
|
||||
SyntaxKind.NeverKeyword,
|
||||
]);
|
||||
])
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { GraphQLFieldResolver } from 'graphql'
|
||||
import { AllInputTypes, FieldResolver, GetGen, GetGen3, HasGen3, NeedsResolver } from '../typegenTypeHelpers'
|
||||
import { BaseScalars } from './_types'
|
||||
import { ArgsRecord } from './args'
|
||||
import { AllNexusInputTypeDefs, AllNexusOutputTypeDefs } from './wrapping'
|
||||
import { BaseScalars } from './_types'
|
||||
|
||||
export interface CommonFieldConfig {
|
||||
/**
|
||||
|
|
@ -59,16 +59,14 @@ export type NexusOutputFieldDef = NexusOutputFieldConfig<string, any> & {
|
|||
// prettier-ignore
|
||||
export type ScalarOutSpread<TypeName extends string, FieldName extends string> =
|
||||
NeedsResolver<TypeName, FieldName> extends true
|
||||
? HasGen3<'argTypes', TypeName, FieldName> extends true
|
||||
? [ScalarOutConfig<TypeName, FieldName>]
|
||||
: [ScalarOutConfig<TypeName, FieldName>] | [FieldResolver<TypeName, FieldName>]
|
||||
? [ScalarOutConfig<TypeName, FieldName>]
|
||||
: HasGen3<'argTypes', TypeName, FieldName> extends true
|
||||
? [ScalarOutConfig<TypeName, FieldName>]
|
||||
: [] | [FieldResolver<TypeName, FieldName>] | [ScalarOutConfig<TypeName, FieldName>]
|
||||
: [ScalarOutConfig<TypeName, FieldName>] | []
|
||||
|
||||
// prettier-ignore
|
||||
export type ScalarOutConfig<TypeName extends string, FieldName extends string> =
|
||||
NeedsResolver<TypeName, FieldName> extends true
|
||||
NeedsResolver<TypeName, FieldName> extends true
|
||||
? OutputScalarConfig<TypeName, FieldName> &
|
||||
{
|
||||
resolve: FieldResolver<TypeName, FieldName>
|
||||
|
|
@ -160,12 +158,16 @@ export class OutputDefinitionBlock<TypeName extends string> {
|
|||
name: fieldName,
|
||||
type: typeName,
|
||||
}
|
||||
|
||||
if (typeof opts[0] === 'function') {
|
||||
// FIXME ditto to the one in `field` method
|
||||
config.resolve = opts[0] as any
|
||||
console.warn(
|
||||
`Since v0.18.0 Nexus no longer supports resolver shorthands like:\n\n t.string("${fieldName}", () => ...).\n\nInstead please write:\n\n t.string("${fieldName}", { resolve: () => ... })\n\nIn the next version of Nexus this will be a runtime error.`
|
||||
)
|
||||
} else {
|
||||
config = { ...config, ...opts[0] }
|
||||
}
|
||||
|
||||
this.typeBuilder.addField(this.decorateField(config))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ import { inputObjectType, objectType } from '../../src'
|
|||
export const UserObject = objectType({
|
||||
name: 'User',
|
||||
definition(t) {
|
||||
t.id('id', () => `User:1` as any)
|
||||
t.string('email', () => 'test@example.com' as any)
|
||||
t.string('name', () => `Test User` as any)
|
||||
t.id('id', { resolve: () => `User:1` as any })
|
||||
t.string('email', { resolve: () => 'test@example.com' as any })
|
||||
t.string('name', { resolve: () => `Test User` as any })
|
||||
},
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import * as path from 'path'
|
||||
import { core, enumType, makeSchema, objectType, queryType } from '..'
|
||||
import { core, enumType, makeSchema, objectType, queryType } from '../'
|
||||
import { A, B } from './_types'
|
||||
|
||||
const { TypegenPrinter, TypegenMetadata } = core
|
||||
|
|
@ -121,7 +121,7 @@ describe('rootTypings', () => {
|
|||
name: 'SomeType',
|
||||
rootTyping: {
|
||||
name: 'invalid',
|
||||
path: 'fzeffezpokm',
|
||||
path: './fzeffezpokm',
|
||||
},
|
||||
definition(t) {
|
||||
t.id('id')
|
||||
|
|
@ -140,7 +140,7 @@ describe('rootTypings', () => {
|
|||
})
|
||||
|
||||
expect(() => typegen.print()).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Expected an absolute path for the root typing path of the type SomeType, saw fzeffezpokm"`
|
||||
`"Expected an absolute path for the root typing path of the type SomeType, saw ./fzeffezpokm"`
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -174,7 +174,7 @@ describe('rootTypings', () => {
|
|||
typegen.print()
|
||||
} catch (e) {
|
||||
expect(e.message.replace(__dirname, '')).toMatchInlineSnapshot(
|
||||
`"Root typing path /invalid_path.ts of the type SomeType does not exist"`
|
||||
`"Root typing path /invalid_path.ts for the type SomeType does not exist"`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import { mockStream } from '../../__helpers'
|
|||
|
||||
export const query = queryType({
|
||||
definition(t) {
|
||||
t.string('foo', () => 'bar')
|
||||
t.string('foo', { resolve: () => 'bar' })
|
||||
},
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -640,7 +640,7 @@ describe('field level configuration', () => {
|
|||
{},
|
||||
{
|
||||
extendConnection(t) {
|
||||
t.int('totalCount', () => 1)
|
||||
t.int('totalCount', { resolve: () => 1 })
|
||||
},
|
||||
}
|
||||
)
|
||||
|
|
@ -653,7 +653,7 @@ describe('field level configuration', () => {
|
|||
{},
|
||||
{
|
||||
extendEdge(t) {
|
||||
t.string('role', () => 'admin')
|
||||
t.string('role', { resolve: () => 'admin' })
|
||||
},
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ describe('unionType', () => {
|
|||
objectType({
|
||||
name: 'DeletedUser',
|
||||
definition(t) {
|
||||
t.string('message', (root) => `This user ${root.id} was deleted`)
|
||||
t.string('message', { resolve: (root) => `This user ${root.id} was deleted` })
|
||||
},
|
||||
rootTyping: `{ id: number; deletedAt: Date }`,
|
||||
}),
|
||||
|
|
|
|||
Loading…
Reference in New Issue