in compiler/crates/relay-transforms/src/relay_client_component.rs [132:280]
fn transform_relay_client_component(
&mut self,
spread: &FragmentSpread,
) -> Result<Transformed<Selection>, Diagnostic> {
if let Some(incompatible_directives_diagnostic) =
self.get_incompatible_directives_diagnostic(spread)
{
return Err(incompatible_directives_diagnostic);
}
// @relay_client_component does not take arguments (yet)
if let Some(argument) = spread.arguments.first() {
return Err(Diagnostic::error(
ValidationMessage::InvalidRelayClientComponentWithArguments,
argument.name.location,
));
}
let fragment = self
.program
.fragment(spread.fragment.item)
.unwrap_or_else(|| panic!("Expected to find fragment `{}`", spread.fragment.item));
// Validate that the fragment's type condition MUST implement `Node`.
let node_interface_id = self.node_interface_id;
let implements_node = match fragment.type_condition {
// Fragments can be specified on object types, interfaces, and unions.
// https://spec.graphql.org/June2018/#sec-Type-Conditions
Type::Interface(id) => {
id == node_interface_id
|| self
.program
.schema
.interface(id)
.implementing_objects
.iter()
.all(|&object_id| {
self.program
.schema
.object(object_id)
.interfaces
.iter()
.any(|interface_id| *interface_id == node_interface_id)
})
}
Type::Object(id) => self
.program
.schema
.object(id)
.interfaces
.iter()
.any(|interface_id| *interface_id == node_interface_id),
Type::Union(id) => self
.program
.schema
.union(id)
.members
.iter()
.all(|&object_id| {
self.program
.schema
.object(object_id)
.interfaces
.iter()
.any(|interface_id| *interface_id == node_interface_id)
}),
_ => false,
};
let is_fragment_on_query =
fragment.type_condition == self.program.schema.query_type().unwrap();
let is_fragment_on_viewer =
self.program.schema.get_type_name(fragment.type_condition) == *VIEWER_TYPE_NAME;
if !implements_node && !is_fragment_on_query && !is_fragment_on_viewer {
return Err(Diagnostic::error(
ValidationMessage::InvalidRelayClientComponentNonNodeFragment,
fragment.name.location,
));
}
let should_use_no_inline = self.no_inline_flag.is_enabled_for(spread.fragment.item);
if should_use_no_inline {
self.no_inline_fragments
.entry(fragment.name.item)
.or_default()
.push(self.document_name.unwrap());
} else {
// Generate a SplitOperation AST
let created_split_operation = self
.split_operations
.entry(spread.fragment.item)
.or_insert_with(|| {
let normalization_name =
get_normalization_operation_name(spread.fragment.item).intern();
(
SplitOperationMetadata {
derived_from: spread.fragment.item,
parent_documents: Default::default(),
raw_response_type: false,
},
OperationDefinition {
name: WithLocation::new(spread.fragment.location, normalization_name),
type_: fragment.type_condition,
kind: OperationKind::Query,
variable_definitions: fragment.variable_definitions.clone(),
directives: fragment.directives.clone(),
selections: vec![Selection::FragmentSpread(Arc::new(FragmentSpread {
arguments: Default::default(),
directives: Default::default(),
fragment: spread.fragment,
}))],
},
)
});
created_split_operation
.0
.parent_documents
.insert(self.document_name.unwrap());
}
// @relay_client_component -> @relay_client_component_server(module_id: "...")
let module_id = get_fragment_filename(spread.fragment.item);
let mut next_directives = spread.directives.clone();
if let Some(relay_client_component_directive) = next_directives
.iter_mut()
.find(|directive| directive.name.item == *RELAY_CLIENT_COMPONENT_DIRECTIVE_NAME)
{
*relay_client_component_directive = Directive {
name: WithLocation {
item: *RELAY_CLIENT_COMPONENT_SERVER_DIRECTIVE_NAME,
location: relay_client_component_directive.name.location,
},
arguments: vec![Argument {
name: WithLocation::generated(*RELAY_CLIENT_COMPONENT_MODULE_ID_ARGUMENT_NAME),
value: WithLocation::generated(Value::Constant(ConstantValue::String(
module_id,
))),
}],
data: None,
};
}
// Record the SplitOperation so we can emit metadata later
self.split_operation_filenames.insert(module_id);
Ok(Transformed::Replace(Selection::FragmentSpread(Arc::new(
FragmentSpread {
directives: next_directives,
..spread.clone()
},
))))
}