fix: "union too complex to represent" for large union types (#571)

Closes #503
Co-authored-by: Jason Kuhrt <jasonkuhrt@me.com>
This commit is contained in:
Flavian Desverne 2020-10-26 15:34:14 +01:00 committed by GitHub
parent de7cdfd396
commit 10c5f8bc8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 1338 additions and 76 deletions

View File

@ -1,9 +1,8 @@
const path = require('path')
const { env } = require('process')
const process = require('process')
/**
* @type {jest.InitialOptions}
*/
/** @typedef {import('ts-jest')} */
/** @type {import('@jest/types').Config.InitialOptions} */
module.exports = {
setupFilesAfterEnv: ['<rootDir>/tests/_setup.ts'],
preset: 'ts-jest',
@ -11,8 +10,12 @@ module.exports = {
watchPlugins: ['jest-watch-typeahead/filename', 'jest-watch-typeahead/testname'],
globals: {
'ts-jest': {
diagnostics: Boolean(process.env.CI),
tsConfig: path.join(__dirname, 'tests/tsconfig.json'),
diagnostics: Boolean(process.env.CI)
? {
pretty: true,
pathRegex: '\\.(spec|test)\\.ts$',
}
: false,
},
},
collectCoverageFrom: ['src/**/*'],

View File

@ -164,33 +164,20 @@ export type GetGen<K extends GenTypesShapeKeys, Fallback = any> = NexusGen exten
export type GetGen2<
K extends GenTypesShapeKeys,
K2 extends keyof GenTypesShape[K]
> = NexusGen extends infer GenTypes
? GenTypes extends GenTypesShape
? K extends keyof GenTypes
? K2 extends keyof GenTypes[K]
? GenTypes[K][K2]
: any
: any
: any
: any
K2 extends Extract<keyof GenTypesShape[K], string>,
Fallback = any
> = K2 extends keyof GetGen<K, never> ? GetGen<K>[K2] : Fallback
export type GetGen3<
K extends GenTypesShapeKeys,
K2 extends Extract<keyof GenTypesShape[K], string>,
K3 extends Extract<keyof GenTypesShape[K][K2], string>,
Fallback = any
> = NexusGen extends infer GenTypes
? GenTypes extends GenTypesShape
? K extends keyof GenTypes
? K2 extends keyof GenTypes[K]
? K3 extends keyof GenTypes[K][K2]
? GenTypes[K][K2][K3]
: Fallback
: any
: any
: any
: any
> = K2 extends keyof GetGen<K, never>
? K3 extends keyof GetGen<K>[K2]
? GetGen<K>[K2][K3]
: Fallback
: Fallback
export type HasGen<K extends GenTypesShapeKeys> = NexusGen extends infer GenTypes
? GenTypes extends GenTypesShape

View File

@ -22,8 +22,8 @@ import {
specifiedScalarTypes,
} from 'graphql'
import * as path from 'path'
import { decorateType } from './definitions/decorateType'
import { MissingType, NexusTypes, withNexusSymbol } from './definitions/_types'
import { decorateType } from './definitions/decorateType'
import { PluginConfig } from './plugin'
export const isInterfaceField = (type: GraphQLObjectType, fieldName: string) => {
@ -420,3 +420,10 @@ export function casesHandled(x: never): never {
* Is the given type equal to the other given type?
*/
export type IsEqual<A, B> = A extends B ? (B extends A ? true : false) : false
/**
* Quickly log objects
*/
export function dump(x: any) {
console.log(require('util').inspect(x, { depth: null }))
}

View File

@ -1,7 +1,7 @@
/// <reference path="../_setup.ts" />
import { join } from 'path'
import * as ts from 'typescript'
import { core } from '../../src'
const { generateSchema, typegenFormatPrettier } = core
type Settings = {
@ -16,16 +16,16 @@ type Settings = {
* - By default looks for an `__app.ts` entrypoint
* - All entrypoint exports are expected to be Nexus type definitions
* - Except the optional export name "plugins" which is treated as an array of plugins for makeSchema
* - The typegen module will be automatically included into the TypeScript build, no need to import it
* - Outputs a `__typegen.ts` typegen module
* - You must import the typegen module into your entrypoint module
* - If you provide a `tsconfig.json` file in the root dir it will be used.
*/
export const testApp = (settings: Settings) => {
const name = settings?.name ?? 'app'
const rootDir = settings.rootDir
const typegenModuleName = '__typegen'
const typegenModulePath = join(rootDir, `${typegenModuleName}.ts`)
const entrypointModuleName = `__app`
const entrypointModulePath = join(rootDir, `${entrypointModuleName}.ts`)
const typegenModulePath = join(rootDir, '__typegen.ts')
const entrypointModulePath = join(rootDir, '__app.ts')
const entrypoint = require(entrypointModulePath)
const { plugins, ...types } = entrypoint
@ -40,22 +40,16 @@ export const testApp = (settings: Settings) => {
shouldGenerateArtifacts: true,
plugins: plugins || [],
async formatTypegen(source, type) {
const content = await typegenFormatPrettier({
trailingComma: 'es5',
arrowParens: 'always',
})(source, type)
return content.replace('"@nexus/schema"', '"../../.."')
const prettierConfigPath = require.resolve('../../.prettierrc')
const content = await typegenFormatPrettier(prettierConfigPath)(source, type)
return content.replace("'@nexus/schema'", "'../../..'")
},
})
})
it(`can compile ${name} app with its typegen`, async () => {
expect([entrypointModulePath, typegenModulePath]).toTypeCheck({
downlevelIteration: true,
noEmitOnError: true,
strict: true,
target: ts.ScriptTarget.ES5,
noErrorTruncation: false,
expect({ rootDir }).toTypeCheck({
outDir: `/tmp/nexus-integration-test-${Date.now()}`,
})
})

View File

@ -1,5 +1,8 @@
import * as fs from 'fs-jetpack'
import * as Path from 'path'
import * as tsm from 'ts-morph'
import * as ts from 'typescript'
;(global as any).TS_FORMAT_PROJECT_ROOT = 'src/'
const formatTSDiagonsticsForJest = (diagnostics: readonly ts.Diagnostic[]): string => {
@ -23,9 +26,33 @@ const formatTSDiagonsticsForJest = (diagnostics: readonly ts.Diagnostic[]): stri
}
expect.extend({
toTypeCheck(fileNames: string | string[], compilerOptions: tsm.CompilerOptions) {
const project = new tsm.Project({ compilerOptions: compilerOptions })
project.addSourceFilesAtPaths(Array.isArray(fileNames) ? fileNames : [fileNames])
toTypeCheck(input: string | string[] | { rootDir: string }, compilerOptions: tsm.CompilerOptions) {
let rootDir
let fileNames: string[]
if (typeof input !== 'string' && !Array.isArray(input)) {
rootDir = input.rootDir
fileNames = []
} else {
fileNames = Array.isArray(input) ? input : [input]
rootDir = Path.dirname(fileNames[0])
}
// check for tsconfig
let tsConfigFilePath
const maybeTsConfigFilePath = `${rootDir}/tsconfig.json`
if (fs.exists(maybeTsConfigFilePath)) {
tsConfigFilePath = maybeTsConfigFilePath
}
const project = new tsm.Project({
tsConfigFilePath,
compilerOptions,
addFilesFromTsConfig: true,
})
if (fileNames.length) {
project.addSourceFilesAtPaths(fileNames)
}
const preEmitDiagnostics = project.getPreEmitDiagnostics()
const emitDiagnostics = project.emitSync().getDiagnostics()
@ -36,8 +63,9 @@ expect.extend({
message: () =>
pass
? 'expected program to not typecheck'
: (project.formatDiagnosticsWithColorAndContext(preEmitDiagnostics),
project.formatDiagnosticsWithColorAndContext(emitDiagnostics)),
: project.formatDiagnosticsWithColorAndContext(preEmitDiagnostics) +
'\n\n\n' +
project.formatDiagnosticsWithColorAndContext(emitDiagnostics),
pass,
}
},

View File

@ -13,7 +13,6 @@ import {
import { dynamicOutputProperty } from '../src/dynamicProperty'
import { CatListFixture } from './_fixtures'
let spy: jest.SpyInstance
beforeEach(() => {
jest.clearAllMocks()
})

View File

@ -1,3 +1,4 @@
import './__typegen'
import {
dynamicInputMethod,
dynamicOutputMethod,

View File

@ -97,6 +97,40 @@ export interface NexusGenFieldTypes {
}
}
export interface NexusGenFieldTypeNames {
Mutation: {
// field return type name
createUser: 'User'
}
Post: {
// field return type name
body: 'String'
title: 'String'
}
Query: {
// field return type name
foo: 'String'
searchPosts: 'Post'
user: 'User'
}
Subscription: {
// field return type name
someBoolean: 'Boolean'
someField: 'Int'
someFields: 'Int'
someFloat: 'Float'
someID: 'ID'
someInt: 'Int'
someInts: 'Int'
someString: 'String'
}
User: {
// field return type name
firstName: 'String'
lastName: 'String'
}
}
export interface NexusGenArgTypes {
Mutation: {
createUser: {
@ -139,6 +173,7 @@ export interface NexusGenTypes {
rootTypes: NexusGenRootTypes
argTypes: NexusGenArgTypes
fieldTypes: NexusGenFieldTypes
fieldTypeNames: NexusGenFieldTypeNames
allTypes: NexusGenAllTypes
inheritedFields: NexusGenInheritedFields
objectNames: NexusGenObjectNames

View File

@ -0,0 +1,9 @@
{
"compilerOptions": {
"strict": true,
"rootDir": "../../..",
"target": "ES2015",
"moduleResolution": "node"
},
"include": ["__app.ts"]
}

View File

@ -1,3 +1,4 @@
import './__typegen'
import { objectType, unionType } from '../../../src'
export const typeNames = [
@ -315,7 +316,7 @@ export const types = [
unionType({
name: 'BigUnion',
definition(t) {
t.members(...(typeNames as any))
t.members(...typeNames)
t.resolveType(() => null)
},
}),

View File

@ -0,0 +1,5 @@
import { testApp } from '../../__helpers/testApp'
testApp({
rootDir: __dirname,
})

View File

@ -0,0 +1,9 @@
{
"compilerOptions": {
"strict": true,
"target": "ES2015",
"rootDir": "../../../",
"moduleResolution": "node"
},
"include": ["__app.ts"]
}

View File

@ -1,7 +0,0 @@
// import { testApp } from '../../__helpers/testApp'
// testApp({
// rootDir: __dirname,
// })
it.todo('fixme')

View File

@ -90,14 +90,6 @@ beforeEach(() => {
jest.resetAllMocks()
})
function toBase64(x: string): string {
return new Buffer(x).toString('base64')
}
function dump(x: any) {
console.log(require('util').inspect(x, { depth: null }))
}
describe('defaults', () => {
it('hasPreviousPage when paginating backwards assumes that node count equal to page size means there is another page to visit backward', async () => {
const schema = makeTestSchema(

View File

@ -1,9 +0,0 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"noUnusedLocals": false, // FIXME
"noEmit": true,
"rootDir": "."
},
"include": ["**/*.ts"]
}