in compiler/crates/relay-transforms/src/client_edges.rs [215:323]
fn transform_linked_field_impl(&mut self, field: &LinkedField) -> Transformed<Selection> {
let schema = &self.program.schema;
let field_type = schema.field(field.definition.item);
// Eventually we will want to enable client edges on non-resolver client
// schema extensions, but we'll start with limiting them to resolvers.
let is_resolver = field_type
.directives
.named(*RELAY_RESOLVER_DIRECTIVE_NAME)
.is_some();
let is_client_edge = field_type.is_extension && is_resolver;
let waterfall_directive = field
.directives()
.named(*CLIENT_EDGE_WATERFALL_DIRECTIVE_NAME);
if !is_client_edge {
// Non-Client-Edge fields do not incur a waterfall, and thus should
// not be annotated with @waterfall.
if let Some(directive) = waterfall_directive {
self.errors.push(Diagnostic::error_with_data(
ValidationMessageWithData::RelayResolversUnexpectedWaterfall,
directive.name.location,
));
}
return self.default_transform_linked_field(field);
}
let edge_to_type = field_type.type_.inner();
let is_edge_to_client_object = schema.is_extension_type(edge_to_type);
let new_selections = self
.transform_selections(&field.selections)
.replace_or_else(|| field.selections.clone());
let metadata_directive = if is_edge_to_client_object {
// We assume edges to client objects will be resolved on the client
// and thus not incur a waterfall. This will change in the furture
// for @live Resolvers that can trigger suspense.
if let Some(directive) = waterfall_directive {
self.errors.push(Diagnostic::error_with_data(
ValidationMessageWithData::RelayResolversUnexpectedWaterfall,
directive.name.location,
));
}
match edge_to_type {
Type::Interface(_) => {
self.errors.push(Diagnostic::error(
ValidationMessage::ClientEdgeToClientInterface,
field.alias_or_name_location(),
));
return self.default_transform_linked_field(field);
}
Type::Union(_) => {
self.errors.push(Diagnostic::error(
ValidationMessage::ClientEdgeToClientUnion,
field.alias_or_name_location(),
));
return Transformed::Keep;
}
Type::Object(object_id) => ClientEdgeMetadataDirective::ClientObject {
type_name: schema.object(object_id).name.item,
},
_ => {
panic!(
"Expected a linked field to reference either an Object, Interface, or Union"
)
}
}
} else {
// Client Edges to server objects must be annotated with @waterfall
if waterfall_directive.is_none() {
self.errors.push(Diagnostic::error_with_data(
ValidationMessageWithData::RelayResolversMissingWaterfall {
field_name: field_type.name.item,
},
field.definition.location,
));
}
let client_edge_query_name = self.generate_query_name();
self.generate_client_edge_query(
client_edge_query_name,
field_type.type_.inner(),
new_selections.clone(),
);
ClientEdgeMetadataDirective::ServerObject {
query_name: client_edge_query_name,
}
};
let transformed_field = Arc::new(LinkedField {
selections: new_selections,
..field.clone()
});
let inline_fragment = InlineFragment {
type_condition: None,
directives: vec![metadata_directive.into()],
selections: vec![
Selection::LinkedField(transformed_field.clone()),
Selection::LinkedField(transformed_field),
],
};
Transformed::Replace(Selection::InlineFragment(Arc::new(inline_fragment)))
}