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:
parent
de7cdfd396
commit
10c5f8bc8c
|
@ -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/**/*'],
|
||||
|
|
|
@ -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]
|
||||
> = K2 extends keyof GetGen<K, never>
|
||||
? K3 extends keyof GetGen<K>[K2]
|
||||
? GetGen<K>[K2][K3]
|
||||
: Fallback
|
||||
: Fallback
|
||||
: any
|
||||
: any
|
||||
: any
|
||||
: any
|
||||
|
||||
export type HasGen<K extends GenTypesShapeKeys> = NexusGen extends infer GenTypes
|
||||
? GenTypes extends GenTypesShape
|
||||
|
|
|
@ -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 }))
|
||||
}
|
||||
|
|
|
@ -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()}`,
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
},
|
||||
|
|
|
@ -13,7 +13,6 @@ import {
|
|||
import { dynamicOutputProperty } from '../src/dynamicProperty'
|
||||
import { CatListFixture } from './_fixtures'
|
||||
|
||||
let spy: jest.SpyInstance
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import './__typegen'
|
||||
import {
|
||||
dynamicInputMethod,
|
||||
dynamicOutputMethod,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"rootDir": "../../..",
|
||||
"target": "ES2015",
|
||||
"moduleResolution": "node"
|
||||
},
|
||||
"include": ["__app.ts"]
|
||||
}
|
|
@ -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)
|
||||
},
|
||||
}),
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,5 @@
|
|||
import { testApp } from '../../__helpers/testApp'
|
||||
|
||||
testApp({
|
||||
rootDir: __dirname,
|
||||
})
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"target": "ES2015",
|
||||
"rootDir": "../../../",
|
||||
"moduleResolution": "node"
|
||||
},
|
||||
"include": ["__app.ts"]
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
// import { testApp } from '../../__helpers/testApp'
|
||||
|
||||
// testApp({
|
||||
// rootDir: __dirname,
|
||||
// })
|
||||
|
||||
it.todo('fixme')
|
|
@ -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(
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"noUnusedLocals": false, // FIXME
|
||||
"noEmit": true,
|
||||
"rootDir": "."
|
||||
},
|
||||
"include": ["**/*.ts"]
|
||||
}
|
Loading…
Reference in New Issue