1
- import { GraphQLSchema , TypeInfo , ASTKindToNode , Visitor , visit , visitWithTypeInfo } from 'graphql' ;
2
- import { getDocumentNodeFromSchema , getRootTypeNames } from '@graphql-tools/utils' ;
1
+ import {
2
+ ASTNode ,
3
+ Visitor ,
4
+ TypeInfo ,
5
+ GraphQLSchema ,
6
+ ASTKindToNode ,
7
+ visit ,
8
+ isInterfaceType ,
9
+ visitWithTypeInfo ,
10
+ } from 'graphql' ;
3
11
import { SiblingOperations } from './sibling-operations' ;
4
12
5
13
export type ReachableTypes = Set < string > ;
@@ -12,51 +20,43 @@ export function getReachableTypes(schema: GraphQLSchema): ReachableTypes {
12
20
if ( process . env . NODE_ENV !== 'test' && reachableTypesCache ) {
13
21
return reachableTypesCache ;
14
22
}
23
+ const reachableTypes : ReachableTypes = new Set ( ) ;
24
+ const getTypeName = node => ( 'type' in node ? getTypeName ( node . type ) : node . name . value ) ;
15
25
16
- const astNode = getDocumentNodeFromSchema ( schema ) ; // Transforms the schema into ASTNode
17
- const cache : Record < string , number > = Object . create ( null ) ;
26
+ const collect = ( node : ASTNode ) : false | void => {
27
+ const typeName = getTypeName ( node ) ;
28
+ if ( reachableTypes . has ( typeName ) ) {
29
+ return ;
30
+ }
31
+ reachableTypes . add ( typeName ) ;
32
+ const type = schema . getType ( typeName ) || schema . getDirective ( typeName ) ;
18
33
19
- const collect = ( nodeType : any ) : void => {
20
- let node = nodeType ;
21
- while ( node . type ) {
22
- node = node . type ;
34
+ if ( isInterfaceType ( type ) ) {
35
+ const { objects, interfaces } = schema . getImplementations ( type ) ;
36
+ for ( const { astNode } of [ ...objects , ...interfaces ] ) {
37
+ visit ( astNode , visitor ) ;
38
+ }
39
+ } else {
40
+ visit ( type . astNode , visitor ) ;
23
41
}
24
- const typeName = node . name . value ;
25
- cache [ typeName ] ??= 0 ;
26
- cache [ typeName ] += 1 ;
27
42
} ;
28
43
29
44
const visitor : Visitor < ASTKindToNode > = {
30
- SchemaDefinition ( node ) {
31
- node . operationTypes . forEach ( collect ) ;
32
- } ,
33
- ObjectTypeDefinition ( node ) {
34
- collect ( node ) ;
35
- node . interfaces ?. forEach ( collect ) ;
36
- } ,
37
- UnionTypeDefinition ( node ) {
38
- collect ( node ) ;
39
- node . types ?. forEach ( collect ) ;
40
- } ,
41
- InputObjectTypeDefinition : collect ,
42
45
InterfaceTypeDefinition : collect ,
43
- ScalarTypeDefinition : collect ,
46
+ ObjectTypeDefinition : collect ,
44
47
InputValueDefinition : collect ,
45
- DirectiveDefinition : collect ,
46
- EnumTypeDefinition : collect ,
48
+ UnionTypeDefinition : collect ,
47
49
FieldDefinition : collect ,
48
50
Directive : collect ,
51
+ NamedType : collect ,
49
52
} ;
50
53
51
- visit ( astNode , visitor ) ;
52
-
53
- const operationTypeNames = getRootTypeNames ( schema ) ;
54
-
55
- const usedTypes = Object . entries ( cache )
56
- . filter ( ( [ typeName , usedCount ] ) => usedCount > 1 || operationTypeNames . has ( typeName ) )
57
- . map ( ( [ typeName ] ) => typeName ) ;
58
-
59
- reachableTypesCache = new Set ( usedTypes ) ;
54
+ for ( const type of [ schema . getQueryType ( ) , schema . getMutationType ( ) , schema . getSubscriptionType ( ) ] ) {
55
+ if ( type ) {
56
+ visit ( type . astNode , visitor ) ;
57
+ }
58
+ }
59
+ reachableTypesCache = reachableTypes ;
60
60
return reachableTypesCache ;
61
61
}
62
62
@@ -72,25 +72,23 @@ export function getUsedFields(schema: GraphQLSchema, operations: SiblingOperatio
72
72
}
73
73
const usedFields : UsedFields = Object . create ( null ) ;
74
74
const typeInfo = new TypeInfo ( schema ) ;
75
- const allDocuments = [ ...operations . getOperations ( ) , ...operations . getFragments ( ) ] ;
76
75
77
76
const visitor = visitWithTypeInfo ( typeInfo , {
78
- Field : {
79
- enter ( node ) : false | void {
80
- const fieldDef = typeInfo . getFieldDef ( ) ;
81
- if ( ! fieldDef ) {
82
- // skip visiting this node if field is not defined in schema
83
- return false ;
84
- }
85
- const parentTypeName = typeInfo . getParentType ( ) . name ;
86
- const fieldName = node . name . value ;
77
+ Field ( node ) : false | void {
78
+ const fieldDef = typeInfo . getFieldDef ( ) ;
79
+ if ( ! fieldDef ) {
80
+ // skip visiting this node if field is not defined in schema
81
+ return false ;
82
+ }
83
+ const parentTypeName = typeInfo . getParentType ( ) . name ;
84
+ const fieldName = node . name . value ;
87
85
88
- usedFields [ parentTypeName ] ??= new Set ( ) ;
89
- usedFields [ parentTypeName ] . add ( fieldName ) ;
90
- } ,
86
+ usedFields [ parentTypeName ] ??= new Set ( ) ;
87
+ usedFields [ parentTypeName ] . add ( fieldName ) ;
91
88
} ,
92
89
} ) ;
93
90
91
+ const allDocuments = [ ...operations . getOperations ( ) , ...operations . getFragments ( ) ] ;
94
92
for ( const { document } of allDocuments ) {
95
93
visit ( document , visitor ) ;
96
94
}
0 commit comments