-
-
Notifications
You must be signed in to change notification settings - Fork 100
/
graphql-ast.ts
96 lines (83 loc) · 2.79 KB
/
graphql-ast.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import {
ASTNode,
ASTVisitor,
TypeInfo,
GraphQLSchema,
visit,
isInterfaceType,
visitWithTypeInfo,
} from 'graphql';
import { SiblingOperations } from './sibling-operations';
import { getTypeName } from './utils';
export type ReachableTypes = Set<string>;
let reachableTypesCache: ReachableTypes;
export function getReachableTypes(schema: GraphQLSchema): ReachableTypes {
// We don't want cache reachableTypes on test environment
// Otherwise reachableTypes will be same for all tests
if (process.env.NODE_ENV !== 'test' && reachableTypesCache) {
return reachableTypesCache;
}
const reachableTypes: ReachableTypes = new Set();
const collect = (node: ASTNode): false | void => {
const typeName = getTypeName(node);
if (reachableTypes.has(typeName)) {
return;
}
reachableTypes.add(typeName);
const type = schema.getType(typeName) || schema.getDirective(typeName);
if (isInterfaceType(type)) {
const { objects, interfaces } = schema.getImplementations(type);
for (const { astNode } of [...objects, ...interfaces]) {
visit(astNode, visitor);
}
} else {
visit(type.astNode, visitor);
}
};
const visitor: ASTVisitor = {
InterfaceTypeDefinition: collect,
ObjectTypeDefinition: collect,
InputValueDefinition: collect,
UnionTypeDefinition: collect,
FieldDefinition: collect,
Directive: collect,
NamedType: collect,
};
for (const type of [schema.getQueryType(), schema.getMutationType(), schema.getSubscriptionType()]) {
if (type) {
visit(type.astNode, visitor);
}
}
reachableTypesCache = reachableTypes;
return reachableTypesCache;
}
export type UsedFields = Record<string, Set<string>>;
let usedFieldsCache: UsedFields;
export function getUsedFields(schema: GraphQLSchema, operations: SiblingOperations): UsedFields {
// We don't want cache usedFields on test environment
// Otherwise usedFields will be same for all tests
if (process.env.NODE_ENV !== 'test' && usedFieldsCache) {
return usedFieldsCache;
}
const usedFields: UsedFields = Object.create(null);
const typeInfo = new TypeInfo(schema);
const visitor = visitWithTypeInfo(typeInfo, {
Field(node): false | void {
const fieldDef = typeInfo.getFieldDef();
if (!fieldDef) {
// skip visiting this node if field is not defined in schema
return false;
}
const parentTypeName = typeInfo.getParentType().name;
const fieldName = node.name.value;
usedFields[parentTypeName] ??= new Set();
usedFields[parentTypeName].add(fieldName);
},
});
const allDocuments = [...operations.getOperations(), ...operations.getFragments()];
for (const { document } of allDocuments) {
visit(document, visitor);
}
usedFieldsCache = usedFields;
return usedFieldsCache;
}