fn transform_relay_client_component()

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()
            },
        ))))
    }