fn transform_linked_field_impl()

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