in compiler/crates/relay-transforms/src/apply_fragment_arguments/mod.rs [346:447]
fn transform_no_inline_fragment(
&mut self,
fragment: &FragmentDefinition,
directive: &Directive,
) {
// If we have already computed, we can return early
if let Some((_, provided_variables)) = self.split_operations.get(&fragment.name.item) {
for (name, def) in provided_variables {
self.provided_variables.insert(*name, def.clone());
}
return;
}
// We do not need to to write normalization files for base fragments
let is_base = self.base_fragment_names.contains(&fragment.name.item);
if !is_base && !self.no_inline_feature.is_enabled_for(fragment.name.item) {
self.errors.push(Diagnostic::error(
format!(
"Invalid usage of @no_inline on fragment '{}': this feature is gated and currently set to: {}",
fragment.name.item, self.no_inline_feature
),
directive.name.location,
));
}
// save the context used by the enclosing operation / fragment
let mut saved_provided_vars = std::mem::take(&mut self.provided_variables);
let saved_scope = std::mem::replace(&mut self.scope, no_inline_fragment_scope(fragment));
self.extract_provided_variables(fragment);
let fragment = self
.default_transform_fragment(fragment)
.unwrap_or_else(|| fragment.clone());
let FragmentDefinition {
name,
mut directives,
mut variable_definitions,
selections,
type_condition,
..
} = fragment;
for variable in &mut variable_definitions {
variable.name.item = format_local_variable(fragment.name.item, variable.name.item);
}
let mut metadata = SplitOperationMetadata {
derived_from: fragment.name.item,
parent_documents: Default::default(),
raw_response_type: is_raw_response_type_enabled(directive),
};
// - A fragment with user defined @no_inline always produces a $normalization file. The `parent_document` of
// that file is the fragment itself as it gets deleted iff that fragment is deleted or no longer
// has the @no_inline directive.
// - A fragment with @no_inline generated by @module, `parent_documents` also include fragments that
// spread the current fragment with @module
metadata.parent_documents.insert(fragment.name.item);
let parent_documents_arg = directive.arguments.named(*PARENT_DOCUMENTS_ARG);
if let Some(Value::Constant(ConstantValue::List(parent_documents))) =
parent_documents_arg.map(|arg| &arg.value.item)
{
for val in parent_documents {
if let ConstantValue::String(name) = val {
metadata.parent_documents.insert(*name);
} else {
panic!("Expected item in the parent_documents to be a StringKey.")
}
}
}
directives.push(metadata.to_directive());
let normalization_name = get_normalization_operation_name(name.item).intern();
let operation = if is_base {
None
} else {
Some(OperationDefinition {
name: WithLocation::new(name.location, normalization_name),
type_: type_condition,
variable_definitions,
directives,
selections,
kind: OperationKind::Query,
})
};
if self.program.operation(normalization_name).is_some() {
self.errors.push(Diagnostic::error(
format!(
"Invalid usage of @no_inline on fragment '{}' - @no_inline is only allowed on allowlisted fragments loaded with @module",
fragment.name.item,
),
directive.name.location,
));
}
self.split_operations.insert(
fragment.name.item,
(operation, self.provided_variables.clone()),
);
// add this fragment's provided variables to that of the enclosing operation / fragment
saved_provided_vars.extend(self.provided_variables.drain(..).into_iter());
self.provided_variables = saved_provided_vars;
self.scope = saved_scope;
}