in compiler/crates/relay-typegen/src/lib.rs [1052:1195]
fn make_prop(
&mut self,
type_selection: TypeSelection,
unmasked: bool,
concrete_type: Option<Type>,
) -> Prop {
let optional = type_selection.is_conditional();
if self.generating_updatable_types && optional {
panic!(
"When generating types for updatable operations and fragments, we should never generate optional fields! This indicates a bug in Relay. type_selection: {:?}",
type_selection
);
}
match type_selection {
TypeSelection::LinkedField(linked_field) => {
let key = linked_field.field_name_or_alias;
if self.generating_updatable_types {
// TODO check whether the field is `node` or `nodes` on `Query`. If so, it should not be
// updatable.
let (just_fragments, no_fragments) =
extract_fragments(linked_field.node_selections);
let getter_object_props =
self.selections_to_babel(no_fragments.into_iter(), unmasked, None);
let getter_return_value = self
.transform_scalar_type(&linked_field.node_type, Some(getter_object_props));
let setter_parameter = if just_fragments.is_empty() {
if linked_field.node_type.is_list() {
AST::RawType(intern!("[]"))
} else {
AST::RawType(intern!("null | void"))
}
} else {
let setter_parameter = AST::Union(
SortedASTList::new(
just_fragments
.iter()
.map(|fragment_spread| {
let type_condition_info = fragment_spread
.type_condition_info
.expect("Fragment spreads in updatable queries should have TypeConditionInfo");
let (key, value) = match type_condition_info {
TypeConditionInfo::Abstract => (format!("__is{}", fragment_spread.fragment_name).intern(), AST::String),
TypeConditionInfo::Concrete { concrete_type } => ("__typename".intern(), AST::StringLiteral(StringLiteral(concrete_type))),
};
let fragment_spread_or_concrete_type_marker = Prop::KeyValuePair(KeyValuePairProp {
key,
value,
read_only: true,
optional: false,
});
let assignable_fragment_spread_ref= Prop::KeyValuePair(KeyValuePairProp {
key: *KEY_FRAGMENT_SPREADS,
value: AST::FragmentReferenceType(
fragment_spread.fragment_name,
),
read_only: true,
optional: false,
});
let client_id_field = Prop::KeyValuePair(KeyValuePairProp {
key: "__id".intern(),
value: AST::String,
read_only: true,
optional: false,
});
AST::InexactObject(InexactObject::new(vec![
assignable_fragment_spread_ref,
fragment_spread_or_concrete_type_marker,
client_id_field,
], self.should_sort_typegen_items))
})
.collect(),
self.should_sort_typegen_items));
if linked_field.node_type.is_list() {
AST::ReadOnlyArray(Box::new(setter_parameter))
} else {
AST::Nullable(Box::new(setter_parameter))
}
};
Prop::GetterSetterPair(GetterSetterPairProp {
key,
getter_return_value,
setter_parameter,
})
} else {
let object_props = self.selections_to_babel(
hashmap_into_values(linked_field.node_selections),
unmasked,
None,
);
let value =
self.transform_scalar_type(&linked_field.node_type, Some(object_props));
Prop::KeyValuePair(KeyValuePairProp {
key,
value,
optional,
read_only: true,
})
}
}
TypeSelection::ScalarField(scalar_field) => {
if scalar_field.special_field == Some(ScalarFieldSpecialSchemaField::TypeName) {
if let Some(concrete_type) = concrete_type {
Prop::KeyValuePair(KeyValuePairProp {
key: scalar_field.field_name_or_alias,
value: AST::StringLiteral(StringLiteral(
self.schema.get_type_name(concrete_type),
)),
optional,
read_only: true,
})
} else {
Prop::KeyValuePair(KeyValuePairProp {
key: scalar_field.field_name_or_alias,
value: scalar_field.value,
optional,
read_only: true,
})
}
} else {
Prop::KeyValuePair(KeyValuePairProp {
key: scalar_field.field_name_or_alias,
value: scalar_field.value,
optional,
// all fields outside of updatable operations are read-only, and within updatable operations,
// all special fields are read only
read_only: !self.generating_updatable_types
|| scalar_field.special_field.is_some(),
})
}
}
_ => panic!(
"Unexpected TypeSelection variant in make_prop, {:?}",
type_selection
),
}
}