Skip to content

Commit

Permalink
Add FragmentOf helper
Browse files Browse the repository at this point in the history
  • Loading branch information
kitten committed Jan 11, 2024
1 parent 5d8dd75 commit a580017
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 32 deletions.
4 changes: 2 additions & 2 deletions src/__tests__/selection.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type {
$tada,
decorateFragmentDef,
getFragmentsOfDocumentsRec,
FragmentDefDecoration,
makeFragmentDefDecoration,
} from '../namespace';

type schema = simpleSchema;
Expand Down Expand Up @@ -129,7 +129,7 @@ test('infers fragment spreads for fragment refs', () => {
`>;

type extraFragments = getFragmentsOfDocumentsRec<
[FragmentDefDecoration<decorateFragmentDef<fragment>>]
[makeFragmentDefDecoration<decorateFragmentDef<fragment>>]
>;

type actual = getDocumentType<query, schema, extraFragments>;
Expand Down
43 changes: 31 additions & 12 deletions src/api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { DocumentNode } from '@0no-co/graphql.web';
import type { DocumentNode, DefinitionNode } from '@0no-co/graphql.web';
import { Kind, parse as _parse } from '@0no-co/graphql.web';

import type {
Expand All @@ -9,10 +9,13 @@ import type {
} from './introspection';

import type {
decorateFragmentDef,
getFragmentsOfDocumentsRec,
FragmentDefDecorationLike,
FragmentDefDecoration,
OperationDefDecorationLike,
getFragmentsOfDocumentsRec,
makeFragmentDefDecoration,
decorateFragmentDef,
makeFragmentRef,
$tada,
} from './namespace';

import type { getDocumentType } from './selection';
Expand Down Expand Up @@ -56,15 +59,20 @@ type getDocumentNode<

function graphql<
const In extends stringLiteral<In>,
const Fragments extends readonly [...FragmentDefDecorationLike[]],
const Fragments extends readonly [...OperationDefDecorationLike[]],
>(
input: In,
fragments?: Fragments
): getDocumentNode<In, Schema, getFragmentsOfDocumentsRec<Fragments>> {
const definitions = new Set(_parse(input).definitions);
const definitions = _parse(input).definitions as DefinitionNode[];
const seen = new Set<unknown>();
for (const document of fragments || []) {
for (const definition of document.definitions)
if (definition.kind === Kind.FRAGMENT_DEFINITION) definitions.add(definition);
for (const definition of document.definitions) {
if (definition.kind === Kind.FRAGMENT_DEFINITION && !seen.has(definition)) {
definitions.push(definition);
seen.add(definition);
}
}
}
return { kind: Kind.DOCUMENT, definitions: [...definitions] } as any;
}
Expand Down Expand Up @@ -94,11 +102,22 @@ interface TadaDocumentNode<
Decoration = never,
> extends DocumentNode,
DocumentDecoration<Result, Variables>,
FragmentDefDecoration<Decoration> {}
makeFragmentDefDecoration<Decoration> {}

type ResultOf<T> = T extends DocumentDecoration<infer Result, infer _> ? Result : never;
type ResultOf<Document> = Document extends DocumentDecoration<infer Result, infer _>
? Result
: never;

type VariablesOf<Document> = Document extends DocumentDecoration<infer _, infer Variables>
? Variables
: never;

type VariablesOf<T> = T extends DocumentDecoration<infer _, infer Variables> ? Variables : never;
type FragmentOf<Document extends OperationDefDecorationLike> = Exclude<
Document[$tada.fragmentDef],
undefined
> extends infer FragmentDef extends FragmentDefDecorationLike
? makeFragmentRef<FragmentDef>
: never;

export { parse, graphql };
export type { setupSchema, TadaDocumentNode, ResultOf, VariablesOf };
export type { setupSchema, TadaDocumentNode, ResultOf, VariablesOf, FragmentOf };
32 changes: 21 additions & 11 deletions src/namespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,17 @@ declare namespace $tada {
export type fragmentId = typeof fragmentId;
}

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

interface OperationDefDecorationLike extends DocumentNode {
[$tada.fragmentDef]?: FragmentDefDecorationLike;
}

type decorateFragmentDef<Document extends DocumentNodeLike> = Document['definitions'][0] extends {
kind: Kind.FRAGMENT_DEFINITION;
name: any;
Expand Down Expand Up @@ -50,25 +61,24 @@ type getFragmentsOfDocumentsRec<Documents> = Documents extends readonly [
getFragmentsOfDocumentsRec<Rest>
: {};

interface FragmentDefDecorationLike extends DocumentNode {
[$tada.fragmentDef]?: {
readonly [$tada.fragmentId]: symbol;
kind: Kind.FRAGMENT_DEFINITION;
name: any;
typeCondition: any;
type makeFragmentRef<Definition extends FragmentDefDecorationLike> = {
[$tada.fragmentRefs]?: {
[Name in Definition['name']['value']]: Definition[$tada.fragmentId];
};
}
};

interface FragmentDefDecoration<Definition> {
[$tada.fragmentDef]?: Definition extends FragmentDefDecorationLike[$tada.fragmentDef]
type makeFragmentDefDecoration<Definition> = {
[$tada.fragmentDef]?: Definition extends OperationDefDecorationLike[$tada.fragmentDef]
? Definition
: never;
}
};

export type {
$tada,
decorateFragmentDef,
getFragmentsOfDocumentsRec,
makeFragmentDefDecoration,
makeFragmentRef,
FragmentDefDecorationLike,
FragmentDefDecoration,
OperationDefDecorationLike,
};
10 changes: 3 additions & 7 deletions src/selection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import type {
NameNode,
} from '@0no-co/graphql.web';

import type { $tada } from './namespace';
import type { obj, objValues } from './utils';
import type { DocumentNodeLike } from './parser';
import type { FragmentDefDecorationLike, $tada, makeFragmentRef } from './namespace';

import type {
IntrospectionField,
Expand Down Expand Up @@ -80,12 +80,8 @@ type getFragmentSelection<
? getSelection<Node['selectionSet']['selections'], Type, Introspection, Fragments>
: Node extends { kind: Kind.FRAGMENT_SPREAD; name: any }
? Node['name']['value'] extends keyof Fragments
? Fragments[Node['name']['value']] extends { readonly [$tada.fragmentId]: symbol }
? {
[$tada.fragmentRefs]?: {
[Name in Node['name']['value']]: Fragments[Node['name']['value']][$tada.fragmentId];
};
}
? Fragments[Node['name']['value']] extends FragmentDefDecorationLike
? makeFragmentRef<Fragments[Node['name']['value']]>
: getSelection<
Fragments[Node['name']['value']]['selectionSet']['selections'],
Type,
Expand Down

0 comments on commit a580017

Please sign in to comment.