Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
1import type {
2 SelectionNode,
3 ASTNode,
4 DefinitionNode,
5 GraphQLSchema,
6 GraphQLFieldMap,
7 FragmentDefinitionNode,
8 FragmentSpreadNode,
9} from 'graphql';
10import { Kind, isAbstractType } from 'graphql';
11import { unwrapType, getName } from './node';
12
13export function traverse(
14 node: ASTNode,
15 enter?: (n: ASTNode) => ASTNode | void,
16 exit?: (n: ASTNode) => ASTNode | void
17): any {
18 if (enter) {
19 node = enter(node) || node;
20 }
21
22 switch (node.kind) {
23 case Kind.DOCUMENT: {
24 node = {
25 ...node,
26 definitions: node.definitions.map(
27 n => traverse(n, enter, exit) as DefinitionNode
28 ),
29 };
30 break;
31 }
32 case Kind.OPERATION_DEFINITION:
33 case Kind.FIELD:
34 case Kind.FRAGMENT_DEFINITION: {
35 if (node.selectionSet) {
36 node = {
37 ...node,
38 selectionSet: {
39 ...node.selectionSet,
40 selections: node.selectionSet.selections.map(
41 n => traverse(n, enter, exit) as SelectionNode
42 ),
43 },
44 };
45 }
46 break;
47 }
48 }
49
50 if (exit) {
51 node = exit(node) || node;
52 }
53
54 return node;
55}
56
57export function resolveFields(
58 schema: GraphQLSchema,
59 visits: string[]
60): GraphQLFieldMap<any, any> {
61 let currentFields = schema.getQueryType()!.getFields();
62
63 for (let i = 0; i < visits.length; i++) {
64 const t = unwrapType(currentFields[visits[i]].type);
65
66 if (isAbstractType(t)) {
67 currentFields = {};
68 schema.getPossibleTypes(t).forEach(implementedType => {
69 currentFields = {
70 ...currentFields,
71 // @ts-ignore TODO: proper casting
72 ...schema.getType(implementedType.name)!.toConfig().fields,
73 };
74 });
75 } else {
76 // @ts-ignore TODO: proper casting
77 currentFields = schema.getType(t!.name)!.toConfig().fields;
78 }
79 }
80
81 return currentFields;
82}
83
84/** Get fragment names referenced by node. */
85export function getUsedFragmentNames(node: FragmentDefinitionNode) {
86 const names: string[] = [];
87
88 traverse(node, n => {
89 if (n.kind === Kind.FRAGMENT_SPREAD) {
90 names.push(getName(n as FragmentSpreadNode));
91 }
92 });
93
94 return names;
95}