1
1
import type { ObjMap } from '../jsutils/ObjMap' ;
2
2
3
- import type { DocumentNode , OperationDefinitionNode } from '../language/ast' ;
3
+ import type {
4
+ DocumentNode ,
5
+ OperationDefinitionNode ,
6
+ SelectionSetNode ,
7
+ } from '../language/ast' ;
4
8
import { Kind } from '../language/kinds' ;
5
9
import { visit } from '../language/visitor' ;
6
10
@@ -13,36 +17,35 @@ import { visit } from '../language/visitor';
13
17
export function separateOperations (
14
18
documentAST : DocumentNode ,
15
19
) : ObjMap < DocumentNode > {
16
- const operations = [ ] ;
20
+ const operations : Array < OperationDefinitionNode > = [ ] ;
17
21
const depGraph : DepGraph = Object . create ( null ) ;
18
- let fromName ;
19
22
20
23
// Populate metadata and build a dependency graph.
21
- visit ( documentAST , {
22
- OperationDefinition ( node ) {
23
- fromName = opName ( node ) ;
24
- operations . push ( node ) ;
25
- } ,
26
- FragmentDefinition ( node ) {
27
- fromName = node . name . value ;
28
- } ,
29
- FragmentSpread ( node ) {
30
- const toName = node . name . value ;
31
- let dependents = depGraph [ fromName ] ;
32
- if ( dependents === undefined ) {
33
- dependents = depGraph [ fromName ] = Object . create ( null ) ;
34
- }
35
- dependents [ toName ] = true ;
36
- } ,
37
- } ) ;
24
+ for ( const definitionNode of documentAST . definitions ) {
25
+ switch ( definitionNode . kind ) {
26
+ case Kind . OPERATION_DEFINITION :
27
+ operations . push ( definitionNode ) ;
28
+ break ;
29
+ case Kind . FRAGMENT_DEFINITION :
30
+ depGraph [ definitionNode . name . value ] = collectDependencies (
31
+ definitionNode . selectionSet ,
32
+ ) ;
33
+ break ;
34
+ }
35
+ }
38
36
39
37
// For each operation, produce a new synthesized AST which includes only what
40
38
// is necessary for completing that operation.
41
39
const separatedDocumentASTs = Object . create ( null ) ;
42
40
for ( const operation of operations ) {
43
- const operationName = opName ( operation ) ;
44
- const dependencies = Object . create ( null ) ;
45
- collectTransitiveDependencies ( dependencies , depGraph , operationName ) ;
41
+ const dependencies = new Set ( ) ;
42
+
43
+ for ( const fragmentName of collectDependencies ( operation . selectionSet ) ) {
44
+ collectTransitiveDependencies ( dependencies , depGraph , fragmentName ) ;
45
+ }
46
+
47
+ // Provides the empty string for anonymous operations.
48
+ const operationName = operation . name ? operation . name . value : '' ;
46
49
47
50
// The list of definition nodes to be included for this operation, sorted
48
51
// to retain the same order as the original document.
@@ -52,35 +55,42 @@ export function separateOperations(
52
55
( node ) =>
53
56
node === operation ||
54
57
( node . kind === Kind . FRAGMENT_DEFINITION &&
55
- dependencies [ node . name . value ] ) ,
58
+ dependencies . has ( node . name . value ) ) ,
56
59
) ,
57
60
} ;
58
61
}
59
62
60
63
return separatedDocumentASTs ;
61
64
}
62
65
63
- type DepGraph = ObjMap < ObjMap < boolean >> ;
64
-
65
- // Provides the empty string for anonymous operations.
66
- function opName ( operation : OperationDefinitionNode ) : string {
67
- return operation . name ? operation . name . value : '' ;
68
- }
66
+ type DepGraph = ObjMap < Array < string >> ;
69
67
70
68
// From a dependency graph, collects a list of transitive dependencies by
71
69
// recursing through a dependency graph.
72
70
function collectTransitiveDependencies (
73
- collected : ObjMap < boolean > ,
71
+ collected : Set < string > ,
74
72
depGraph : DepGraph ,
75
73
fromName : string ,
76
74
) : void {
77
- const immediateDeps = depGraph [ fromName ] ;
78
- if ( immediateDeps ) {
79
- for ( const toName of Object . keys ( immediateDeps ) ) {
80
- if ( ! collected [ toName ] ) {
81
- collected [ toName ] = true ;
75
+ if ( ! collected . has ( fromName ) ) {
76
+ collected . add ( fromName ) ;
77
+
78
+ const immediateDeps = depGraph [ fromName ] ;
79
+ if ( immediateDeps !== undefined ) {
80
+ for ( const toName of immediateDeps ) {
82
81
collectTransitiveDependencies ( collected , depGraph , toName ) ;
83
82
}
84
83
}
85
84
}
86
85
}
86
+
87
+ function collectDependencies ( selectionSet : SelectionSetNode ) : Array < string > {
88
+ const dependencies = [ ] ;
89
+
90
+ visit ( selectionSet , {
91
+ FragmentSpread ( node ) {
92
+ dependencies . push ( node . name . value ) ;
93
+ } ,
94
+ } ) ;
95
+ return dependencies ;
96
+ }
0 commit comments