@@ -118,11 +118,17 @@ impl Transformer for AliasedInlineFragmentRemovalTransform {
118
118
}
119
119
}
120
120
121
+ #[ derive( Clone , Copy ) ]
122
+ struct ParentType {
123
+ type_ : Type ,
124
+ is_plural : bool ,
125
+ }
126
+
121
127
struct FragmentAliasTransform < ' program > {
122
128
program : & ' program Program ,
123
129
is_enforced : & ' program FeatureFlag ,
124
130
document_name : Option < StringKey > ,
125
- parent_type : Option < Type > ,
131
+ parent_type : Option < ParentType > ,
126
132
parent_name : Option < StringKey > ,
127
133
within_inline_fragment_type_condition : bool ,
128
134
maybe_condition : Option < Condition > ,
@@ -155,7 +161,7 @@ impl<'program> FragmentAliasTransform<'program> {
155
161
156
162
self . program
157
163
. schema
158
- . is_named_type_subtype_of ( parent_type, type_condition)
164
+ . is_named_type_subtype_of ( parent_type. type_ , type_condition)
159
165
}
160
166
None => true ,
161
167
}
@@ -180,6 +186,20 @@ impl<'program> FragmentAliasTransform<'program> {
180
186
// this error as a migration strategy.
181
187
return ;
182
188
}
189
+ let fragment = self
190
+ . program
191
+ . fragment ( spread. fragment . item )
192
+ . expect ( "I believe we have already validated that all fragments exist" ) ;
193
+
194
+ let fragment_is_plural =
195
+ RelayDirective :: find ( & fragment. directives ) . map_or ( false , |directive| directive. plural ) ;
196
+
197
+ if fragment_is_plural && self . parent_type . expect ( "expect parent type" ) . is_plural {
198
+ // Plural fragments handle their own nullability when read. However,
199
+ // they can calso be used in a singular selection. In that case,
200
+ // they should be aliased.
201
+ return ;
202
+ }
183
203
if spread
184
204
. directives
185
205
. named ( MATCH_CONSTANTS . module_directive_name )
@@ -211,10 +231,10 @@ impl<'program> FragmentAliasTransform<'program> {
211
231
if !self
212
232
. program
213
233
. schema
214
- . is_named_type_subtype_of ( parent_type, type_condition)
234
+ . is_named_type_subtype_of ( parent_type. type_ , type_condition)
215
235
{
216
236
let fragment_type_name = self . program . schema . get_type_name ( type_condition) ;
217
- let selection_type_name = self . program . schema . get_type_name ( parent_type) ;
237
+ let selection_type_name = self . program . schema . get_type_name ( parent_type. type_ ) ;
218
238
let diagnostic = if self . within_inline_fragment_type_condition {
219
239
ValidationMessageWithData :: ExpectedAliasOnNonSubtypeSpreadWithinTypedInlineFragment {
220
240
fragment_name : spread. fragment . item ,
@@ -248,7 +268,10 @@ impl Transformer for FragmentAliasTransform<'_> {
248
268
) -> Transformed < FragmentDefinition > {
249
269
self . document_name = Some ( fragment. name . item . 0 ) ;
250
270
self . parent_name = Some ( fragment. name . item . 0 ) ;
251
- self . parent_type = Some ( fragment. type_condition ) ;
271
+ self . parent_type = Some ( ParentType {
272
+ type_ : fragment. type_condition ,
273
+ is_plural : false ,
274
+ } ) ;
252
275
let transformed = self . default_transform_fragment ( fragment) ;
253
276
self . parent_type = None ;
254
277
self . parent_name = None ;
@@ -261,7 +284,10 @@ impl Transformer for FragmentAliasTransform<'_> {
261
284
operation : & OperationDefinition ,
262
285
) -> Transformed < OperationDefinition > {
263
286
self . document_name = Some ( operation. name . item . 0 ) ;
264
- self . parent_type = Some ( operation. type_ ) ;
287
+ self . parent_type = Some ( ParentType {
288
+ type_ : operation. type_ ,
289
+ is_plural : false ,
290
+ } ) ;
265
291
self . parent_name = Some ( operation. name . item . 0 ) ;
266
292
let transformed = self . default_transform_operation ( operation) ;
267
293
self . parent_type = None ;
@@ -312,16 +338,24 @@ impl Transformer for FragmentAliasTransform<'_> {
312
338
let will_always_match = self . will_always_match ( fragment. type_condition ) ;
313
339
314
340
if let Some ( type_condition) = fragment. type_condition {
315
- self . parent_type = Some ( type_condition) ;
341
+ let parent_type = self
342
+ . parent_type
343
+ . expect ( "Selection should be within a parent type." ) ;
344
+ self . parent_type = Some ( ParentType {
345
+ type_ : type_condition,
346
+ is_plural : parent_type. is_plural ,
347
+ } ) ;
316
348
}
317
349
350
+ let parent_type = self
351
+ . parent_type
352
+ . expect ( "Selection should be within a parent type." ) ;
353
+
318
354
let alias_metadata = FragmentAliasMetadata {
319
355
alias,
320
356
type_condition : fragment. type_condition ,
321
357
non_nullable : will_always_match,
322
- selection_type : self
323
- . parent_type
324
- . expect ( "Selection should be within a parent type." ) ,
358
+ selection_type : parent_type. type_ ,
325
359
wraps_spread : false ,
326
360
} ;
327
361
@@ -379,10 +413,14 @@ impl Transformer for FragmentAliasTransform<'_> {
379
413
380
414
match spread. alias ( ) {
381
415
Ok ( Some ( alias) ) => {
382
- let is_plural = RelayDirective :: find ( & fragment. directives )
416
+ let fragment_is_plural = RelayDirective :: find ( & fragment. directives )
383
417
. map_or ( false , |directive| directive. plural ) ;
384
418
385
- if is_plural {
419
+ let parent_type = self
420
+ . parent_type
421
+ . expect ( "Selection should be within a parent type." ) ;
422
+
423
+ if fragment_is_plural && parent_type. is_plural {
386
424
self . errors . push ( Diagnostic :: error (
387
425
ValidationMessage :: PluralFragmentAliasNotSupported ,
388
426
alias. location ,
@@ -393,9 +431,7 @@ impl Transformer for FragmentAliasTransform<'_> {
393
431
alias,
394
432
type_condition,
395
433
non_nullable : self . will_always_match ( type_condition) ,
396
- selection_type : self
397
- . parent_type
398
- . expect ( "Selection should be within a parent type." ) ,
434
+ selection_type : parent_type. type_ ,
399
435
wraps_spread : true ,
400
436
} ;
401
437
@@ -422,13 +458,12 @@ impl Transformer for FragmentAliasTransform<'_> {
422
458
// does not apply to the selections within the linked field.
423
459
let parent_condition = self . maybe_condition . take ( ) ;
424
460
425
- self . parent_type = Some (
426
- self . program
427
- . schema
428
- . field ( field. definition . item )
429
- . type_
430
- . inner ( ) ,
431
- ) ;
461
+ let field_definition = self . program . schema . field ( field. definition . item ) ;
462
+
463
+ self . parent_type = Some ( ParentType {
464
+ type_ : field_definition. type_ . inner ( ) ,
465
+ is_plural : field_definition. type_ . is_list ( ) ,
466
+ } ) ;
432
467
433
468
let transformed = self . default_transform_linked_field ( field) ;
434
469
0 commit comments