feat: allow asNexusMethod to specify TS type (#473)

Co-authored-by: Jason Kuhrt <jasonkuhrt@me.com>

- Create a new `NexusGenScalars` for all scalar types
- Remove scalars from `NexusGenRootTypes` (might be a breaking change, need review)
- Use the `NexusGenScalars` for all non specified (base GraphQL) scalars in `input` and `output` types
- Add `rootTyping` parameter to `asNexusMethod` to allow the user to specify it, otherwise it will fallback to the `backingTypeMap`

BREAKING CHANGE:

The global TS type `NexusGenRootTypes` no longer contains scalars. All scalars now live under a new global TS type named `NexusGenScalars`.

```ts
// before
let foo: NexusGenRootTypes['String']
```
```ts
// after
let foo: NexusGenScalars['String']
```
This commit is contained in:
Émile Fugulin 2020-08-04 09:50:09 -04:00 committed by GitHub
parent 61eccca200
commit 1d97b788b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 146 additions and 71 deletions

View File

@ -52,8 +52,13 @@ export function scalarType<TypeName extends string>(options: NexusScalarTypeConf
return new NexusScalarTypeDef(options.name, options)
}
export function asNexusMethod<T extends GraphQLScalarType>(scalar: T, methodName: string): T {
export function asNexusMethod<T extends GraphQLScalarType>(
scalar: T,
methodName: string,
rootTyping?: RootTypingDef
): T {
return decorateType(scalar, {
asNexusMethod: methodName,
rootTyping: rootTyping,
})
}

View File

@ -81,6 +81,7 @@ export class TypegenPrinter {
const body = [
this.printInputTypeMap(),
this.printEnumTypeMap(),
this.printScalarTypeMap(),
this.printRootTypeMap(),
this.printAllTypesMap(),
this.printReturnTypeMap(),
@ -340,6 +341,23 @@ export class TypegenPrinter {
return inputObjMap
}
buildScalarTypeMap() {
const scalarMap: TypeMapping = {}
this.groupedTypes.scalar.forEach((e) => {
if (isSpecifiedScalarType(e)) {
scalarMap[e.name] = SpecifiedScalars[e.name as SpecifiedScalarNames]
return
}
const backingType = this.resolveBackingType(e.name)
if (backingType) {
scalarMap[e.name] = backingType
} else {
scalarMap[e.name] = 'any'
}
})
return scalarMap
}
printInputTypeMap() {
return this.printTypeFieldInterface('NexusGenInputs', this.buildInputTypeMap(), 'input type')
}
@ -348,13 +366,16 @@ export class TypegenPrinter {
return this.printTypeInterface('NexusGenEnums', this.buildEnumTypeMap())
}
printScalarTypeMap() {
return this.printTypeInterface('NexusGenScalars', this.buildScalarTypeMap())
}
buildRootTypeMap() {
const rootTypeMap: RootTypeMapping = {}
const hasFields: (GraphQLInterfaceType | GraphQLObjectType | GraphQLScalarType | GraphQLUnionType)[] = []
const hasFields: (GraphQLInterfaceType | GraphQLObjectType | GraphQLUnionType)[] = []
hasFields
.concat(this.groupedTypes.object)
.concat(this.groupedTypes.interface)
.concat(this.groupedTypes.scalar)
.concat(this.groupedTypes.union)
.forEach((type) => {
const rootTyping = this.resolveBackingType(type.name)
@ -362,13 +383,7 @@ export class TypegenPrinter {
rootTypeMap[type.name] = rootTyping
return
}
if (isScalarType(type)) {
if (isSpecifiedScalarType(type)) {
rootTypeMap[type.name] = SpecifiedScalars[type.name as SpecifiedScalarNames]
} else {
rootTypeMap[type.name] = 'any'
}
} else if (isUnionType(type)) {
if (isUnionType(type)) {
rootTypeMap[type.name] = type
.getTypes()
.map((t) => `NexusGenRootTypes['${t.name}']`)
@ -411,15 +426,18 @@ export class TypegenPrinter {
buildAllTypesMap() {
const typeMap: TypeMapping = {}
const toCheck: (GraphQLInputObjectType | GraphQLEnumType)[] = []
const toCheck: (GraphQLInputObjectType | GraphQLEnumType | GraphQLScalarType)[] = []
toCheck
.concat(this.groupedTypes.input)
.concat(this.groupedTypes.enum)
.concat(this.groupedTypes.scalar)
.forEach((type) => {
if (isInputObjectType(type)) {
typeMap[type.name] = `NexusGenInputs['${type.name}']`
} else if (isEnumType(type)) {
typeMap[type.name] = `NexusGenEnums['${type.name}']`
} else if (isScalarType(type)) {
typeMap[type.name] = `NexusGenScalars['${type.name}']`
}
})
return typeMap
@ -642,12 +660,7 @@ export class TypegenPrinter {
if (isSpecifiedScalarType(type)) {
return SpecifiedScalars[type.name as SpecifiedScalarNames]
}
const backingType = (this.typegenInfo.backingTypeMap as any)[type.name]
if (typeof backingType === 'string') {
return backingType
} else {
return 'any'
}
return `NexusGenScalars['${type.name}']`
}
printPlugins() {

View File

@ -35,17 +35,25 @@ export interface NexusGenEnums {
TestEnumType: TestEnum
}
export interface NexusGenScalars {
String: string
Int: number
Float: number
Boolean: boolean
ID: string
}
export interface NexusGenRootTypes {
Query: {};
String: string;
Int: number;
Float: number;
Boolean: boolean;
ID: string;
}
export interface NexusGenAllTypes extends NexusGenRootTypes {
TestEnumType: NexusGenEnums['TestEnumType'];
String: NexusGenScalars['String'];
Int: NexusGenScalars['Int'];
Float: NexusGenScalars['Float'];
Boolean: NexusGenScalars['Boolean'];
ID: NexusGenScalars['ID'];
}
export interface NexusGenFieldTypes {

View File

@ -28,7 +28,7 @@ exports[`typegenPrinter should build an argument type map 1`] = `
input: NexusGenInputs['CreatePostInput']; // CreatePostInput!
}
registerClick: { // args
uuid?: string | null; // UUID
uuid?: NexusGenScalars['UUID'] | null; // UUID
}
someList: { // args
items: Array<string | null>; // [String]!
@ -58,7 +58,7 @@ exports[`typegenPrinter should not print roots for fields with resolvers 1`] = `
geo: number[][]; // [[Float!]!]!
id: string; // ID!
messyGeo?: Array<number[] | null> | null; // [[Float!]]
uuid: string; // UUID!
uuid: NexusGenScalars['UUID']; // UUID!
}
Query: {};
User: { // root type
@ -69,12 +69,6 @@ exports[`typegenPrinter should not print roots for fields with resolvers 1`] = `
phone?: string | null; // String
}
Node: NexusGenRootTypes['Post'] | NexusGenRootTypes['User'];
String: string;
Int: number;
Float: number;
Boolean: boolean;
ID: string;
UUID: string;
ExampleUnion: NexusGenRootTypes['Post'] | NexusGenRootTypes['User'];
}"
`;
@ -91,7 +85,7 @@ exports[`typegenPrinter should print a return type map 1`] = `
geo: number[][]; // [[Float!]!]!
id: string; // ID!
messyGeo: Array<number[] | null> | null; // [[Float!]]
uuid: string; // UUID!
uuid: NexusGenScalars['UUID']; // UUID!
}
Query: { // field return type
posts: NexusGenRootTypes['Post'][]; // [Post!]!
@ -120,7 +114,7 @@ exports[`typegenPrinter should print a root type map 1`] = `
geo: number[][]; // [[Float!]!]!
id: string; // ID!
messyGeo?: Array<number[] | null> | null; // [[Float!]]
uuid: string; // UUID!
uuid: NexusGenScalars['UUID']; // UUID!
}
Query: {};
User: { // root type
@ -132,12 +126,6 @@ exports[`typegenPrinter should print a root type map 1`] = `
posts: NexusGenRootTypes['Post'][]; // [Post!]!
}
Node: NexusGenRootTypes['Post'] | NexusGenRootTypes['User'];
String: string;
Int: number;
Float: number;
Boolean: boolean;
ID: string;
UUID: string;
ExampleUnion: NexusGenRootTypes['Post'] | NexusGenRootTypes['User'];
}"
`;
@ -175,6 +163,15 @@ export interface NexusGenEnums {
SomeEnum: \\"A\\" | \\"B\\"
}
export interface NexusGenScalars {
String: string
Int: number
Float: number
Boolean: boolean
ID: string
UUID: string
}
export interface NexusGenRootTypes {
Mutation: {};
Post: { // root type
@ -182,7 +179,7 @@ export interface NexusGenRootTypes {
geo: number[][]; // [[Float!]!]!
id: string; // ID!
messyGeo?: Array<number[] | null> | null; // [[Float!]]
uuid: string; // UUID!
uuid: NexusGenScalars['UUID']; // UUID!
}
Query: {};
User: { // root type
@ -194,12 +191,6 @@ export interface NexusGenRootTypes {
posts: NexusGenRootTypes['Post'][]; // [Post!]!
}
Node: NexusGenRootTypes['Post'] | NexusGenRootTypes['User'];
String: string;
Int: number;
Float: number;
Boolean: boolean;
ID: string;
UUID: string;
ExampleUnion: NexusGenRootTypes['Post'] | NexusGenRootTypes['User'];
}
@ -208,6 +199,12 @@ export interface NexusGenAllTypes extends NexusGenRootTypes {
PostFilters: NexusGenInputs['PostFilters'];
OrderEnum: NexusGenEnums['OrderEnum'];
SomeEnum: NexusGenEnums['SomeEnum'];
String: NexusGenScalars['String'];
Int: NexusGenScalars['Int'];
Float: NexusGenScalars['Float'];
Boolean: NexusGenScalars['Boolean'];
ID: NexusGenScalars['ID'];
UUID: NexusGenScalars['UUID'];
}
export interface NexusGenFieldTypes {
@ -221,7 +218,7 @@ export interface NexusGenFieldTypes {
geo: number[][]; // [[Float!]!]!
id: string; // ID!
messyGeo: Array<number[] | null> | null; // [[Float!]]
uuid: string; // UUID!
uuid: NexusGenScalars['UUID']; // UUID!
}
Query: { // field return type
posts: NexusGenRootTypes['Post'][]; // [Post!]!
@ -247,7 +244,7 @@ export interface NexusGenArgTypes {
input: NexusGenInputs['CreatePostInput']; // CreatePostInput!
}
registerClick: { // args
uuid?: string | null; // UUID
uuid?: NexusGenScalars['UUID'] | null; // UUID
}
someList: { // args
items: Array<string | null>; // [String]!

View File

@ -34,6 +34,14 @@ export interface NexusGenInputs {
export interface NexusGenEnums {}
export interface NexusGenScalars {
String: string;
Int: number;
Float: number;
Boolean: boolean;
ID: string;
}
export interface NexusGenRootTypes {
Mutation: {};
Post: {
@ -43,15 +51,15 @@ export interface NexusGenRootTypes {
};
Query: {};
User: { firstName: string; lastName: string };
String: string;
Int: number;
Float: number;
Boolean: boolean;
ID: string;
}
export interface NexusGenAllTypes extends NexusGenRootTypes {
PostSearchInput: NexusGenInputs["PostSearchInput"];
String: NexusGenScalars["String"];
Int: NexusGenScalars["Int"];
Float: NexusGenScalars["Float"];
Boolean: NexusGenScalars["Boolean"];
ID: NexusGenScalars["ID"];
}
export interface NexusGenFieldTypes {

View File

@ -64,16 +64,24 @@ export interface NexusGenInputs {
export interface NexusGenEnums {
}
export interface NexusGenScalars {
String: string
Int: number
Float: number
Boolean: boolean
ID: string
}
export interface NexusGenRootTypes {
Query: {};
String: string;
Int: number;
Float: number;
Boolean: boolean;
ID: string;
}
export interface NexusGenAllTypes extends NexusGenRootTypes {
String: NexusGenScalars['String'];
Int: NexusGenScalars['Int'];
Float: NexusGenScalars['Float'];
Boolean: NexusGenScalars['Boolean'];
ID: NexusGenScalars['ID'];
}
export interface NexusGenFieldTypes {

View File

@ -22,16 +22,24 @@ export interface NexusGenInputs {
export interface NexusGenEnums {
}
export interface NexusGenScalars {
String: string
Int: number
Float: number
Boolean: boolean
ID: string
}
export interface NexusGenRootTypes {
Query: {};
String: string;
Int: number;
Float: number;
Boolean: boolean;
ID: string;
}
export interface NexusGenAllTypes extends NexusGenRootTypes {
String: NexusGenScalars['String'];
Int: NexusGenScalars['Int'];
Float: NexusGenScalars['Float'];
Boolean: NexusGenScalars['Boolean'];
ID: NexusGenScalars['ID'];
}
export interface NexusGenFieldTypes {

View File

@ -1,8 +1,27 @@
import { graphql } from 'graphql'
import { GraphQLDate, GraphQLDateTime } from 'graphql-iso-date'
import { asNexusMethod, inputObjectType, makeSchema, objectType, queryField } from '../src/core'
import {
asNexusMethod,
inputObjectType,
makeSchema,
makeSchemaInternal,
objectType,
queryField,
resolveTypegenConfig,
} from '../src/core'
import { TypegenMetadata } from '../src/typegenMetadata'
describe('scalarType', () => {
it('asNexusMethod: may specify the TS type of the scalar', async () => {
const schema = makeSchemaInternal({
types: [asNexusMethod(GraphQLDate, 'date', 'Int')],
outputs: false,
shouldExitAfterGenerateArtifacts: false,
})
const generator = new TypegenMetadata(resolveTypegenConfig(schema.finalConfig))
const typegen = await generator.generateTypesFile(schema.schema, 'foo.ts')
expect(typegen).toMatch(/Date: Int/)
})
it('asNexusMethod: should wrap a scalar and make it available on the builder', async () => {
const schema = makeSchema({
types: [

View File

@ -26,18 +26,21 @@ export interface NexusGenEnums {
SomeEnum: 'A' | 'B'
}
export interface NexusGenRootTypes {
Mutation: {}
Post: {}
Query: {}
User: {}
Node: NexusGenRootTypes['Post'] | NexusGenRootTypes['User']
export interface NexusGenScalars {
String: string
Int: number
Float: number
Boolean: boolean
ID: string
UUID: any
}
export interface NexusGenRootTypes {
Mutation: {}
Post: {}
Query: {}
User: {}
Node: NexusGenRootTypes['Post'] | NexusGenRootTypes['User']
ExampleUnion: NexusGenRootTypes['Post'] | NexusGenRootTypes['User']
}
@ -46,6 +49,12 @@ export interface NexusGenAllTypes extends NexusGenRootTypes {
PostFilters: NexusGenInputs['PostFilters']
OrderEnum: NexusGenEnums['OrderEnum']
SomeEnum: NexusGenEnums['SomeEnum']
String: NexusGenScalars['String']
Int: NexusGenScalars['Int']
Float: NexusGenScalars['Float']
Boolean: NexusGenScalars['Boolean']
ID: NexusGenScalars['ID']
UUID: NexusGenScalars['UUID']
}
export interface NexusGenFieldTypes {
@ -61,7 +70,7 @@ export interface NexusGenFieldTypes {
geo: number[][] // [[Float!]!]!
id: string // ID!
messyGeo: Array<number[] | null> | null // [[Float!]]
uuid: any // UUID!
uuid: NexusGenScalars['UUID'] // UUID!
}
Query: {
// field return type
@ -92,7 +101,7 @@ export interface NexusGenArgTypes {
}
registerClick: {
// args
uuid?: any | null // UUID
uuid?: NexusGenScalars['UUID'] | null // UUID
}
someList: {
// args