@@ -13,127 +13,29 @@ module InternalRepresentation
13
13
#
14
14
# The rewritten query tree serves as the basis for the `FieldsWillMerge` validation.
15
15
#
16
- class Rewrite
16
+ module Rewrite
17
17
include GraphQL ::Language
18
18
19
19
NO_DIRECTIVES = [ ] . freeze
20
20
21
21
# @return InternalRepresentation::Document
22
- attr_reader :document
22
+ attr_reader :rewrite_document
23
23
24
- def initialize
25
- @document = InternalRepresentation ::Document . new
26
- end
27
-
28
- # @return [Hash<String, Node>] Roots of this query
29
- def operations
30
- warn "#{ self . class } #operations is deprecated; use `document.operation_definitions` instead"
31
- document . operation_definitions
32
- end
33
-
34
- def validate ( context )
35
- visitor = context . visitor
36
- query = context . query
24
+ def initialize ( *)
25
+ super
26
+ @query = context . query
27
+ @rewrite_document = InternalRepresentation ::Document . new
37
28
# Hash<Nodes::FragmentSpread => Set<InternalRepresentation::Node>>
38
29
# A record of fragment spreads and the irep nodes that used them
39
- spread_parents = Hash . new { |h , k | h [ k ] = Set . new }
30
+ @rewrite_spread_parents = Hash . new { |h , k | h [ k ] = Set . new }
40
31
# Hash<Nodes::FragmentSpread => Scope>
41
- spread_scopes = { }
32
+ @rewrite_spread_scopes = { }
42
33
# Array<Set<InternalRepresentation::Node>>
43
34
# The current point of the irep_tree during visitation
44
- nodes_stack = [ ]
35
+ @rewrite_nodes_stack = [ ]
45
36
# Array<Scope>
46
- scopes_stack = [ ]
47
-
48
- skip_nodes = Set . new
49
-
50
- visit_op = VisitDefinition . new ( context , @document . operation_definitions , nodes_stack , scopes_stack )
51
- visitor [ Nodes ::OperationDefinition ] . enter << visit_op . method ( :enter )
52
- visitor [ Nodes ::OperationDefinition ] . leave << visit_op . method ( :leave )
53
-
54
- visit_frag = VisitDefinition . new ( context , @document . fragment_definitions , nodes_stack , scopes_stack )
55
- visitor [ Nodes ::FragmentDefinition ] . enter << visit_frag . method ( :enter )
56
- visitor [ Nodes ::FragmentDefinition ] . leave << visit_frag . method ( :leave )
57
-
58
- visitor [ Nodes ::InlineFragment ] . enter << -> ( ast_node , ast_parent ) {
59
- # Inline fragments provide two things to the rewritten tree:
60
- # - They _may_ narrow the scope by their type condition
61
- # - They _may_ apply their directives to their children
62
- if skip? ( ast_node , query )
63
- skip_nodes . add ( ast_node )
64
- end
65
-
66
- if skip_nodes . none?
67
- scopes_stack . push ( scopes_stack . last . enter ( context . type_definition ) )
68
- end
69
- }
70
-
71
- visitor [ Nodes ::InlineFragment ] . leave << -> ( ast_node , ast_parent ) {
72
- if skip_nodes . none?
73
- scopes_stack . pop
74
- end
75
-
76
- if skip_nodes . include? ( ast_node )
77
- skip_nodes . delete ( ast_node )
78
- end
79
- }
80
-
81
- visitor [ Nodes ::Field ] . enter << -> ( ast_node , ast_parent ) {
82
- if skip? ( ast_node , query )
83
- skip_nodes . add ( ast_node )
84
- end
85
-
86
- if skip_nodes . none?
87
- node_name = ast_node . alias || ast_node . name
88
- parent_nodes = nodes_stack . last
89
- next_nodes = [ ]
90
-
91
- field_defn = context . field_definition
92
- if field_defn . nil?
93
- # It's a non-existent field
94
- new_scope = nil
95
- else
96
- field_return_type = field_defn . type
97
- scopes_stack . last . each do |scope_type |
98
- parent_nodes . each do |parent_node |
99
- node = parent_node . scoped_children [ scope_type ] [ node_name ] ||= Node . new (
100
- parent : parent_node ,
101
- name : node_name ,
102
- owner_type : scope_type ,
103
- query : query ,
104
- return_type : field_return_type ,
105
- )
106
- node . ast_nodes << ast_node
107
- node . definitions << field_defn
108
- next_nodes << node
109
- end
110
- end
111
- new_scope = Scope . new ( query , field_return_type . unwrap )
112
- end
113
-
114
- nodes_stack . push ( next_nodes )
115
- scopes_stack . push ( new_scope )
116
- end
117
- }
118
-
119
- visitor [ Nodes ::Field ] . leave << -> ( ast_node , ast_parent ) {
120
- if skip_nodes . none?
121
- nodes_stack . pop
122
- scopes_stack . pop
123
- end
124
-
125
- if skip_nodes . include? ( ast_node )
126
- skip_nodes . delete ( ast_node )
127
- end
128
- }
129
-
130
- visitor [ Nodes ::FragmentSpread ] . enter << -> ( ast_node , ast_parent ) {
131
- if skip_nodes . none? && !skip? ( ast_node , query )
132
- # Register the irep nodes that depend on this AST node:
133
- spread_parents [ ast_node ] . merge ( nodes_stack . last )
134
- spread_scopes [ ast_node ] = scopes_stack . last
135
- end
136
- }
37
+ @rewrite_scopes_stack = [ ]
38
+ @rewrite_skip_nodes = Set . new
137
39
138
40
# Resolve fragment spreads.
139
41
# Fragment definitions got their own irep trees during visitation.
@@ -142,12 +44,12 @@ def validate(context)
142
44
# can be shared between its usages.
143
45
context . on_dependency_resolve do |defn_ast_node , spread_ast_nodes , frag_ast_node |
144
46
frag_name = frag_ast_node . name
145
- fragment_node = @document . fragment_definitions [ frag_name ]
47
+ fragment_node = @rewrite_document . fragment_definitions [ frag_name ]
146
48
147
49
if fragment_node
148
50
spread_ast_nodes . each do |spread_ast_node |
149
- parent_nodes = spread_parents [ spread_ast_node ]
150
- parent_scope = spread_scopes [ spread_ast_node ]
51
+ parent_nodes = @rewrite_spread_parents [ spread_ast_node ]
52
+ parent_scope = @rewrite_spread_scopes [ spread_ast_node ]
151
53
parent_nodes . each do |parent_node |
152
54
parent_node . deep_merge_node ( fragment_node , scope : parent_scope , merge_self : false )
153
55
end
@@ -156,43 +58,126 @@ def validate(context)
156
58
end
157
59
end
158
60
159
- def skip? ( ast_node , query )
160
- dir = ast_node . directives
161
- dir . any? && !GraphQL ::Execution ::DirectiveChecks . include? ( dir , query )
61
+ # @return [Hash<String, Node>] Roots of this query
62
+ def operations
63
+ warn "#{ self . class } #operations is deprecated; use `document.operation_definitions` instead"
64
+ @document . operation_definitions
65
+ end
66
+
67
+ def on_operation_definition ( ast_node , parent )
68
+ push_root_node ( ast_node , @rewrite_document . operation_definitions ) { super }
69
+ end
70
+
71
+ def on_fragment_definition ( ast_node , parent )
72
+ push_root_node ( ast_node , @rewrite_document . fragment_definitions ) { super }
73
+ end
74
+
75
+ def push_root_node ( ast_node , definitions )
76
+ # Either QueryType or the fragment type condition
77
+ owner_type = context . type_definition
78
+ defn_name = ast_node . name
79
+
80
+ node = Node . new (
81
+ parent : nil ,
82
+ name : defn_name ,
83
+ owner_type : owner_type ,
84
+ query : @query ,
85
+ ast_nodes : [ ast_node ] ,
86
+ return_type : owner_type ,
87
+ )
88
+
89
+ definitions [ defn_name ] = node
90
+ @rewrite_scopes_stack . push ( Scope . new ( @query , owner_type ) )
91
+ @rewrite_nodes_stack . push ( [ node ] )
92
+ yield
93
+ @rewrite_nodes_stack . pop
94
+ @rewrite_scopes_stack . pop
95
+ end
96
+
97
+ def on_inline_fragment ( node , parent )
98
+ # Inline fragments provide two things to the rewritten tree:
99
+ # - They _may_ narrow the scope by their type condition
100
+ # - They _may_ apply their directives to their children
101
+ if skip? ( node )
102
+ @rewrite_skip_nodes . add ( node )
103
+ end
104
+
105
+ if @rewrite_skip_nodes . none?
106
+ @rewrite_scopes_stack . push ( @rewrite_scopes_stack . last . enter ( context . type_definition ) )
107
+ end
108
+
109
+ super
110
+
111
+ if @rewrite_skip_nodes . none?
112
+ @rewrite_scopes_stack . pop
113
+ end
114
+
115
+ if @rewrite_skip_nodes . include? ( node )
116
+ @rewrite_skip_nodes . delete ( node )
117
+ end
162
118
end
163
119
164
- class VisitDefinition
165
- def initialize ( context , definitions , nodes_stack , scopes_stack )
166
- @context = context
167
- @query = context . query
168
- @definitions = definitions
169
- @nodes_stack = nodes_stack
170
- @scopes_stack = scopes_stack
120
+ def on_field ( ast_node , ast_parent )
121
+ if skip? ( ast_node )
122
+ @rewrite_skip_nodes . add ( ast_node )
123
+ end
124
+
125
+ if @rewrite_skip_nodes . none?
126
+ node_name = ast_node . alias || ast_node . name
127
+ parent_nodes = @rewrite_nodes_stack . last
128
+ next_nodes = [ ]
129
+
130
+ field_defn = context . field_definition
131
+ if field_defn . nil?
132
+ # It's a non-existent field
133
+ new_scope = nil
134
+ else
135
+ field_return_type = field_defn . type
136
+ @rewrite_scopes_stack . last . each do |scope_type |
137
+ parent_nodes . each do |parent_node |
138
+ node = parent_node . scoped_children [ scope_type ] [ node_name ] ||= Node . new (
139
+ parent : parent_node ,
140
+ name : node_name ,
141
+ owner_type : scope_type ,
142
+ query : @query ,
143
+ return_type : field_return_type ,
144
+ )
145
+ node . ast_nodes << ast_node
146
+ node . definitions << field_defn
147
+ next_nodes << node
148
+ end
149
+ end
150
+ new_scope = Scope . new ( @query , field_return_type . unwrap )
151
+ end
152
+
153
+ @rewrite_nodes_stack . push ( next_nodes )
154
+ @rewrite_scopes_stack . push ( new_scope )
155
+ end
156
+
157
+ super
158
+
159
+ if @rewrite_skip_nodes . none?
160
+ @rewrite_nodes_stack . pop
161
+ @rewrite_scopes_stack . pop
171
162
end
172
163
173
- def enter ( ast_node , ast_parent )
174
- # Either QueryType or the fragment type condition
175
- owner_type = @context . type_definition && @context . type_definition . unwrap
176
- defn_name = ast_node . name
177
-
178
- node = Node . new (
179
- parent : nil ,
180
- name : defn_name ,
181
- owner_type : owner_type ,
182
- query : @query ,
183
- ast_nodes : [ ast_node ] ,
184
- return_type : @context . type_definition ,
185
- )
186
-
187
- @definitions [ defn_name ] = node
188
- @scopes_stack . push ( Scope . new ( @query , owner_type ) )
189
- @nodes_stack . push ( [ node ] )
164
+ if @rewrite_skip_nodes . include? ( ast_node )
165
+ @rewrite_skip_nodes . delete ( ast_node )
190
166
end
167
+ end
191
168
192
- def leave ( ast_node , ast_parent )
193
- @nodes_stack . pop
194
- @scopes_stack . pop
169
+ def on_fragment_spread ( ast_node , ast_parent )
170
+ if @rewrite_skip_nodes . none? && !skip? ( ast_node )
171
+ # Register the irep nodes that depend on this AST node:
172
+ @rewrite_spread_parents [ ast_node ] . merge ( @rewrite_nodes_stack . last )
173
+ @rewrite_spread_scopes [ ast_node ] = @rewrite_scopes_stack . last
195
174
end
175
+ super
176
+ end
177
+
178
+ def skip? ( ast_node )
179
+ dir = ast_node . directives
180
+ dir . any? && !GraphQL ::Execution ::DirectiveChecks . include? ( dir , @query )
196
181
end
197
182
end
198
183
end
0 commit comments