Skip to content

Commit

Permalink
Abstract annotated document nodes into TadaDocumentNode
Browse files Browse the repository at this point in the history
  • Loading branch information
kitten committed Jan 10, 2024
1 parent acb71c0 commit 6662986
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 45 deletions.
4 changes: 2 additions & 2 deletions src/__tests__/selection.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expectTypeOf, test } from 'vitest';
import { simpleSchema } from './fixtures/simpleSchema';
import { $tada, decorateDocument, getFragmentsOfDocumentsRec } from '../namespace';
import { $tada, decorateFragmentDef, getFragmentsOfDocumentsRec } from '../namespace';
import { parseDocument } from '../parser';
import { mapIntrospection } from '../introspection';
import { getDocumentType } from '../selection';
Expand Down Expand Up @@ -121,7 +121,7 @@ test('infers fragment spreads for fragment refs', () => {
query { ...Fields }
`>;

type extraFragments = getFragmentsOfDocumentsRec<[decorateDocument<fragment>]>;
type extraFragments = getFragmentsOfDocumentsRec<[decorateFragmentDef<fragment>]>;
type actual = getDocumentType<query, schema, extraFragments>;

type expected = {
Expand Down
46 changes: 39 additions & 7 deletions src/api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { parse as _parse } from '@0no-co/graphql.web';
import type { DocumentNode } from '@0no-co/graphql.web';

import type {
IntrospectionQuery,
Expand All @@ -8,15 +9,16 @@ import type {
} from './introspection';

import type {
decorateDocument,
decorateFragmentDef,
getFragmentsOfDocumentsRec,
FragmentDocumentNode,
FragmentDefDecorationLike,
FragmentDefDecoration,
} from './namespace';

import type { getDocumentType } from './selection';
import type { getVariablesType } from './variables';
import type { parseDocument, DocumentNodeLike } from './parser';
import type { stringLiteral, TypedDocumentNode } from './utils';
import type { stringLiteral, DocumentDecoration } from './utils';

interface AbstractSetupSchema {
introspection: IntrospectionQuery;
Expand Down Expand Up @@ -44,20 +46,50 @@ type getDocumentNode<
? getDocumentType<Document, Introspection, Fragments> extends infer Result
? Result extends never
? never
: TypedDocumentNode<Result, getVariablesType<Document, Introspection>> &
decorateDocument<Document>
: TadaDocumentNode<
Result,
getVariablesType<Document, Introspection>,
decorateFragmentDef<Document>
>
: never
: never;

function graphql<
const In extends stringLiteral<In>,
const Fragments extends readonly [...FragmentDocumentNode[]],
const Fragments extends readonly [...FragmentDefDecorationLike[]],
>(
input: In,
_fragments?: Fragments
): getDocumentNode<In, Schema, getFragmentsOfDocumentsRec<Fragments>> {
return _parse(input) as any;
}

/** A GraphQL `DocumentNode` with attached generics for its result data and variables.
*
* @remarks
* A GraphQL {@link DocumentNode} defines both the variables it accepts on request and the `data`
* shape it delivers on a response in the GraphQL query language.
*
* To bridge the gap to TypeScript, tools may be used to generate TypeScript types that define the shape
* of `data` and `variables` ahead of time. These types are then attached to GraphQL documents using this
* `TypedDocumentNode` type.
*
* Using a `DocumentNode` that is typed like this will cause any `urql` API to type its input `variables`
* and resulting `data` using the types provided.
*
* @privateRemarks
* For compatibility reasons this type has been copied and internalized from:
* https://github.com/dotansimha/graphql-typed-document-node/blob/3711b12/packages/core/src/index.ts#L3-L10
*
* @see {@link https://github.com/dotansimha/graphql-typed-document-node} for more information.
*/
interface TadaDocumentNode<
Result = { [key: string]: any },
Variables = { [key: string]: any },
Decoration = never,
> extends DocumentNode,
DocumentDecoration<Result, Variables>,
FragmentDefDecoration<Decoration> {}

export { parse, graphql };
export type { setupSchema };
export type { setupSchema, TadaDocumentNode };
31 changes: 20 additions & 11 deletions src/namespace.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Kind } from '@0no-co/graphql.web';
import type { DocumentNodeLike } from './parser';
import type { obj } from './utils';

/** Private namespace holding our symbols for markers.
*
Expand All @@ -20,23 +21,19 @@ declare namespace $tada {
export type fragmentId = typeof fragmentId;
}

type decorateDocument<Document extends DocumentNodeLike> = Document['definitions'][0] extends {
type decorateFragmentDef<Document extends DocumentNodeLike> = Document['definitions'][0] extends {
kind: Kind.FRAGMENT_DEFINITION;
name: any;
typeCondition: any;
}
? {
[$tada.fragmentDef]?: Document['definitions'][0] & {
readonly [$tada.fragmentId]: unique symbol;
};
}
: {};
? obj<Document['definitions'][0] & { readonly [$tada.fragmentId]: unique symbol }>
: never;

type getFragmentsOfDocumentsRec<Documents> = Documents extends readonly [
infer Document,
...infer Rest,
]
? (Document extends { [$tada.fragmentDef]?: any }
? (Document extends FragmentDefDecorationLike
? Exclude<Document[$tada.fragmentDef], undefined> extends infer FragmentDef extends {
kind: Kind.FRAGMENT_DEFINITION;
name: any;
Expand All @@ -48,13 +45,25 @@ type getFragmentsOfDocumentsRec<Documents> = Documents extends readonly [
getFragmentsOfDocumentsRec<Rest>
: {};

type FragmentDocumentNode = {
interface FragmentDefDecorationLike {
[$tada.fragmentDef]?: {
readonly [$tada.fragmentId]: symbol;
kind: Kind.FRAGMENT_DEFINITION;
name: any;
typeCondition: any;
};
};
}

export type { $tada, decorateDocument, getFragmentsOfDocumentsRec, FragmentDocumentNode };
interface FragmentDefDecoration<Definition> {
[$tada.fragmentDef]?: Definition extends FragmentDefDecorationLike[$tada.fragmentDef]
? Definition
: never;
}

export type {
$tada,
decorateFragmentDef,
getFragmentsOfDocumentsRec,
FragmentDefDecorationLike,
FragmentDefDecoration,
};
25 changes: 0 additions & 25 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,28 +54,3 @@ export interface DocumentDecoration<
*/
__ensureTypesOfVariablesAndResultMatching?: (variables: Variables) => Result;
}

/** A GraphQL `DocumentNode` with attached generics for its result data and variables.
*
* @remarks
* A GraphQL {@link DocumentNode} defines both the variables it accepts on request and the `data`
* shape it delivers on a response in the GraphQL query language.
*
* To bridge the gap to TypeScript, tools may be used to generate TypeScript types that define the shape
* of `data` and `variables` ahead of time. These types are then attached to GraphQL documents using this
* `TypedDocumentNode` type.
*
* Using a `DocumentNode` that is typed like this will cause any `urql` API to type its input `variables`
* and resulting `data` using the types provided.
*
* @privateRemarks
* For compatibility reasons this type has been copied and internalized from:
* https://github.com/dotansimha/graphql-typed-document-node/blob/3711b12/packages/core/src/index.ts#L3-L10
*
* @see {@link https://github.com/dotansimha/graphql-typed-document-node} for more information.
*/
export interface TypedDocumentNode<
Result = { [key: string]: any },
Variables = { [key: string]: any },
> extends DocumentNode,
DocumentDecoration<Result, Variables> {}

0 comments on commit 6662986

Please sign in to comment.