From cf0fb394dbb60f69eacfccec8e49201bce2de94c Mon Sep 17 00:00:00 2001 From: Bhuwan Khattar Date: Wed, 29 Nov 2023 09:43:26 -0800 Subject: [PATCH] incremental build compatible ResolversSchemaModule Reviewed By: alunyov Differential Revision: D51495491 fbshipit-source-id: 44e55c4ae397605a847fb55489092381e2843504 --- .../crates/relay-compiler/src/artifact_map.rs | 4 + .../src/build_project/build_ir.rs | 6 +- .../build_project/resolvers_schema_module.rs | 17 +-- .../crates/relay-compiler/src/graphql_asts.rs | 8 +- .../src/server/lsp_state_resources.rs | 10 +- ...resolvers_root_fragment_split_operation.rs | 122 ++++++++++++------ compiler/crates/relay-transforms/src/lib.rs | 1 + 7 files changed, 102 insertions(+), 66 deletions(-) diff --git a/compiler/crates/relay-compiler/src/artifact_map.rs b/compiler/crates/relay-compiler/src/artifact_map.rs index 9acf258d58c8a..f7f98a4b418c8 100644 --- a/compiler/crates/relay-compiler/src/artifact_map.rs +++ b/compiler/crates/relay-compiler/src/artifact_map.rs @@ -31,8 +31,12 @@ pub struct ArtifactMap(pub DashMap>); #[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq, Hash)] pub enum ArtifactSourceKey { + /// Derieved from a GraphQL Executable Definition ExecutableDefinition(ExecutableDefinitionName), + /// Derieved from a RelayResolver docblock ResolverHash(ResolverSourceHash), + /// Derived from GraphQL Schema + Schema(), } impl From for ArtifactSourceKey { diff --git a/compiler/crates/relay-compiler/src/build_project/build_ir.rs b/compiler/crates/relay-compiler/src/build_project/build_ir.rs index d185f70d2217d..57d1da20e0e71 100644 --- a/compiler/crates/relay-compiler/src/build_project/build_ir.rs +++ b/compiler/crates/relay-compiler/src/build_project/build_ir.rs @@ -15,6 +15,7 @@ use graphql_syntax::ExecutableDefinition; use graphql_text_printer::print_executable_definition_ast; use md5::Digest; use md5::Md5; +use relay_transforms::annotate_resolver_root_fragments; use schema::SDLSchema; use super::ProjectAsts; @@ -56,7 +57,10 @@ pub fn build_ir( ) -> Result> { let asts = project_asts.definitions; let source_hashes = SourceHashes::from_definitions(&asts); - let ir = graphql_ir::build_ir_in_relay_mode(schema, &asts, &project_config.feature_flags)?; + let mut ir = graphql_ir::build_ir_in_relay_mode(schema, &asts, &project_config.feature_flags)?; + if project_config.resolvers_schema_module.is_some() { + ir = annotate_resolver_root_fragments(schema, ir); + } if is_incremental_build { let affected_ir = get_reachable_ir( ir, diff --git a/compiler/crates/relay-compiler/src/build_project/resolvers_schema_module.rs b/compiler/crates/relay-compiler/src/build_project/resolvers_schema_module.rs index 035e5eeee0df2..6eb4af73549dc 100644 --- a/compiler/crates/relay-compiler/src/build_project/resolvers_schema_module.rs +++ b/compiler/crates/relay-compiler/src/build_project/resolvers_schema_module.rs @@ -15,7 +15,6 @@ use common::NamedItem; use common::SourceLocationKey; use intern::Lookup; use relay_codegen::printer::get_module_path; -use relay_transforms::generate_relay_resolvers_model_fragments::get_resolver_source_hash; use relay_transforms::get_fragment_filename; use relay_transforms::get_resolver_fragment_dependency_name; use relay_transforms::relay_resolvers::get_resolver_info; @@ -42,16 +41,10 @@ pub fn generate_resolvers_schema_module( schema: &SDLSchema, output_path: PathBuf, ) -> Result { - let mut artifact_source_keys = vec![]; - let content = generate_resolvers_schema_module_content( - config, - project_config, - schema, - &output_path, - &mut artifact_source_keys, - )?; + let content = + generate_resolvers_schema_module_content(config, project_config, schema, &output_path)?; Ok(Artifact { - artifact_source_keys, + artifact_source_keys: vec![ArtifactSourceKey::Schema()], path: output_path, content: ArtifactContent::Generic { content: sign_file(&content).into_bytes(), @@ -65,7 +58,6 @@ fn generate_resolvers_schema_module_content( project_config: &ProjectConfig, schema: &SDLSchema, artifact_path: &PathBuf, - artifact_source_keys: &mut Vec, ) -> Result { let mut content = String::new(); @@ -124,9 +116,6 @@ fn generate_resolvers_schema_module_content( object_name = object.name.item )?; } - if let Some(source_hash) = get_resolver_source_hash(field) { - artifact_source_keys.push(ArtifactSourceKey::ResolverHash(source_hash)); - } let js_import_path = project_config.js_module_import_identifier( artifact_path, diff --git a/compiler/crates/relay-compiler/src/graphql_asts.rs b/compiler/crates/relay-compiler/src/graphql_asts.rs index 07997dab9bc33..548eea4740b49 100644 --- a/compiler/crates/relay-compiler/src/graphql_asts.rs +++ b/compiler/crates/relay-compiler/src/graphql_asts.rs @@ -65,9 +65,11 @@ impl GraphQLAsts { ArtifactSourceKey::ExecutableDefinition(def_name) => { Some(def_name) } - // Dirty resolvers artifacts are handled separately - // and should not affect the list of affected document defintions - ArtifactSourceKey::ResolverHash(_) => None, + ArtifactSourceKey::Schema() + | ArtifactSourceKey::ResolverHash(_) => { + // We're only concerned with collecting ExecutableDefinitionNames + None + } }) .collect() }), diff --git a/compiler/crates/relay-lsp/src/server/lsp_state_resources.rs b/compiler/crates/relay-lsp/src/server/lsp_state_resources.rs index 519c6a5af7659..1e83ce445e704 100644 --- a/compiler/crates/relay-lsp/src/server/lsp_state_resources.rs +++ b/compiler/crates/relay-lsp/src/server/lsp_state_resources.rs @@ -433,12 +433,10 @@ impl Some(*name), - // For the resolver case, we don't really need to track removed resolver definitions - // here, as the documents for resolves are not accessible for the user - // in the LSP program. We only care about unused fragments/operations - // that are editable by the user. - // We also don't write artifacts from LSP so it is safe to skip these here. - ArtifactSourceKey::ResolverHash(_) => None, + ArtifactSourceKey::Schema() | ArtifactSourceKey::ResolverHash(_) => { + // In the LSP program, we only care about tracking user-editable ExecutableDefinitions + None + } }) .collect::>() }); diff --git a/compiler/crates/relay-transforms/src/generate_relay_resolvers_root_fragment_split_operation.rs b/compiler/crates/relay-transforms/src/generate_relay_resolvers_root_fragment_split_operation.rs index 43f22486f780c..8371b6c274957 100644 --- a/compiler/crates/relay-transforms/src/generate_relay_resolvers_root_fragment_split_operation.rs +++ b/compiler/crates/relay-transforms/src/generate_relay_resolvers_root_fragment_split_operation.rs @@ -10,23 +10,70 @@ use std::sync::Arc; use common::DiagnosticsResult; use common::NamedItem; use docblock_shared::RELAY_RESOLVER_DIRECTIVE_NAME; +use graphql_ir::associated_data_impl; +use graphql_ir::ExecutableDefinition; +use graphql_ir::FragmentDefinition; +use graphql_ir::FragmentDefinitionName; use graphql_ir::OperationDefinition; use graphql_ir::OperationDefinitionName; use graphql_ir::Program; use graphql_syntax::OperationKind; use intern::string_key::Intern; +use rustc_hash::FxHashSet; +use schema::SDLSchema; -use crate::generate_relay_resolvers_model_fragments::directives_with_artifact_source; use crate::get_normalization_operation_name; use crate::get_resolver_fragment_dependency_name; use crate::SplitOperationMetadata; use crate::RESOLVER_BELONGS_TO_BASE_SCHEMA_DIRECTIVE; +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +struct IsResolverRootFragment(); +associated_data_impl!(IsResolverRootFragment); + pub fn generate_relay_resolvers_root_fragment_split_operation( program: &Program, ) -> DiagnosticsResult { let mut operations = vec![]; - for field in program.schema.get_fields() { + for fragment in program.fragments() { + if IsResolverRootFragment::find(&fragment.directives).is_some() { + operations.push(Arc::new(OperationDefinition { + name: fragment.name.map(|name| { + OperationDefinitionName(get_normalization_operation_name(name.0).intern()) + }), + type_: fragment.type_condition, + variable_definitions: fragment.variable_definitions.clone(), + directives: vec![ + SplitOperationMetadata { + location: fragment.name.location, + parent_documents: FxHashSet::from_iter([fragment.name.item.into()]), + derived_from: Some(fragment.name.item), + raw_response_type_generation_mode: None, + } + .into(), + ], + selections: fragment.selections.clone(), + kind: OperationKind::Query, + })); + } + } + + if operations.is_empty() { + Ok(program.clone()) + } else { + let mut next_program = program.clone(); + + for operation in operations { + next_program.insert_operation(operation) + } + + Ok(next_program) + } +} + +fn get_resolver_root_fragment_names(schema: &SDLSchema) -> FxHashSet { + let mut names = FxHashSet::default(); + for field in schema.get_fields() { if !field.is_extension || field .directives @@ -40,47 +87,38 @@ pub fn generate_relay_resolvers_root_fragment_split_operation( continue; } - let root_fragment_name = get_resolver_fragment_dependency_name(field); - if root_fragment_name.is_none() { - continue; + if let Some(root_fragment_name) = get_resolver_fragment_dependency_name(field) { + names.insert(root_fragment_name); } - - let root_fragment = program.fragment(root_fragment_name.unwrap()).unwrap(); - let operation_name = root_fragment - .name - .map(|name| OperationDefinitionName(get_normalization_operation_name(name.0).intern())); - - let mut directives = directives_with_artifact_source(field); - directives.push( - SplitOperationMetadata { - location: root_fragment.name.location, - parent_documents: Default::default(), - derived_from: Some(root_fragment.name.item), - raw_response_type_generation_mode: None, - } - .into(), - ); - let operation = OperationDefinition { - name: operation_name, - type_: root_fragment.type_condition, - variable_definitions: root_fragment.variable_definitions.clone(), - directives, - selections: root_fragment.selections.clone(), - kind: OperationKind::Query, - }; - - operations.push(Arc::new(operation)) } + names +} - if operations.is_empty() { - Ok(program.clone()) - } else { - let mut next_program = program.clone(); - - for operation in operations { - next_program.insert_operation(operation) - } - - Ok(next_program) - } +/// Adds a directive on all `FragmentDefinition`s in IR that are marked as a `@rootFragment` +/// for any resolver backed field in the schema (but not base schema) +pub fn annotate_resolver_root_fragments( + schema: &SDLSchema, + ir: Vec, +) -> Vec { + let resolver_root_fragment_names = get_resolver_root_fragment_names(schema); + ir.into_iter() + .map(|def| { + if let ExecutableDefinition::Fragment(ref fragment) = def { + return if resolver_root_fragment_names.contains(&fragment.name.item) { + ExecutableDefinition::Fragment(FragmentDefinition { + directives: fragment + .directives + .iter() + .cloned() + .chain(vec![IsResolverRootFragment().into()]) + .collect(), + ..fragment.clone() + }) + } else { + def + }; + } + def + }) + .collect() } diff --git a/compiler/crates/relay-transforms/src/lib.rs b/compiler/crates/relay-transforms/src/lib.rs index 0059e03a1e1f3..6bfd9779636cc 100644 --- a/compiler/crates/relay-transforms/src/lib.rs +++ b/compiler/crates/relay-transforms/src/lib.rs @@ -130,6 +130,7 @@ pub use generate_id_field::generate_id_field; pub use generate_live_query_metadata::generate_live_query_metadata; pub use generate_relay_resolvers_model_fragments::ArtifactSourceKeyData; pub use generate_relay_resolvers_operations_for_nested_objects::generate_relay_resolvers_operations_for_nested_objects; +pub use generate_relay_resolvers_root_fragment_split_operation::annotate_resolver_root_fragments; pub use generate_relay_resolvers_root_fragment_split_operation::generate_relay_resolvers_root_fragment_split_operation; pub use generate_typename::generate_typename; pub use generate_typename::TYPE_DISCRIMINATOR_DIRECTIVE_NAME;