Skip to content

Commit

Permalink
feat(ts-client): use a global namespace (#808)
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonkuhrt committed Apr 29, 2024
1 parent 2a39e81 commit 4ac0cd1
Show file tree
Hide file tree
Showing 16 changed files with 102 additions and 55 deletions.
9 changes: 8 additions & 1 deletion src/layers/1_Schema/Hybrid/types/Scalar/Scalar.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/ban-types */

import type { GlobalRegistry } from '../../../../2_generator/globalRegistry.js'
import type { Codec } from './codec.js'
import { nativeScalarCodecs } from './nativeScalarCodecs.js'

Expand Down Expand Up @@ -58,6 +59,12 @@ export const Scalars = {
}

// todo this mixes scalars from different schemas
export type Any = String | Int | Boolean | ID | Float | Values<NamedSchemas[keyof NamedSchemas]['customScalars']>
export type Any =
| String
| Int
| Boolean
| ID
| Float
| Values<GlobalRegistry.Schemas[keyof GlobalRegistry.Schemas]['customScalars']>

type Values<T> = T extends any ? keyof T extends never ? never : T[keyof T] : never
9 changes: 9 additions & 0 deletions src/layers/2_generator/__snapshots__/files.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ export interface Index {
rootResultFields: {
Query: {
result: 'result'
resultNonNull: 'resultNonNull'
}
Mutation: {}
Subscription: {}
Expand Down Expand Up @@ -306,6 +307,12 @@ export namespace Root {
case: Enum.Case
}>
>
resultNonNull: $.Field<
Union.Result,
$.Args<{
case: Enum.Case
}>
>
string: $.Field<$.Output.Nullable<$Scalar.String>, null>
stringWithArgEnum: $.Field<
$.Output.Nullable<$Scalar.String>,
Expand Down Expand Up @@ -698,6 +705,7 @@ export const Query = $.Object$(\`Query\`, {
}),
),
result: $.field($.Output.Nullable(() => Result), $.Args({ case: Case })),
resultNonNull: $.field(() => Result, $.Args({ case: Case })),
string: $.field($.Output.Nullable($Scalar.String)),
stringWithArgEnum: $.field($.Output.Nullable($Scalar.String), $.Args({ ABCEnum: $.Input.Nullable(ABCEnum) })),
stringWithArgInputObject: $.field(
Expand Down Expand Up @@ -773,6 +781,7 @@ export const $Index = {
rootResultFields: {
Query: {
result: 'result' as const,
resultNonNull: 'resultNonNull' as const,
},
Mutation: {},
Subscription: {},
Expand Down
18 changes: 10 additions & 8 deletions src/layers/2_generator/code/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,23 @@ export const generateGlobal = (config: Config) => {

code.push(`
declare global {
interface NamedSchemas {
${config.name}: {
index: Index
customScalars: {
${
export namespace GraphQLRequestTypes {
export interface Schemas {
${config.name}: {
index: Index
customScalars: {
${
config.typeMapByKind.GraphQLScalarTypeCustom
.map((_) => {
return `${_.name}: ${
needsDefaultCustomScalarImplementation ? `${StandardScalarNamespace}.String` : `CustomScalar.${_.name}`
}`
}).join(`\n`)
}
}
featureOptions: {
schemaErrors: ${config.options.errorTypeNamePattern ? `true` : `false`}
}
featureOptions: {
schemaErrors: ${config.options.errorTypeNamePattern ? `true` : `false`}
}
}
}
}
Expand Down
25 changes: 14 additions & 11 deletions src/layers/2_generator/globalRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import type { TSError } from '../../lib/TSError.js'
import type { Schema } from '../1_Schema/__.js'

declare global {
// todo some kind of distinct prefix
interface NamedSchemas {}
export namespace GraphQLRequestTypes {
interface Schemas {
}
}
}

export type GlobalRegistry = Record<string, {
Expand All @@ -15,27 +17,28 @@ export type GlobalRegistry = Record<string, {
}>

export namespace GlobalRegistry {
export type Schemas = NamedSchemas
export type Schemas = GraphQLRequestTypes.Schemas

export type DefaultSchemaName = 'default'

export type SchemaNames = keyof NamedSchemas extends never
export type SchemaNames = keyof GraphQLRequestTypes.Schemas extends never
? TSError<'SchemaNames', 'No schemas have been registered. Did you run graphql-request generate?'>
: keyof NamedSchemas
: keyof GraphQLRequestTypes.Schemas

// todo use conditional types?
// eslint-disable-next-line
// @ts-ignore populated after generation
export type HasSchemaErrors<$Name extends SchemaNames> = NamedSchemas[$Name]['featureOptions']['schemaErrors']
export type HasSchemaErrors<$Name extends SchemaNames> =
// todo use conditional types?
// eslint-disable-next-line
// @ts-ignore populated after generation
GraphQLRequestTypes.Schemas[$Name]['featureOptions']['schemaErrors']

// todo use conditional types?
// eslint-disable-next-line
// @ts-ignore populated after generation
export type GetSchemaIndexOptionally<$Name extends SchemaNames | undefined> = $Name extends SchemaNames
// eslint-disable-next-line
// @ts-ignore populated after generation
? NamedSchemas[$Name]['index']
? GraphQLRequestTypes.Schemas[$Name]['index']
// eslint-disable-next-line
// @ts-ignore populated after generation
: NamedSchemas['default']['index']
: GraphQLRequestTypes.Schemas['default']['index']
}
5 changes: 3 additions & 2 deletions src/layers/5_client/client.returnMode.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe('default is data', () => {
describe('data', () => {
const client = create({ schema, schemaIndex, returnMode: 'data' })
test(`document.run`, async () => {
expectTypeOf(client.document({ main: { query: { id: true } } }).run()).resolves.toEqualTypeOf<{ id: string | null }>()
expectTypeOf(client.document({ x: { query: { id: true } } }).run()).resolves.toEqualTypeOf<{ id: string | null }>()
})
test('query.<fieldMethod>', async () => {
await expectTypeOf(client.query.__typename()).resolves.toEqualTypeOf<'Query'>()
Expand Down Expand Up @@ -78,7 +78,8 @@ describe('successData', () => {
})
describe('without explicit __typename', () => {
test('document',async () => {
await expectTypeOf(client.document({x:{query:{result:{$:{case:'Object1'}}}}}).run()).resolves.toEqualTypeOf<{result:{__typename: "Object1"} | null}>()
const result = client.document({x:{query:{resultNonNull:{$:{case:'Object1'}}}}}).run()
await expectTypeOf(result).resolves.toEqualTypeOf<{resultNonNull:{__typename: "Object1"}}>()
})
test('query.<fieldMethod>',async () => {
await expectTypeOf(client.query.result({$:{case:'Object1'}})).resolves.toEqualTypeOf<{__typename: "Object1"} | null>()
Expand Down
17 changes: 11 additions & 6 deletions src/layers/5_client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ interface HookInputDocumentEncode {
documentObject: GraphQLObjectSelection
}

type InputForSchema<$Name extends GlobalRegistry.SchemaNames> = $Name extends any ? {
type InputForSchema2<$Name extends GlobalRegistry.SchemaNames> = $Name extends any ? {
/**
* @defaultValue 'default'
*/
Expand Down Expand Up @@ -67,14 +67,19 @@ type InputForSchema<$Name extends GlobalRegistry.SchemaNames> = $Name extends an
}
: never

type Input = InputForSchema<GlobalRegistry.SchemaNames>

export const create = <$Input extends Input>(
type Create = <
$Input extends InputForSchema2<GlobalRegistry.SchemaNames>,
>(
input: $Input,
): Client<
) => Client<
GlobalRegistry.GetSchemaIndexOptionally<$Input['name']>,
ApplyInputDefaults<{ returnMode: $Input['returnMode'] }>
> => {
>

// export const create = <$Input extends Input>(
export const create: Create = (
input,
) => {
// const parentInput = input
/**
* @remarks Without generation the type of returnMode can be `ReturnModeTypeBase` which leads
Expand Down
3 changes: 2 additions & 1 deletion src/layers/5_select/select.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { RootTypeName } from '../../lib/graphql.js'
import type { Exact } from '../../lib/prelude.js'
import type { Schema } from '../1_Schema/__.js'
import type { GlobalRegistry } from '../2_generator/globalRegistry.js'
import type { SelectionSet } from '../3_SelectionSet/__.js'

// dprint-ignore
Expand Down Expand Up @@ -36,4 +37,4 @@ const idProxy = new Proxy({}, {

// eslint-disable-next-line
// @ts-ignore generated types
export const select: TypeSelectionSets<NamedSchemas['default']['index']> = idProxy
export const select: TypeSelectionSets<GlobalRegistry.Schemas['default']['index']> = idProxy
18 changes: 10 additions & 8 deletions tests/_/schema/generated/Global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ import { Index } from './Index.js'
import type * as CustomScalar from '../../customScalarCodecs.js'

declare global {
interface NamedSchemas {
default: {
index: Index
customScalars: {
Date: CustomScalar.Date
}
featureOptions: {
schemaErrors: true
export namespace GraphQLRequestTypes {
export interface Schemas {
default: {
index: Index
customScalars: {
Date: CustomScalar.Date
}
featureOptions: {
schemaErrors: true
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions tests/_/schema/generated/Index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export interface Index {
rootResultFields: {
Query: {
result: 'result'
resultNonNull: 'resultNonNull'
}
Mutation: {}
Subscription: {}
Expand Down
6 changes: 6 additions & 0 deletions tests/_/schema/generated/SchemaBuildtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ export namespace Root {
case: Enum.Case
}>
>
resultNonNull: $.Field<
Union.Result,
$.Args<{
case: Enum.Case
}>
>
string: $.Field<$.Output.Nullable<$Scalar.String>, null>
stringWithArgEnum: $.Field<
$.Output.Nullable<$Scalar.String>,
Expand Down
2 changes: 2 additions & 0 deletions tests/_/schema/generated/SchemaRuntime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ export const Query = $.Object$(`Query`, {
}),
),
result: $.field($.Output.Nullable(() => Result), $.Args({ case: Case })),
resultNonNull: $.field(() => Result, $.Args({ case: Case })),
string: $.field($.Output.Nullable($Scalar.String)),
stringWithArgEnum: $.field($.Output.Nullable($Scalar.String), $.Args({ ABCEnum: $.Input.Nullable(ABCEnum) })),
stringWithArgInputObject: $.field(
Expand Down Expand Up @@ -247,6 +248,7 @@ export const $Index = {
rootResultFields: {
Query: {
result: 'result' as const,
resultNonNull: 'resultNonNull' as const,
},
Mutation: {},
Subscription: {},
Expand Down
1 change: 1 addition & 0 deletions tests/_/schema/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ type Query {
objectNonNull: Object1!
objectWithArgs(boolean: Boolean, float: Float, id: ID, int: Int, string: String): Object1
result(case: Case!): Result
resultNonNull(case: Case!): Result!
string: String
stringWithArgEnum(ABCEnum: ABCEnum): String
stringWithArgInputObject(input: InputObject): String
Expand Down
13 changes: 8 additions & 5 deletions tests/_/schema/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import SchemaBuilder from '@pothos/core'
import SimpleObjectsPlugin from '@pothos/plugin-simple-objects'
import { DateTimeISOResolver } from 'graphql-scalars'
import { db } from '../db.js'
import type { ID } from './generated/Scalar.js'

const builder = new SchemaBuilder<{
DefaultFieldNullability: true
Expand Down Expand Up @@ -81,10 +80,6 @@ builder.mutationType({
}),
})

interface Interface {
id: ID
}

const Interface = builder.simpleInterface(`Interface`, {
fields: t => ({
id: t.id(),
Expand Down Expand Up @@ -426,6 +421,14 @@ builder.queryType({
return db[args.case]
},
}),
resultNonNull: t.field({
args: { case: t.arg({ type: ResultCase, required: true }) },
type: Result,
nullable: false, // the differenece between null & non-null revealed errors we test for now.
resolve: (_, args) => {
return db[args.case]
},
}),
}),
})

Expand Down
2 changes: 1 addition & 1 deletion tests/_/schemaGenerate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const generate = async (
code: {
libraryPaths: {
schema: `../../../../src/entrypoints/alpha/schema.js`,
scalars: `../../../../src/Schema/Hybrid/types/Scalar/Scalar.js`,
scalars: `../../../../src/layers/1_Schema/Hybrid/types/Scalar/Scalar.js`,
},
},
name: input.name,
Expand Down
14 changes: 8 additions & 6 deletions tests/_/schemaMutationOnly/generated/Global.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { Index } from './Index.js'

declare global {
interface NamedSchemas {
MutationOnly: {
index: Index
customScalars: {}
featureOptions: {
schemaErrors: false
export namespace GraphQLRequestTypes {
export interface Schemas {
MutationOnly: {
index: Index
customScalars: {}
featureOptions: {
schemaErrors: false
}
}
}
}
Expand Down
14 changes: 8 additions & 6 deletions tests/_/schemaQueryOnly/generated/Global.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { Index } from './Index.js'

declare global {
interface NamedSchemas {
QueryOnly: {
index: Index
customScalars: {}
featureOptions: {
schemaErrors: false
export namespace GraphQLRequestTypes {
export interface Schemas {
QueryOnly: {
index: Index
customScalars: {}
featureOptions: {
schemaErrors: false
}
}
}
}
Expand Down

0 comments on commit 4ac0cd1

Please sign in to comment.